]> jfr.im git - irc/evilnet/x3.git/blob - src/chanserv.c
SVSPART trace / alert support (level 999). Just like the recent SVSJOIN support
[irc/evilnet/x3.git] / src / chanserv.c
1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
3 *
4 * This file is part of x3.
5 *
6 * x3 is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
19 */
20
21 #include "chanserv.h"
22 #include "conf.h"
23 #include "global.h"
24 #include "modcmd.h"
25 #include "opserv.h" /* for opserv_bad_channel() */
26 #include "saxdb.h"
27 #include "spamserv.h"
28 #include "timeq.h"
29
30 #define CHANSERV_CONF_NAME "services/chanserv"
31
32 /* ChanServ options */
33 #define KEY_SUPPORT_CHANNEL "support_channel"
34 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
35 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
36 #define KEY_INFO_DELAY "info_delay"
37 #define KEY_MAX_GREETLEN "max_greetlen"
38 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
39 #define KEY_ADJUST_DELAY "adjust_delay"
40 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
41 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
42 #define KEY_BAN_TIMEOUT_FREQ "ban_timeout_freq"
43 #define KEY_MAX_CHAN_USERS "max_chan_users"
44 #define KEY_MAX_CHAN_BANS "max_chan_bans"
45 #define KEY_NICK "nick"
46 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
47 #define KEY_8BALL_RESPONSES "8ball"
48 #define KEY_OLD_BAN_NAMES "old_ban_names"
49 #define KEY_REFRESH_PERIOD "refresh_period"
50 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
51 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
52 #define KEY_MAX_OWNED "max_owned"
53 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
54 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
55 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
56 #define KEY_NODELETE_LEVEL "nodelete_level"
57 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
58 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
59
60 /* ChanServ database */
61 #define KEY_VERSION_CONTROL "version_control"
62 #define KEY_CHANNELS "channels"
63 #define KEY_NOTE_TYPES "note_types"
64
65 /* version control paramiter */
66 #define KEY_VERSION_NUMBER "version_number"
67
68 /* Note type parameters */
69 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
70 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
71 #define KEY_NOTE_SETTER_ACCESS "setter_access"
72 #define KEY_NOTE_VISIBILITY "visibility"
73 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
74 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
75 #define KEY_NOTE_VIS_ALL "all"
76 #define KEY_NOTE_MAX_LENGTH "max_length"
77 #define KEY_NOTE_SETTER "setter"
78 #define KEY_NOTE_NOTE "note"
79
80 /* Do-not-register channels */
81 #define KEY_DNR "dnr"
82 #define KEY_DNR_SET "set"
83 #define KEY_DNR_SETTER "setter"
84 #define KEY_DNR_REASON "reason"
85
86 /* Channel data */
87 #define KEY_REGISTERED "registered"
88 #define KEY_REGISTRAR "registrar"
89 #define KEY_SUSPENDED "suspended"
90 #define KEY_PREVIOUS "previous"
91 #define KEY_SUSPENDER "suspender"
92 #define KEY_ISSUED "issued"
93 #define KEY_REVOKED "revoked"
94 #define KEY_SUSPEND_EXPIRES "suspend_expires"
95 #define KEY_SUSPEND_REASON "suspend_reason"
96 #define KEY_GIVEOWNERSHIP "giveownership"
97 #define KEY_STAFF_ISSUER "staff_issuer"
98 #define KEY_OLD_OWNER "old_owner"
99 #define KEY_TARGET "target"
100 #define KEY_TARGET_ACCESS "target_access"
101 #define KEY_VISITED "visited"
102 #define KEY_TOPIC "topic"
103 #define KEY_GREETING "greeting"
104 #define KEY_USER_GREETING "user_greeting"
105 #define KEY_MODES "modes"
106 #define KEY_FLAGS "flags"
107 #define KEY_OPTIONS "options"
108 #define KEY_USERS "users"
109 #define KEY_BANS "bans" /* for lamers */
110 #define KEY_MAX "max"
111 #define KEY_NOTES "notes"
112 #define KEY_TOPIC_MASK "topic_mask"
113 #define KEY_OWNER_TRANSFER "owner_transfer"
114
115 /* User data */
116 #define KEY_LEVEL "level"
117 #define KEY_INFO "info"
118 #define KEY_SEEN "seen"
119
120 /* Ban data */
121 #define KEY_OWNER "owner"
122 #define KEY_REASON "reason"
123 #define KEY_SET "set"
124 #define KEY_DURATION "duration"
125 #define KEY_EXPIRES "expires"
126 #define KEY_TRIGGERED "triggered"
127
128 #define KEY_GOD_TIMEOUT "god_timeout"
129
130 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
131 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
132
133 /* Administrative messages */
134 static const struct message_entry msgtab[] = {
135 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
136
137 /* Channel registration */
138 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
139 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
140 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
141 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
142 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
143 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
144 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
145 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
146
147 /* Do-not-register channels */
148 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
149 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
150 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
151 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
152 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
153 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
154 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
155 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
156 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
157 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
158 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
159
160 /* Channel unregistration */
161 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
162 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
163 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
164 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
165
166 /* Channel moving */
167 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
168 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
169
170 /* Channel merging */
171 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
172 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
173 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
174 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
175 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
176
177 /* Handle unregistration */
178 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
179
180 /* Error messages */
181 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
182 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
183 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
184 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
185 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
186 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
187 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
188 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
189 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
190 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
191 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
192 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
193 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
194 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
195
196 /* Removing yourself from a channel. */
197 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
198 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
199 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
200
201 /* User management */
202 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
203 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
204 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
205 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
206 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
207 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
208 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
209 { "CSMSG_ADDUSER_PENDING", "I have sent him/her a message letting them know, and if they auth or register soon, I will finish adding them automatically." },
210 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
211 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
212 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
213 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
214 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
215 { "CSMSG_ADDUSER_PENDING_TARGET", "Channel Services bot here: %s would like to add you to my userlist in channel %s, but you are not authenticated to $b$N$b. Please authenticate now and you will be added. If you do not have an account, type /msg $N help register" },
216 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
217
218 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
219 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
220 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
221 { "CSMSG_NO_OWNER", "There is no owner for %s; please use $bCLVL$b and/or $bADDOWNER$b instead." },
222 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
223 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
224 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
225
226 /* Ban management */
227 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
228 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
229 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
230 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
231 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
232 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
233 { "CSMSG_BAN_REMOVED", "Matching ban(s) and LAMER(s) in $b%s$b were removed." },
234 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
235 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
236 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
237 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
238 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
239 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
240 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
241 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
242 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
243 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
244
245 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
246
247 /* Channel management */
248 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
249 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
250 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
251
252 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
253 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
254 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
255 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
256 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
257 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
258 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
259
260 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
261 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
262 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
263 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
264 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
265 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
266 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
267 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
268 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
269 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
270 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
271 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
272 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
273 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
274 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
275 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
276 { "CSMSG_SET_MODES", "$bModes $b %s" },
277 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
278 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
279 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
280 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
281 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
282 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
283 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
284 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
285 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
286 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
287 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
288 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
289 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
290 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
291 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
292 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
293 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
294 { "CSMSG_SET_RESYNC", "$bResync $b %d - %s" },
295 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
296
297 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
298 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
299 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
300 { "CSMSG_USET_AUTOJOIN", "$bAutoJoin $b %s" },
301 { "CSMSG_USET_INFO", "$bInfo $b %s" },
302
303 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
304 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
305 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
306 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
307 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
308 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
309 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
310 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
311 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
312 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
313 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
314
315 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
316 { "CSMSG_AUTOMODE_NORMAL", "Give voice to peons, half-op to halfops, and op to ops." },
317 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
318 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
319 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
320 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
321 { "CSMSG_AUTOMODE_ONLYVOICE", "Just voice everyone with access." },
322
323 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
324 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
325 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
326 { "CSMSG_PROTECT_NONE", "No users will be protected." },
327 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
328 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
329 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
330
331 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
332 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
333 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
334 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
335 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
336
337 { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
338 { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
339 { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
340 { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
341 { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
342
343 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
344 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
345 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
346 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
347 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
348
349 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
350 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
351 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
352 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
353 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
354 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
355
356 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
357 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
358 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
359 { "CSMSG_CANNOT_INVITE", "You cannot invite %s to %s." },
360 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
361 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
362 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
363 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
364 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
365
366 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
367 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
368 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
369
370 /* Channel userlist */
371 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
372 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
373 /* uncomment if needed to adujust styles (and change code below)
374 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
375 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
376 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
377 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
378 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
379 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
380 */
381 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
382 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
383 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
384
385 /* Channel note list */
386 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
387 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
388 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
389 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
390 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
391 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
392 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
393 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
394 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
395 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
396 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
397 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
398 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
399 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
400 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
401
402 /* Channel [un]suspension */
403 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
404 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
405 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
406 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
407 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
408 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
409 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
410
411 /* Access information */
412 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
413 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
414 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
415 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
416 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
417 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
418 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
419 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
420 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
421 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
422 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
423
424 /* Seen information */
425 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
426 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
427 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
428 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
429
430 /* Names information */
431 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
432 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
433
434 /* Channel information */
435 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
436 { "CSMSG_BAR", "----------------------------------------"},
437 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
438 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
439 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
440 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
441 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
442 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
443 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
444 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
445 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
446 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
447 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
448 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
449 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
450 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
451 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
452 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
453 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
454 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
455 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
456 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
457 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
458 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
459 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
460 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
461 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
462 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
463
464 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
465 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
466 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
467 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
468 { "CSMSG_PEEK_OPS", "$bOps:$b" },
469 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
470 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
471
472 /* Network information */
473 { "CSMSG_NETWORK_INFO", "Network Information:" },
474 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
475 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
476 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
477 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
478 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
479 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
480 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
481 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
482
483 /* Staff list */
484 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
485 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
486 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
487
488 /* Channel searches */
489 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
490 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
491 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
492 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
493
494 /* Channel configuration */
495 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
496 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
497 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
498 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
499
500 /* User settings */
501 { "CSMSG_USER_OPTIONS", "User Options:" },
502 // { "CSMSG_USER_PROTECTED", "That user is protected." },
503
504 /* Toys */
505 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
506 { "CSMSG_PING_RESPONSE", "Pong!" },
507 { "CSMSG_WUT_RESPONSE", "wut" },
508 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
509 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
510 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
511 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
512 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
513 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
514 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
515
516 /* Other things */
517 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
518 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
519 { "CSMSG_DEFCON_NO_NEW_CHANNELS", "You cannot register new channels at this time, please try again soon." },
520 { "CSMSG_DEFCON_NO_MODE_CHANGE", "You cannot change the MODE at this time, please try again soon." },
521 { NULL, NULL }
522 };
523
524 /* eject_user and unban_user flags */
525 #define ACTION_KICK 0x0001
526 #define ACTION_BAN 0x0002
527 #define ACTION_ADD_LAMER 0x0004
528 #define ACTION_ADD_TIMED_LAMER 0x0008
529 #define ACTION_UNBAN 0x0010
530 #define ACTION_DEL_LAMER 0x0020
531
532 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
533 #define MODELEN 40 + KEYLEN
534 #define PADLEN 21
535 #define ACCESSLEN 10
536
537 #define CSFUNC_ARGS user, channel, argc, argv, cmd
538
539 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
540 #define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
541 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
542 reply("MSG_MISSING_PARAMS", argv[0]); \
543 CHANSERV_SYNTAX(); \
544 return 0; }
545
546 DECLARE_LIST(dnrList, struct do_not_register *);
547 DEFINE_LIST(dnrList, struct do_not_register *);
548
549 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
550
551 struct userNode *chanserv;
552 dict_t note_types;
553 int off_channel;
554 extern struct string_list *autojoin_channels;
555 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
556 static struct log_type *CS_LOG;
557 struct adduserPending* adduser_pendings = NULL;
558 unsigned int adduser_pendings_count = 0;
559 unsigned long god_timeout;
560
561 static struct
562 {
563 struct channelList support_channels;
564 struct mod_chanmode default_modes;
565
566 unsigned long db_backup_frequency;
567 unsigned long channel_expire_frequency;
568 unsigned long ban_timeout_frequency;
569
570 long info_delay;
571 unsigned int adjust_delay;
572 long channel_expire_delay;
573 unsigned int nodelete_level;
574
575 unsigned int adjust_threshold;
576 int join_flood_threshold;
577
578 unsigned int greeting_length;
579 unsigned int refresh_period;
580 unsigned int giveownership_period;
581
582 unsigned int max_owned;
583 unsigned int max_chan_users;
584 unsigned int max_chan_bans; /* lamers */
585 unsigned int max_userinfo_length;
586
587 struct string_list *set_shows;
588 struct string_list *eightball;
589 struct string_list *old_ban_names;
590
591 const char *ctcp_short_ban_duration;
592 const char *ctcp_long_ban_duration;
593
594 const char *irc_operator_epithet;
595 const char *network_helper_epithet;
596 const char *support_helper_epithet;
597 } chanserv_conf;
598
599 struct listData
600 {
601 struct userNode *user;
602 struct userNode *bot;
603 struct chanNode *channel;
604 const char *search;
605 unsigned short lowest;
606 unsigned short highest;
607 struct userData **users;
608 struct helpfile_table table;
609 };
610
611 enum note_access_type
612 {
613 NOTE_SET_CHANNEL_ACCESS,
614 NOTE_SET_CHANNEL_SETTER,
615 NOTE_SET_PRIVILEGED
616 };
617
618 enum note_visible_type
619 {
620 NOTE_VIS_ALL,
621 NOTE_VIS_CHANNEL_USERS,
622 NOTE_VIS_PRIVILEGED
623 };
624
625 struct note_type
626 {
627 enum note_access_type set_access_type;
628 union {
629 unsigned int min_opserv;
630 unsigned short min_ulevel;
631 } set_access;
632 enum note_visible_type visible_type;
633 unsigned int max_length;
634 unsigned int refs;
635 char name[1];
636 };
637
638 struct note
639 {
640 struct note_type *type;
641 char setter[NICKSERV_HANDLE_LEN+1];
642 char note[1];
643 };
644
645 static unsigned int registered_channels;
646 static unsigned int banCount;
647
648 static const struct {
649 char *name;
650 char *title;
651 unsigned short level;
652 char ch;
653 } accessLevels[] = { /* MUST be orderd less to most! */
654 { "peon", "Peon", UL_PEON, '+' },
655 { "halfop", "HalfOp", UL_HALFOP, '%' },
656 { "op", "Op", UL_OP, '@' },
657 { "manager", "Manager", UL_MANAGER, '%' },
658 { "coowner", "Coowner", UL_COOWNER, '*' },
659 { "owner", "Owner", UL_OWNER, '!' },
660 { "helper", "BUG:", UL_HELPER, 'X' }
661 };
662
663 /* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
664 static const struct {
665 char *format_name;
666 char *db_name;
667 unsigned short default_value;
668 unsigned int old_idx;
669 unsigned int old_flag;
670 unsigned short flag_value;
671 } levelOptions[] = {
672 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
673 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
674 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
675 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
676 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
677 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
678 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
679 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
680 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
681 };
682
683 struct charOptionValues {
684 char value;
685 char *format_name;
686 } automodeValues[] = {
687 { 'n', "CSMSG_AUTOMODE_NONE" },
688 { 'y', "CSMSG_AUTOMODE_NORMAL" },
689 { 'v', "CSMSG_AUTOMODE_VOICE" },
690 { 'h', "CSMSG_AUTOMODE_HOP" },
691 { 'o', "CSMSG_AUTOMODE_OP" },
692 { 'm', "CSMSG_AUTOMODE_MUTE" },
693 { 'l', "CSMSG_AUTOMODE_ONLYVOICE" }
694 }, protectValues[] = {
695 { 'a', "CSMSG_PROTECT_ALL" },
696 { 'e', "CSMSG_PROTECT_EQUAL" },
697 { 'l', "CSMSG_PROTECT_LOWER" },
698 { 'n', "CSMSG_PROTECT_NONE" }
699 }, toysValues[] = {
700 { 'd', "CSMSG_TOYS_DISABLED" },
701 { 'n', "CSMSG_TOYS_PRIVATE" },
702 { 'p', "CSMSG_TOYS_PUBLIC" }
703 }, topicRefreshValues[] = {
704 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
705 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
706 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
707 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
708 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
709 }, ctcpReactionValues[] = {
710 { 'n', "CSMSG_CTCPREACTION_NONE" },
711 { 'k', "CSMSG_CTCPREACTION_KICK" },
712 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
713 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
714 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
715 }, banTimeoutValues[] = {
716 { '0', "CSMSG_BANTIMEOUT_NONE" },
717 { '1', "CSMSG_BANTIMEOUT_10M" },
718 { '2', "CSMSG_BANTIMEOUT_2H" },
719 { '3', "CSMSG_BANTIMEOUT_4H" },
720 { '4', "CSMSG_BANTIMEOUT_1D" },
721 { '5', "CSMSG_BANTIMEOUT_1W" }
722 },
723 resyncValues[] = {
724 { 'n', "CSMSG_RESYNC_NEVER" },
725 { '1', "CSMSG_RESYNC_3_HOURS" },
726 { '2', "CSMSG_RESYNC_6_HOURS" },
727 { '3', "CSMSG_RESYNC_12_HOURS" },
728 { '4', "CSMSG_RESYNC_24_HOURS" }
729 };
730
731 static const struct {
732 char *format_name;
733 char *db_name;
734 char default_value;
735 unsigned int old_idx;
736 unsigned char count;
737 struct charOptionValues *values;
738 } charOptions[] = {
739 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues), automodeValues },
740 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
741 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
742 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
743 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues },
744 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues), banTimeoutValues },
745 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues), resyncValues },
746 };
747
748 struct userData *helperList;
749 struct chanData *channelList;
750 static struct module *chanserv_module;
751 static unsigned int userCount;
752 unsigned int chanserv_read_version = 0; /* db version control */
753
754 #define CHANSERV_DB_VERSION 2
755
756 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
757 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
758
759 unsigned short
760 user_level_from_name(const char *name, unsigned short clamp_level)
761 {
762 unsigned int level = 0, ii;
763 if(isdigit(name[0]))
764 level = strtoul(name, NULL, 10);
765 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
766 if(!irccasecmp(name, accessLevels[ii].name))
767 level = accessLevels[ii].level;
768 if(level > clamp_level)
769 return 0;
770 return level;
771 }
772
773 char *
774 user_level_name_from_level(int level)
775 {
776 unsigned int ii;
777 char* highest;
778
779 highest = "None";
780 if(level >= 1)
781 highest = "Peon";
782 for(ii = 0; (ii < ArrayLength(accessLevels)); ii++)
783 if(level >= accessLevels[ii].level)
784 highest = accessLevels[ii].title;
785 return(highest);
786 }
787
788
789 int
790 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
791 {
792 char *sep;
793 *minl = strtoul(arg, &sep, 10);
794 if(*sep == '\0')
795 {
796 *maxl = *minl;
797 return 1;
798 }
799 else if(*sep == '-')
800 {
801 *maxl = strtoul(sep+1, &sep, 10);
802 return *sep == '\0';
803 }
804 else
805 return 0;
806 }
807
808 struct userData*
809 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
810 {
811 struct userData *uData, **head;
812
813 if(!channel || !handle)
814 return NULL;
815
816 if(override && HANDLE_FLAGGED(handle, HELPING)
817 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
818 {
819 for(uData = helperList;
820 uData && uData->handle != handle;
821 uData = uData->next);
822
823 if(!uData)
824 {
825 uData = calloc(1, sizeof(struct userData));
826 uData->handle = handle;
827
828 uData->access = UL_HELPER;
829 uData->seen = 0;
830
831 uData->info = NULL;
832
833 uData->prev = NULL;
834 uData->next = helperList;
835 if(helperList)
836 helperList->prev = uData;
837 helperList = uData;
838 }
839
840 head = &helperList;
841 }
842 else
843 {
844 for(uData = channel->users; uData; uData = uData->next)
845 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
846 break;
847
848 head = &(channel->users);
849 }
850
851 if(uData && (uData != *head))
852 {
853 /* Shuffle the user to the head of whatever list he was in. */
854 if(uData->next)
855 uData->next->prev = uData->prev;
856 if(uData->prev)
857 uData->prev->next = uData->next;
858
859 uData->prev = NULL;
860 uData->next = *head;
861
862 if(*head)
863 (**head).prev = uData;
864 *head = uData;
865 }
866
867 return uData;
868 }
869
870 /* Returns non-zero if user has at least the minimum access.
871 * exempt_owner is set when handling !set, so the owner can set things
872 * to/from >500.
873 */
874 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
875 {
876 struct userData *uData;
877 struct chanData *cData = channel->channel_info;
878 unsigned short minimum = cData->lvlOpts[opt];
879 if(!minimum)
880 return 1;
881 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
882 if(!uData)
883 return 0;
884 if(minimum <= uData->access)
885 return 1;
886 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
887 return 1;
888 return 0;
889 }
890
891 /* Scan for other users authenticated to the same handle
892 still in the channel. If so, keep them listed as present.
893
894 user is optional, if not null, it skips checking that userNode
895 (for the handle_part function) */
896 static void
897 scan_user_presence(struct userData *uData, struct userNode *user)
898 {
899 struct modeNode *mn;
900
901 if(IsSuspended(uData->channel)
902 || IsUserSuspended(uData)
903 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
904 {
905 uData->present = 0;
906 }
907 else
908 {
909 uData->present = 1;
910 uData->seen = now;
911 }
912 }
913
914 static void
915 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
916 {
917 unsigned int eflags, argc;
918 char *argv[4];
919 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
920
921 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
922 if(!channel->channel_info
923 || IsSuspended(channel->channel_info)
924 || IsService(user)
925 || !ircncasecmp(text, "ACTION ", 7))
926 return;
927 /* We dont punish people we know -Rubin
928 * * Figure out the minimum level needed to CTCP the channel *
929 *
930 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
931 * return;
932 */
933 /* If they are a user of the channel, they are exempt */
934 if(_GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
935 return;
936 /* We need to enforce against them; do so. */
937 eflags = 0;
938 argv[0] = text;
939 argv[1] = user->nick;
940 argc = 2;
941 if(GetUserMode(channel, user))
942 eflags |= ACTION_KICK;
943 switch(channel->channel_info->chOpts[chCTCPReaction]) {
944 default: case 'n': return;
945 case 'k':
946 eflags |= ACTION_KICK;
947 break;
948 case 'b':
949 eflags |= ACTION_BAN;
950 break;
951 case 't':
952 eflags |= ACTION_BAN | ACTION_ADD_LAMER | ACTION_ADD_TIMED_LAMER;
953 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
954 break;
955 case 'T':
956 eflags |= ACTION_BAN | ACTION_ADD_LAMER | ACTION_ADD_TIMED_LAMER;
957 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
958 break;
959 }
960 argv[argc++] = bad_ctcp_reason;
961 eject_user(chanserv, channel, argc, argv, NULL, eflags);
962 }
963
964 struct note_type *
965 chanserv_create_note_type(const char *name)
966 {
967 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
968 strcpy(ntype->name, name);
969 ntype->refs = 1;
970 dict_insert(note_types, ntype->name, ntype);
971 return ntype;
972 }
973
974 static void
975 chanserv_deref_note_type(void *data)
976 {
977 struct note_type *ntype = data;
978
979 if(--ntype->refs > 0)
980 return;
981 free(ntype);
982 }
983
984 static void
985 chanserv_flush_note_type(struct note_type *ntype)
986 {
987 struct chanData *cData;
988 for(cData = channelList; cData; cData = cData->next)
989 dict_remove(cData->notes, ntype->name);
990 }
991
992 static void
993 chanserv_truncate_notes(struct note_type *ntype)
994 {
995 struct chanData *cData;
996 struct note *note;
997 unsigned int size = sizeof(*note) + ntype->max_length;
998
999 for(cData = channelList; cData; cData = cData->next) {
1000 note = dict_find(cData->notes, ntype->name, NULL);
1001 if(!note)
1002 continue;
1003 if(strlen(note->note) <= ntype->max_length)
1004 continue;
1005 dict_remove2(cData->notes, ntype->name, 1);
1006 note = realloc(note, size);
1007 note->note[ntype->max_length] = 0;
1008 dict_insert(cData->notes, ntype->name, note);
1009 }
1010 }
1011
1012 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
1013
1014 static struct note *
1015 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
1016 {
1017 struct note *note;
1018 unsigned int len = strlen(text);
1019
1020 if(len > type->max_length) len = type->max_length;
1021 note = calloc(1, sizeof(*note) + len);
1022 note->type = type;
1023 strncpy(note->setter, setter, sizeof(note->setter)-1);
1024 memcpy(note->note, text, len);
1025 note->note[len] = 0;
1026 dict_insert(channel->notes, type->name, note);
1027 type->refs++;
1028 return note;
1029 }
1030
1031 static void
1032 chanserv_free_note(void *data)
1033 {
1034 struct note *note = data;
1035
1036 chanserv_deref_note_type(note->type);
1037 assert(note->type->refs > 0); /* must use delnote to remove the type */
1038 free(note);
1039 }
1040
1041 static MODCMD_FUNC(cmd_createnote) {
1042 struct note_type *ntype;
1043 unsigned int arg = 1, existed = 0, max_length;
1044
1045 if((ntype = dict_find(note_types, argv[1], NULL)))
1046 existed = 1;
1047 else
1048 ntype = chanserv_create_note_type(argv[arg]);
1049 if(!irccasecmp(argv[++arg], "privileged"))
1050 {
1051 arg++;
1052 ntype->set_access_type = NOTE_SET_PRIVILEGED;
1053 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1054 }
1055 else if(!irccasecmp(argv[arg], "channel"))
1056 {
1057 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1058 if(!ulvl)
1059 {
1060 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1061 goto fail;
1062 }
1063 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1064 ntype->set_access.min_ulevel = ulvl;
1065 }
1066 else if(!irccasecmp(argv[arg], "setter"))
1067 {
1068 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1069 }
1070 else
1071 {
1072 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1073 goto fail;
1074 }
1075
1076 if(!irccasecmp(argv[++arg], "privileged"))
1077 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1078 else if(!irccasecmp(argv[arg], "channel_users"))
1079 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1080 else if(!irccasecmp(argv[arg], "all"))
1081 ntype->visible_type = NOTE_VIS_ALL;
1082 else {
1083 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1084 goto fail;
1085 }
1086
1087 if((arg+1) >= argc) {
1088 reply("MSG_MISSING_PARAMS", argv[0]);
1089 goto fail;
1090 }
1091 max_length = strtoul(argv[++arg], NULL, 0);
1092 if(max_length < 20 || max_length > 450)
1093 {
1094 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1095 goto fail;
1096 }
1097 if(existed && (max_length < ntype->max_length))
1098 {
1099 ntype->max_length = max_length;
1100 chanserv_truncate_notes(ntype);
1101 }
1102 ntype->max_length = max_length;
1103
1104 if(existed)
1105 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1106 else
1107 reply("CSMSG_NOTE_CREATED", ntype->name);
1108 return 1;
1109
1110 fail:
1111 if(!existed)
1112 dict_remove(note_types, ntype->name);
1113 return 0;
1114 }
1115
1116 static MODCMD_FUNC(cmd_removenote) {
1117 struct note_type *ntype;
1118 int force;
1119
1120 ntype = dict_find(note_types, argv[1], NULL);
1121 force = (argc > 2) && !irccasecmp(argv[2], "force");
1122 if(!ntype)
1123 {
1124 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1125 return 0;
1126 }
1127 if(ntype->refs > 1)
1128 {
1129 if(!force)
1130 {
1131 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1132 return 0;
1133 }
1134 chanserv_flush_note_type(ntype);
1135 }
1136 dict_remove(note_types, argv[1]);
1137 reply("CSMSG_NOTE_DELETED", argv[1]);
1138 return 1;
1139 }
1140
1141 static int
1142 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1143 {
1144 if(!orig)
1145 return 0;
1146 if(orig->modes_set & change->modes_clear)
1147 return 1;
1148 if(orig->modes_clear & change->modes_set)
1149 return 1;
1150 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1151 && strcmp(orig->new_key, change->new_key))
1152 return 1;
1153 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1154 && (orig->new_limit != change->new_limit))
1155 return 1;
1156 return 0;
1157 }
1158
1159 static char max_length_text[MAXLEN+1][16];
1160
1161 static struct helpfile_expansion
1162 chanserv_expand_variable(const char *variable)
1163 {
1164 struct helpfile_expansion exp;
1165
1166 if(!irccasecmp(variable, "notes"))
1167 {
1168 dict_iterator_t it;
1169 exp.type = HF_TABLE;
1170 exp.value.table.length = 1;
1171 exp.value.table.width = 3;
1172 exp.value.table.flags = 0;
1173 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1174 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1175 exp.value.table.contents[0][0] = "Note Type";
1176 exp.value.table.contents[0][1] = "Visibility";
1177 exp.value.table.contents[0][2] = "Max Length";
1178 for(it=dict_first(note_types); it; it=iter_next(it))
1179 {
1180 struct note_type *ntype = iter_data(it);
1181 int row;
1182
1183 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1184 row = exp.value.table.length++;
1185 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1186 exp.value.table.contents[row][0] = ntype->name;
1187 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1188 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1189 "unknown";
1190 if(!max_length_text[ntype->max_length][0])
1191 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1192 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1193 }
1194 return exp;
1195 }
1196
1197 exp.type = HF_STRING;
1198 exp.value.str = NULL;
1199 return exp;
1200 }
1201
1202 static struct chanData*
1203 register_channel(struct chanNode *cNode, char *registrar)
1204 {
1205 struct chanData *channel;
1206 enum levelOption lvlOpt;
1207 enum charOption chOpt;
1208
1209 channel = calloc(1, sizeof(struct chanData));
1210
1211 channel->notes = dict_new();
1212 dict_set_free_data(channel->notes, chanserv_free_note);
1213
1214 channel->registrar = strdup(registrar);
1215 channel->registered = now;
1216 channel->visited = now;
1217 channel->limitAdjusted = now;
1218 channel->ownerTransfer = now;
1219 channel->flags = CHANNEL_DEFAULT_FLAGS;
1220 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1221 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1222 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1223 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1224
1225 channel->prev = NULL;
1226 channel->next = channelList;
1227
1228 if(channelList)
1229 channelList->prev = channel;
1230 channelList = channel;
1231 registered_channels++;
1232
1233 channel->channel = cNode;
1234 LockChannel(cNode);
1235 cNode->channel_info = channel;
1236
1237 return channel;
1238 }
1239
1240 static struct userData*
1241 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1242 {
1243 struct userData *ud;
1244
1245 if(access > UL_OWNER)
1246 return NULL;
1247
1248 ud = calloc(1, sizeof(*ud));
1249 ud->channel = channel;
1250 ud->handle = handle;
1251 ud->seen = seen;
1252 ud->access = access;
1253 ud->info = info ? strdup(info) : NULL;
1254
1255 ud->prev = NULL;
1256 ud->next = channel->users;
1257 if(channel->users)
1258 channel->users->prev = ud;
1259 channel->users = ud;
1260
1261 channel->userCount++;
1262 userCount++;
1263
1264 ud->u_prev = NULL;
1265 ud->u_next = ud->handle->channels;
1266 if(ud->u_next)
1267 ud->u_next->u_prev = ud;
1268 ud->handle->channels = ud;
1269
1270 ud->flags = USER_FLAGS_DEFAULT;
1271 return ud;
1272 }
1273
1274 static void unregister_channel(struct chanData *channel, const char *reason);
1275
1276 void
1277 del_channel_user(struct userData *user, int do_gc)
1278 {
1279 struct chanData *channel = user->channel;
1280
1281 channel->userCount--;
1282 userCount--;
1283
1284 if(user->prev)
1285 user->prev->next = user->next;
1286 else
1287 channel->users = user->next;
1288 if(user->next)
1289 user->next->prev = user->prev;
1290
1291 if(user->u_prev)
1292 user->u_prev->u_next = user->u_next;
1293 else
1294 user->handle->channels = user->u_next;
1295 if(user->u_next)
1296 user->u_next->u_prev = user->u_prev;
1297
1298 free(user->info);
1299 free(user);
1300 if(do_gc && !channel->users && !IsProtected(channel)) {
1301 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1302 unregister_channel(channel, "lost all users.");
1303 }
1304 }
1305
1306 static struct adduserPending*
1307 add_adduser_pending(struct chanNode *channel, struct userNode *user, int level)
1308 {
1309 struct adduserPending *ap;
1310 ap = calloc(1,sizeof(struct adduserPending));
1311 ap->channel = channel;
1312 ap->user = user;
1313 ap->level = level;
1314 ap->created = time(NULL);
1315
1316 /* ap->prev defaults to NULL already.. */
1317 ap->next = adduser_pendings;
1318 if(adduser_pendings)
1319 adduser_pendings->prev = ap;
1320 adduser_pendings = ap;
1321 adduser_pendings_count++;
1322 return(ap);
1323 }
1324
1325 static void
1326 del_adduser_pending(struct adduserPending *ap)
1327 {
1328 if(ap->prev)
1329 ap->prev->next = ap->next;
1330 else
1331 adduser_pendings = ap->next;
1332
1333 if(ap->next)
1334 ap->next->prev = ap->prev;
1335 free(ap);
1336 }
1337
1338 static void expire_adduser_pending();
1339
1340 /* find_adduser_pending(channel, user) will find an arbitrary record
1341 * from user, channel, or user and channel.
1342 * if user or channel are NULL, they will match any records.
1343 */
1344 static struct adduserPending*
1345 find_adduser_pending(struct chanNode *channel, struct userNode *user)
1346 {
1347 struct adduserPending *ap;
1348
1349 expire_adduser_pending(); /* why not here.. */
1350
1351 if(!channel && !user) /* 2 nulls matches all */
1352 return(adduser_pendings);
1353 for(ap = adduser_pendings;ap;ap = ap->next)
1354 {
1355 if((channel == ap->channel && (user == NULL || user == ap->user)) || (user==ap->user && channel==NULL))
1356 return ap;
1357 }
1358 return NULL;
1359 }
1360
1361
1362 /* Remove all pendings for a user or channel
1363 *
1364 * called in nickserv.c DelUser() and proto-* unregister_channel()
1365 */
1366 void
1367 wipe_adduser_pending(struct chanNode *channel, struct userNode *user)
1368 {
1369 struct adduserPending *ap;
1370
1371 /* So this is a bit wastefull, i hate dealing with linked lists.
1372 * if its a problem we'll rewrite it right */
1373 while((ap = find_adduser_pending(channel, user))) {
1374 del_adduser_pending(ap);
1375 }
1376 }
1377
1378 /* Called from nickserv.c cmd_auth after someone auths */
1379 void
1380 process_adduser_pending(struct userNode *user)
1381 {
1382 struct adduserPending *ap;
1383 if(!user->handle_info)
1384 return; /* not associated with an account */
1385 while((ap = find_adduser_pending(NULL, user)))
1386 {
1387 struct userData *actee;
1388 if(GetTrueChannelAccess(ap->channel->channel_info, ap->user->handle_info))
1389 {
1390 /* Already on the userlist. do nothing*/
1391 }
1392 else
1393 {
1394 actee = add_channel_user(ap->channel->channel_info, ap->user->handle_info, ap->level, 0, NULL);
1395 scan_user_presence(actee, NULL);
1396 }
1397 del_adduser_pending(ap);
1398 }
1399 }
1400
1401 static void
1402 expire_adduser_pending()
1403 {
1404 struct adduserPending *ap, *ap_next;
1405 ap = adduser_pendings;
1406 while(ap)
1407 {
1408 if((ap->created + ADDUSER_PENDING_EXPIRE) < time(NULL))
1409 { /* expire it */
1410 ap_next = ap->next; /* save next */
1411 del_adduser_pending(ap); /* free and relink */
1412 ap = ap_next; /* advance */
1413 }
1414 else
1415 ap = ap->next;
1416 }
1417 }
1418
1419 static void expire_ban(void *data);
1420
1421 struct banData*
1422 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1423 {
1424 struct banData *bd;
1425 unsigned int ii, l1, l2;
1426
1427 if(!mask)
1428 return NULL;
1429
1430 bd = malloc(sizeof(struct banData));
1431
1432 bd->channel = channel;
1433 bd->set = set;
1434 bd->triggered = triggered;
1435 bd->expires = expires;
1436
1437 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1438 {
1439 extern const char *hidden_host_suffix;
1440 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1441 char *new_mask;
1442
1443 l1 = strlen(mask);
1444 l2 = strlen(old_name);
1445 if(l2+2 > l1)
1446 continue;
1447 if(irccasecmp(mask + l1 - l2, old_name))
1448 continue;
1449 new_mask = alloca(MAXLEN);
1450 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1451 mask = new_mask;
1452 }
1453 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1454 if(owner)
1455 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1456 bd->reason = strdup(reason);
1457
1458 if(expires)
1459 timeq_add(expires, expire_ban, bd);
1460
1461 bd->prev = NULL;
1462 bd->next = channel->bans; /* lamers */
1463 if(channel->bans)
1464 channel->bans->prev = bd;
1465 channel->bans = bd;
1466 channel->banCount++;
1467 banCount++;
1468
1469 return bd;
1470 }
1471
1472 static void
1473 del_channel_ban(struct banData *ban)
1474 {
1475 ban->channel->banCount--;
1476 banCount--;
1477
1478 if(ban->prev)
1479 ban->prev->next = ban->next;
1480 else
1481 ban->channel->bans = ban->next;
1482
1483 if(ban->next)
1484 ban->next->prev = ban->prev;
1485
1486 if(ban->expires)
1487 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1488
1489 if(ban->reason)
1490 free(ban->reason);
1491
1492 free(ban);
1493 }
1494
1495 static void
1496 expire_ban(void *data) /* lamer.. */
1497 {
1498 struct banData *bd = data;
1499 if(!IsSuspended(bd->channel))
1500 {
1501 struct banList bans;
1502 struct mod_chanmode change;
1503 unsigned int ii;
1504 bans = bd->channel->channel->banlist;
1505 mod_chanmode_init(&change);
1506 for(ii=0; ii<bans.used; ii++)
1507 {
1508 if(!strcmp(bans.list[ii]->ban, bd->mask))
1509 {
1510 change.argc = 1;
1511 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1512 change.args[0].u.hostmask = bd->mask;
1513 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1514 break;
1515 }
1516 }
1517 }
1518 bd->expires = 0;
1519 del_channel_ban(bd);
1520 }
1521
1522 static void chanserv_expire_suspension(void *data);
1523
1524 static void
1525 unregister_channel(struct chanData *channel, const char *reason)
1526 {
1527 struct mod_chanmode change;
1528 char msgbuf[MAXLEN];
1529
1530 /* After channel unregistration, the following must be cleaned
1531 up:
1532 - Channel information.
1533 - Channel users.
1534 - Channel bans. (lamers)
1535 - Channel suspension data.
1536 - adduser_pending data.
1537 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1538 */
1539
1540 if(!channel)
1541 return;
1542
1543 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1544
1545 if(off_channel > 0)
1546 {
1547 mod_chanmode_init(&change);
1548 change.modes_clear |= MODE_REGISTERED;
1549 mod_chanmode_announce(chanserv, channel->channel, &change);
1550 }
1551
1552 wipe_adduser_pending(channel->channel, NULL);
1553
1554 while(channel->users)
1555 del_channel_user(channel->users, 0);
1556
1557 while(channel->bans)
1558 del_channel_ban(channel->bans);
1559
1560 free(channel->topic);
1561 free(channel->registrar);
1562 free(channel->greeting);
1563 free(channel->user_greeting);
1564 free(channel->topic_mask);
1565
1566 if(channel->prev)
1567 channel->prev->next = channel->next;
1568 else
1569 channelList = channel->next;
1570
1571 if(channel->next)
1572 channel->next->prev = channel->prev;
1573
1574 if(channel->suspended)
1575 {
1576 struct chanNode *cNode = channel->channel;
1577 struct suspended *suspended, *next_suspended;
1578
1579 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1580 {
1581 next_suspended = suspended->previous;
1582 free(suspended->suspender);
1583 free(suspended->reason);
1584 if(suspended->expires)
1585 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1586 free(suspended);
1587 }
1588
1589 if(cNode)
1590 cNode->channel_info = NULL;
1591 }
1592 channel->channel->channel_info = NULL;
1593
1594 dict_delete(channel->notes);
1595 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1596 if(!IsSuspended(channel))
1597 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1598 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1599 UnlockChannel(channel->channel);
1600 free(channel);
1601 registered_channels--;
1602 }
1603
1604 static void
1605 expire_channels(UNUSED_ARG(void *data))
1606 {
1607 struct chanData *channel, *next;
1608 struct userData *user;
1609 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1610
1611 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1612 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1613
1614 for(channel = channelList; channel; channel = next)
1615 {
1616 next = channel->next;
1617
1618 /* See if the channel can be expired. */
1619 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1620 || IsProtected(channel))
1621 continue;
1622
1623 /* Make sure there are no high-ranking users still in the channel. */
1624 for(user=channel->users; user; user=user->next)
1625 if(user->present && (user->access >= UL_PRESENT))
1626 break;
1627 if(user)
1628 continue;
1629
1630 /* Unregister the channel */
1631 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1632 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1633 unregister_channel(channel, "registration expired.");
1634 }
1635
1636 if(chanserv_conf.channel_expire_frequency)
1637 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1638 }
1639
1640 static int
1641 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel, int protect_invitables)
1642 {
1643 char protect = channel->chOpts[chProtect];
1644 struct userData *cs_victim, *cs_aggressor;
1645
1646 /* If victim access level is greater than set invitelevel, don't let
1647 * us kick them, but don't consider it punishment if someone else does
1648 */
1649
1650
1651 if(victim == aggressor)
1652 return 0;
1653 /* Don't protect if the victim isn't authenticated (because they
1654 can't be a channel user), unless we are to protect non-users
1655 also. */
1656
1657 cs_victim = GetChannelAccess(channel, victim->handle_info);
1658
1659 /* If they have enough access to invite themselvs through a ban,
1660 * and its us kicking them, don't. -Rubin */
1661 if(protect_invitables==true && cs_victim && (cs_victim->access >= channel->lvlOpts[lvlInviteMe]))
1662 return 1;
1663
1664 if(protect == 'n')
1665 return 0;
1666
1667 if(protect != 'a' && !cs_victim)
1668 return 0;
1669
1670 /* Protect if the aggressor isn't a user because at this point,
1671 the aggressor can only be less than or equal to the victim. */
1672
1673 /* Not protected from chanserv except above */
1674 /* XXX: need to generic-ize chanserv to "one of x3's services" somehow.. */
1675 if(aggressor == chanserv)
1676 return 0;
1677
1678 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1679 if(!cs_aggressor)
1680 return 1;
1681
1682 /* If the aggressor was a user, then the victim can't be helped. */
1683 if(!cs_victim)
1684 return 0;
1685
1686 switch(protect)
1687 {
1688 case 'l':
1689 if(cs_victim->access > cs_aggressor->access)
1690 return 1;
1691 break;
1692 case 'a':
1693 case 'e':
1694 if(cs_victim->access >= cs_aggressor->access)
1695 return 1;
1696 break;
1697 }
1698
1699 return 0;
1700 }
1701
1702 static int
1703 validate_op(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, struct userNode *victim)
1704 {
1705 struct chanData *cData = channel->channel_info;
1706 struct userData *cs_victim;
1707
1708 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1709 || (cs_victim->access < UL_OP /* cData->lvlOpts[lvlGiveOps]*/))
1710 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1711 {
1712 if(cmd)
1713 reply("CSMSG_OPBY_LOCKED");
1714 else
1715 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1716 return 0;
1717 }
1718
1719 return 1;
1720 }
1721
1722 static int
1723 validate_halfop(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, struct userNode *victim)
1724 {
1725 struct chanData *cData = channel->channel_info;
1726 struct userData *cs_victim;
1727
1728 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1729 || (cs_victim->access < UL_HALFOP /* cData->lvlOpts[lvlGiveHalfOps] */))
1730 && !check_user_level(channel, user, lvlEnfHalfOps, 0, 0))
1731 {
1732 reply("CSMSG_HOPBY_LOCKED");
1733 return 0;
1734 }
1735
1736 return 1;
1737 }
1738
1739
1740 static int
1741 validate_deop(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, struct userNode *victim)
1742 {
1743 if(IsService(victim))
1744 {
1745 reply("MSG_SERVICE_IMMUNE", victim->nick);
1746 return 0;
1747 }
1748
1749 if(protect_user(victim, user, channel->channel_info, false))
1750 {
1751 reply("CSMSG_USER_PROTECTED", victim->nick);
1752 return 0;
1753 }
1754
1755 return 1;
1756 }
1757
1758 static int
1759 validate_dehop(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, struct userNode *victim)
1760 {
1761 if(IsService(victim))
1762 {
1763 reply("MSG_SERVICE_IMMUNE", victim->nick);
1764 return 0;
1765 }
1766
1767 if(protect_user(victim, user, channel->channel_info, false))
1768 {
1769 reply("CSMSG_USER_PROTECTED", victim->nick);
1770 return 0;
1771 }
1772
1773 return 1;
1774 }
1775
1776 static struct do_not_register *
1777 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1778 {
1779 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1780 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1781 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1782 strcpy(dnr->reason, reason);
1783 dnr->set = now;
1784 if(dnr->chan_name[0] == '*')
1785 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1786 else if(strpbrk(dnr->chan_name, "*?"))
1787 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1788 else
1789 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1790 return dnr;
1791 }
1792
1793 static struct dnrList
1794 chanserv_find_dnrs(const char *chan_name, const char *handle)
1795 {
1796 struct dnrList list;
1797 dict_iterator_t it;
1798 struct do_not_register *dnr;
1799
1800 dnrList_init(&list);
1801 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1802 dnrList_append(&list, dnr);
1803 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1804 dnrList_append(&list, dnr);
1805 if(chan_name)
1806 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1807 if(match_ircglob(chan_name, iter_key(it)))
1808 dnrList_append(&list, iter_data(it));
1809 return list;
1810 }
1811
1812 static unsigned int
1813 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1814 {
1815 struct dnrList list;
1816 struct do_not_register *dnr;
1817 unsigned int ii;
1818 char buf[INTERVALLEN];
1819
1820 list = chanserv_find_dnrs(chan_name, handle);
1821 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1822 {
1823 dnr = list.list[ii];
1824 if(dnr->set)
1825 {
1826 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1827 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1828 }
1829 else
1830 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1831 }
1832 if(ii < list.used)
1833 reply("CSMSG_MORE_DNRS", list.used - ii);
1834 free(list.list);
1835 return ii;
1836 }
1837
1838 struct do_not_register *
1839 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1840 {
1841 struct do_not_register *dnr;
1842 dict_iterator_t it;
1843
1844 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1845 return dnr;
1846 if(chan_name)
1847 {
1848 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1849 return dnr;
1850 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1851 if(match_ircglob(chan_name, iter_key(it)))
1852 return iter_data(it);
1853 }
1854 return NULL;
1855 }
1856
1857 static CHANSERV_FUNC(cmd_noregister)
1858 {
1859 const char *target;
1860 struct do_not_register *dnr;
1861 char buf[INTERVALLEN];
1862 unsigned int matches;
1863
1864 if(argc < 2)
1865 {
1866 dict_iterator_t it;
1867
1868 reply("CSMSG_DNR_SEARCH_RESULTS");
1869 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
1870 reply("CSMSG_BAR");
1871 matches = 0;
1872 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1873 {
1874 dnr = iter_data(it);
1875 if(dnr->set)
1876 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1877 else
1878 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1879 matches++;
1880 }
1881 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1882 {
1883 dnr = iter_data(it);
1884 if(dnr->set)
1885 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1886 else
1887 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1888 matches++;
1889 }
1890 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1891 {
1892 dnr = iter_data(it);
1893 if(dnr->set)
1894 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1895 else
1896 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1897 matches++;
1898 }
1899
1900 if(matches)
1901 reply("MSG_MATCH_COUNT", matches);
1902 else
1903 reply("MSG_NO_MATCHES");
1904 return 0;
1905 }
1906
1907 target = argv[1];
1908
1909 if(!IsChannelName(target) && (*target != '*'))
1910 {
1911 reply("CSMSG_NOT_DNR", target);
1912 return 0;
1913 }
1914
1915 if(argc > 2)
1916 {
1917 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1918 if((*target == '*') && !get_handle_info(target + 1))
1919 {
1920 reply("MSG_HANDLE_UNKNOWN", target + 1);
1921 return 0;
1922 }
1923 chanserv_add_dnr(target, user->handle_info->handle, reason);
1924 reply("CSMSG_NOREGISTER_CHANNEL", target);
1925 return 1;
1926 }
1927
1928 reply("CSMSG_DNR_SEARCH_RESULTS");
1929 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
1930 reply("CSMSG_BAR");
1931 if(*target == '*')
1932 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1933 else
1934 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1935 if(!matches)
1936 reply("MSG_NO_MATCHES");
1937 return 0;
1938 }
1939
1940 static CHANSERV_FUNC(cmd_allowregister)
1941 {
1942 const char *chan_name = argv[1];
1943
1944 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1945 {
1946 dict_remove(handle_dnrs, chan_name+1);
1947 reply("CSMSG_DNR_REMOVED", chan_name);
1948 }
1949 else if(dict_find(plain_dnrs, chan_name, NULL))
1950 {
1951 dict_remove(plain_dnrs, chan_name);
1952 reply("CSMSG_DNR_REMOVED", chan_name);
1953 }
1954 else if(dict_find(mask_dnrs, chan_name, NULL))
1955 {
1956 dict_remove(mask_dnrs, chan_name);
1957 reply("CSMSG_DNR_REMOVED", chan_name);
1958 }
1959 else
1960 {
1961 reply("CSMSG_NO_SUCH_DNR", chan_name);
1962 return 0;
1963 }
1964 return 1;
1965 }
1966
1967 unsigned int
1968 chanserv_get_owned_count(struct handle_info *hi)
1969 {
1970 struct userData *cList;
1971 unsigned int owned;
1972
1973 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1974 if(cList->access == UL_OWNER)
1975 owned++;
1976 return owned;
1977 }
1978
1979 static CHANSERV_FUNC(cmd_register)
1980 {
1981 struct handle_info *handle;
1982 struct chanData *cData;
1983 struct modeNode *mn;
1984 char reason[MAXLEN];
1985 char *chan_name;
1986 unsigned int new_channel, force=0;
1987 struct do_not_register *dnr;
1988 unsigned int n;
1989
1990 if (checkDefCon(DEFCON_NO_NEW_CHANNELS) && !IsOper(user)) {
1991 reply("CSMSG_DEFCON_NO_NEW_CHANNELS");
1992 return 0;
1993 }
1994
1995 if(channel)
1996 {
1997 if(channel->channel_info)
1998 {
1999 reply("CSMSG_ALREADY_REGGED", channel->name);
2000 return 0;
2001 }
2002
2003 if(channel->bad_channel)
2004 {
2005 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2006 return 0;
2007 }
2008
2009 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2010 {
2011 reply("CSMSG_MUST_BE_OPPED", channel->name);
2012 return 0;
2013 }
2014
2015 new_channel = 0;
2016 chan_name = channel->name;
2017 }
2018 else
2019 {
2020 if(argc < 2)
2021 {
2022 reply("MSG_MISSING_PARAMS", cmd->name);
2023 svccmd_send_help_brief(user, chanserv, cmd);
2024 return 0;
2025 }
2026 if(!IsChannelName(argv[1]))
2027 {
2028 reply("MSG_NOT_CHANNEL_NAME");
2029 return 0;
2030 }
2031
2032 if(opserv_bad_channel(argv[1]))
2033 {
2034 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2035 return 0;
2036 }
2037
2038 new_channel = 1;
2039 chan_name = argv[1];
2040 }
2041
2042 if(argc >= (new_channel+2))
2043 {
2044 if(!IsHelping(user))
2045 {
2046 reply("CSMSG_PROXY_FORBIDDEN");
2047 return 0;
2048 }
2049
2050 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2051 return 0;
2052 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2053 dnr = chanserv_is_dnr(chan_name, handle);
2054
2055 /* Check if they are over the limit.. */
2056 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2057 {
2058 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2059 return 0;
2060 }
2061
2062 }
2063 else
2064 {
2065 handle = user->handle_info;
2066 dnr = chanserv_is_dnr(chan_name, handle);
2067 /* Check if they are over the limit.. */
2068 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2069 {
2070 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf.max_owned);
2071 return 0;
2072 }
2073 /* Check if another service is in the channel */
2074 if(channel)
2075 for(n = 0; n < channel->members.used; n++)
2076 {
2077 mn = channel->members.list[n];
2078 if((mn && mn->user && (mn->user->modes & FLAGS_SERVICE)) || IsLocal(mn->user))
2079 {
2080 reply("CSMSG_ANOTHER_SERVICE");
2081 return 0;
2082 }
2083 }
2084 }
2085 if(dnr && !force)
2086 {
2087 if(!IsHelping(user))
2088 reply("CSMSG_DNR_CHANNEL", chan_name);
2089 else
2090 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2091 return 0;
2092 }
2093
2094 /* now handled above for message specilization *
2095 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2096 {
2097 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2098 return 0;
2099 }
2100 */
2101
2102 if(new_channel)
2103 channel = AddChannel(argv[1], now, NULL, NULL, NULL);
2104
2105 cData = register_channel(channel, user->handle_info->handle);
2106 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2107 cData->modes = chanserv_conf.default_modes;
2108 if(off_channel > 0)
2109 cData->modes.modes_set |= MODE_REGISTERED;
2110 if (IsOffChannel(cData))
2111 {
2112 mod_chanmode_announce(chanserv, channel, &cData->modes);
2113 }
2114 else
2115 {
2116 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2117 change->args[change->argc].mode = MODE_CHANOP;
2118 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2119 change->argc++;
2120 mod_chanmode_announce(chanserv, channel, change);
2121 mod_chanmode_free(change);
2122 }
2123
2124 /* Initialize the channel's max user record. */
2125 cData->max = channel->members.used;
2126
2127 if(handle != user->handle_info)
2128 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2129 else
2130
2131 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2132 global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "CSMSG_REGISTERED_TO", channel->name,
2133 handle->handle, user->handle_info->handle);
2134 return 1;
2135 }
2136
2137 static const char *
2138 make_confirmation_string(struct userData *uData)
2139 {
2140 static char strbuf[16];
2141 char *src;
2142 unsigned int accum;
2143
2144 accum = 0;
2145 for(src = uData->handle->handle; *src; )
2146 accum = accum * 31 + toupper(*src++);
2147 if(uData->channel)
2148 for(src = uData->channel->channel->name; *src; )
2149 accum = accum * 31 + toupper(*src++);
2150 sprintf(strbuf, "%08x", accum);
2151 return strbuf;
2152 }
2153
2154 static CHANSERV_FUNC(cmd_unregister)
2155 {
2156 char *name;
2157 char reason[MAXLEN];
2158 struct chanData *cData;
2159 struct userData *uData;
2160
2161 cData = channel->channel_info;
2162 if(!cData)
2163 {
2164 reply("CSMSG_NOT_REGISTERED", channel->name);
2165 return 0;
2166 }
2167
2168 uData = GetChannelUser(cData, user->handle_info);
2169 if(!uData || (uData->access < UL_OWNER))
2170 {
2171 reply("CSMSG_NO_ACCESS");
2172 return 0;
2173 }
2174
2175 if(IsProtected(cData))
2176 {
2177 reply("CSMSG_UNREG_NODELETE", channel->name);
2178 return 0;
2179 }
2180
2181 if(!IsHelping(user))
2182 {
2183 const char *confirm_string;
2184 if(IsSuspended(cData))
2185 {
2186 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2187 return 0;
2188 }
2189 confirm_string = make_confirmation_string(uData);
2190 if((argc < 2) || strcmp(argv[1], confirm_string))
2191 {
2192 reply("CSMSG_CONFIRM_UNREG", channel->name, confirm_string);
2193 return 0;
2194 }
2195 }
2196
2197 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2198 name = strdup(channel->name);
2199 unregister_channel(cData, reason);
2200 spamserv_cs_unregister(user, channel, manually, "unregistered");
2201 reply("CSMSG_UNREG_SUCCESS", name);
2202 free(name);
2203 return 1;
2204 }
2205
2206 static void
2207 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2208 {
2209 extern struct userNode *spamserv;
2210 struct mod_chanmode *change;
2211
2212 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2213 {
2214 change = mod_chanmode_alloc(2);
2215 change->argc = 2;
2216 change->args[0].mode = MODE_CHANOP;
2217 change->args[0].u.member = AddChannelUser(chanserv, channel);
2218 change->args[1].mode = MODE_CHANOP;
2219 change->args[1].u.member = AddChannelUser(spamserv, channel);
2220 }
2221 else
2222 {
2223 change = mod_chanmode_alloc(1);
2224 change->argc = 1;
2225 change->args[0].mode = MODE_CHANOP;
2226 change->args[0].u.member = AddChannelUser(chanserv, channel);
2227 }
2228
2229 mod_chanmode_announce(chanserv, channel, change);
2230 mod_chanmode_free(change);
2231 }
2232
2233 static CHANSERV_FUNC(cmd_move)
2234 {
2235 struct mod_chanmode change;
2236 struct chanNode *target;
2237 struct modeNode *mn;
2238 struct userData *uData;
2239 struct do_not_register *dnr;
2240 int chanserv_join = 0, spamserv_join;
2241
2242 REQUIRE_PARAMS(2);
2243
2244 if(IsProtected(channel->channel_info))
2245 {
2246 reply("CSMSG_MOVE_NODELETE", channel->name);
2247 return 0;
2248 }
2249
2250 if(!IsChannelName(argv[1]))
2251 {
2252 reply("MSG_NOT_CHANNEL_NAME");
2253 return 0;
2254 }
2255
2256 if(opserv_bad_channel(argv[1]))
2257 {
2258 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2259 return 0;
2260 }
2261
2262 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2263 {
2264 for(uData = channel->channel_info->users; uData; uData = uData->next)
2265 {
2266 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2267 {
2268 if(!IsHelping(user))
2269 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2270 else
2271 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2272 return 0;
2273 }
2274 }
2275 }
2276
2277 mod_chanmode_init(&change);
2278 if(!(target = GetChannel(argv[1])))
2279 {
2280 target = AddChannel(argv[1], now, NULL, NULL, NULL);
2281 if(!IsSuspended(channel->channel_info))
2282 chanserv_join = 1;
2283 }
2284 else if(target->channel_info)
2285 {
2286 reply("CSMSG_ALREADY_REGGED", target->name);
2287 return 0;
2288 }
2289 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2290 && !IsHelping(user))
2291 {
2292 reply("CSMSG_MUST_BE_OPPED", target->name);
2293 return 0;
2294 }
2295 else if(!IsSuspended(channel->channel_info))
2296 chanserv_join = 1;
2297
2298 if(off_channel > 0)
2299 {
2300 /* Clear MODE_REGISTERED from old channel, add it to new. */
2301 change.argc = 0;
2302 change.modes_clear = MODE_REGISTERED;
2303 mod_chanmode_announce(chanserv, channel, &change);
2304 change.modes_clear = 0;
2305 change.modes_set = MODE_REGISTERED;
2306 mod_chanmode_announce(chanserv, target, &change);
2307 }
2308
2309 /* Move the channel_info to the target channel; it
2310 shouldn't be necessary to clear timeq callbacks
2311 for the old channel. */
2312 target->channel_info = channel->channel_info;
2313 target->channel_info->channel = target;
2314 channel->channel_info = NULL;
2315
2316 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2317
2318 if (chanserv_join)
2319 ss_cs_join_channel(target, spamserv_join);
2320
2321 if(!IsSuspended(target->channel_info))
2322 {
2323 char reason2[MAXLEN];
2324 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2325 DelChannelUser(chanserv, channel, reason2, 0);
2326 }
2327
2328 UnlockChannel(channel);
2329 LockChannel(target);
2330 global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "CSMSG_CHANNEL_MOVED",
2331 channel->name, target->name, user->handle_info->handle);
2332
2333 reply("CSMSG_MOVE_SUCCESS", target->name);
2334 return 1;
2335 }
2336
2337 static void
2338 merge_users(struct chanData *source, struct chanData *target)
2339 {
2340 struct userData *suData, *tuData, *next;
2341 dict_iterator_t it;
2342 dict_t merge;
2343
2344 merge = dict_new();
2345
2346 /* Insert the source's users into the scratch area. */
2347 for(suData = source->users; suData; suData = suData->next)
2348 dict_insert(merge, suData->handle->handle, suData);
2349
2350 /* Iterate through the target's users, looking for
2351 users common to both channels. The lower access is
2352 removed from either the scratch area or target user
2353 list. */
2354 for(tuData = target->users; tuData; tuData = next)
2355 {
2356 struct userData *choice;
2357
2358 next = tuData->next;
2359
2360 /* If a source user exists with the same handle as a target
2361 channel's user, resolve the conflict by removing one. */
2362 suData = dict_find(merge, tuData->handle->handle, NULL);
2363 if(!suData)
2364 continue;
2365
2366 /* Pick the data we want to keep. */
2367 /* If the access is the same, use the later seen time. */
2368 if(suData->access == tuData->access)
2369 choice = (suData->seen > tuData->seen) ? suData : tuData;
2370 else /* Otherwise, keep the higher access level. */
2371 choice = (suData->access > tuData->access) ? suData : tuData;
2372
2373 /* Remove the user that wasn't picked. */
2374 if(choice == tuData)
2375 {
2376 dict_remove(merge, suData->handle->handle);
2377 del_channel_user(suData, 0);
2378 }
2379 else
2380 del_channel_user(tuData, 0);
2381 }
2382
2383 /* Move the remaining users to the target channel. */
2384 for(it = dict_first(merge); it; it = iter_next(it))
2385 {
2386 suData = iter_data(it);
2387
2388 /* Insert the user into the target channel's linked list. */
2389 suData->prev = NULL;
2390 suData->next = target->users;
2391 suData->channel = target;
2392
2393 if(target->users)
2394 target->users->prev = suData;
2395 target->users = suData;
2396
2397 /* Update the user counts for the target channel; the
2398 source counts are left alone. */
2399 target->userCount++;
2400 }
2401
2402 /* Possible to assert (source->users == NULL) here. */
2403 source->users = NULL;
2404 dict_delete(merge);
2405 }
2406
2407 static void
2408 merge_bans(struct chanData *source, struct chanData *target)
2409 {
2410 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2411
2412 /* Hold on to the original head of the target ban list
2413 to avoid comparing source bans with source bans. */
2414 tFront = target->bans;
2415
2416 /* Perform a totally expensive O(n*m) merge, ick. */
2417 for(sbData = source->bans; sbData; sbData = sNext)
2418 {
2419 /* Flag to track whether the ban's been moved
2420 to the destination yet. */
2421 int moved = 0;
2422
2423 /* Possible to assert (sbData->prev == NULL) here. */
2424 sNext = sbData->next;
2425
2426 for(tbData = tFront; tbData; tbData = tNext)
2427 {
2428 tNext = tbData->next;
2429
2430 /* Perform two comparisons between each source
2431 and target ban, conflicts are resolved by
2432 keeping the broader ban and copying the later
2433 expiration and triggered time. */
2434 if(match_ircglobs(tbData->mask, sbData->mask))
2435 {
2436 /* There is a broader ban in the target channel that
2437 overrides one in the source channel; remove the
2438 source ban and break. */
2439 if(sbData->expires > tbData->expires)
2440 tbData->expires = sbData->expires;
2441 if(sbData->triggered > tbData->triggered)
2442 tbData->triggered = sbData->triggered;
2443 del_channel_ban(sbData);
2444 break;
2445 }
2446 else if(match_ircglobs(sbData->mask, tbData->mask))
2447 {
2448 /* There is a broader ban in the source channel that
2449 overrides one in the target channel; remove the
2450 target ban, fall through and move the source over. */
2451 if(tbData->expires > sbData->expires)
2452 sbData->expires = tbData->expires;
2453 if(tbData->triggered > sbData->triggered)
2454 sbData->triggered = tbData->triggered;
2455 if(tbData == tFront)
2456 tFront = tNext;
2457 del_channel_ban(tbData);
2458 }
2459
2460 /* Source bans can override multiple target bans, so
2461 we allow a source to run through this loop multiple
2462 times, but we can only move it once. */
2463 if(moved)
2464 continue;
2465 moved = 1;
2466
2467 /* Remove the source ban from the source ban list. */
2468 if(sbData->next)
2469 sbData->next->prev = sbData->prev;
2470
2471 /* Modify the source ban's associated channel. */
2472 sbData->channel = target;
2473
2474 /* Insert the ban into the target channel's linked list. */
2475 sbData->prev = NULL;
2476 sbData->next = target->bans;
2477
2478 if(target->bans)
2479 target->bans->prev = sbData;
2480 target->bans = sbData;
2481
2482 /* Update the user counts for the target channel. */
2483 target->banCount++;
2484 }
2485 }
2486
2487 /* Possible to assert (source->bans == NULL) here. */
2488 source->bans = NULL;
2489 }
2490
2491 static void
2492 merge_data(struct chanData *source, struct chanData *target)
2493 {
2494 /* Use more recent visited and owner-transfer time; use older
2495 * registered time. Bitwise or may_opchan. Use higher max.
2496 * Do not touch last_refresh, ban count or user counts.
2497 */
2498 if(source->visited > target->visited)
2499 target->visited = source->visited;
2500 if(source->registered < target->registered)
2501 target->registered = source->registered;
2502 if(source->ownerTransfer > target->ownerTransfer)
2503 target->ownerTransfer = source->ownerTransfer;
2504 if(source->may_opchan)
2505 target->may_opchan = 1;
2506 if(source->max > target->max)
2507 target->max = source->max;
2508 }
2509
2510 static void
2511 merge_channel(struct chanData *source, struct chanData *target)
2512 {
2513 merge_users(source, target);
2514 merge_bans(source, target);
2515 merge_data(source, target);
2516 }
2517
2518 static CHANSERV_FUNC(cmd_merge)
2519 {
2520 struct userData *target_user;
2521 struct chanNode *target;
2522 char reason[MAXLEN];
2523
2524 REQUIRE_PARAMS(2);
2525
2526 /* Make sure the target channel exists and is registered to the user
2527 performing the command. */
2528 if(!(target = GetChannel(argv[1])))
2529 {
2530 reply("MSG_INVALID_CHANNEL");
2531 return 0;
2532 }
2533
2534 if(!target->channel_info)
2535 {
2536 reply("CSMSG_NOT_REGISTERED", target->name);
2537 return 0;
2538 }
2539
2540 if(IsProtected(channel->channel_info))
2541 {
2542 reply("CSMSG_MERGE_NODELETE");
2543 return 0;
2544 }
2545
2546 if(IsSuspended(target->channel_info))
2547 {
2548 reply("CSMSG_MERGE_SUSPENDED");
2549 return 0;
2550 }
2551
2552 if(channel == target)
2553 {
2554 reply("CSMSG_MERGE_SELF");
2555 return 0;
2556 }
2557
2558 target_user = GetChannelUser(target->channel_info, user->handle_info);
2559 if(!target_user || (target_user->access < UL_OWNER))
2560 {
2561 reply("CSMSG_MERGE_NOT_OWNER");
2562 return 0;
2563 }
2564
2565 /* Merge the channel structures and associated data. */
2566 merge_channel(channel->channel_info, target->channel_info);
2567 spamserv_cs_move_merge(user, channel, target, 0);
2568 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2569 unregister_channel(channel->channel_info, reason);
2570 reply("CSMSG_MERGE_SUCCESS", target->name);
2571 return 1;
2572 }
2573
2574 static CHANSERV_FUNC(cmd_opchan)
2575 {
2576 struct mod_chanmode change;
2577 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2578 {
2579 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2580 return 0;
2581 }
2582 channel->channel_info->may_opchan = 0;
2583 mod_chanmode_init(&change);
2584 change.argc = 1;
2585 change.args[0].mode = MODE_CHANOP;
2586 change.args[0].u.member = GetUserMode(channel, chanserv);
2587 mod_chanmode_announce(chanserv, channel, &change);
2588 reply("CSMSG_OPCHAN_DONE", channel->name);
2589 return 1;
2590 }
2591
2592 static CHANSERV_FUNC(cmd_adduser)
2593 {
2594 struct userData *actee;
2595 struct userData *actor;
2596 struct handle_info *handle;
2597 unsigned short access;
2598
2599 REQUIRE_PARAMS(3);
2600
2601 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2602 {
2603 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2604 return 0;
2605 }
2606
2607 access = user_level_from_name(argv[2], UL_OWNER);
2608 if(!access)
2609 {
2610 reply("CSMSG_INVALID_ACCESS", argv[2]);
2611 return 0;
2612 }
2613
2614 actor = GetChannelUser(channel->channel_info, user->handle_info);
2615 if(actor->access <= access)
2616 {
2617 reply("CSMSG_NO_BUMP_ACCESS");
2618 return 0;
2619 }
2620
2621 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2622 {
2623 // 'kevin must first authenticate with AuthServ.' is sent to user
2624 struct userNode *unode;
2625 unode = GetUserH(argv[1]); /* find user struct by nick */
2626 if(unode)
2627 {
2628 if(find_adduser_pending(channel, unode)) {
2629 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel->name);
2630 }
2631 else {
2632 if(IsInChannel(channel, unode)) {
2633 reply("CSMSG_ADDUSER_PENDING");
2634 add_adduser_pending(channel, unode, access);
2635 send_message_type(1,unode, chanserv, "CSMSG_ADDUSER_PENDING_TARGET", user->nick, channel->name);
2636 }
2637 /* this results in user must auth AND not in chan errors. too confusing..
2638 else {
2639 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2640 }
2641 */
2642 }
2643 }
2644 return 0;
2645 }
2646
2647 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2648 {
2649 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, user_level_name_from_level(actee->access));
2650 return 0;
2651 }
2652
2653 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2654 scan_user_presence(actee, NULL);
2655 reply("CSMSG_ADDED_USER", handle->handle, channel->name, user_level_name_from_level(access), access);
2656 return 1;
2657 }
2658
2659 static CHANSERV_FUNC(cmd_clvl)
2660 {
2661 struct handle_info *handle;
2662 struct userData *victim;
2663 struct userData *actor;
2664 unsigned short new_access;
2665 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2666
2667 REQUIRE_PARAMS(3);
2668
2669 actor = GetChannelUser(channel->channel_info, user->handle_info);
2670
2671 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2672 return 0;
2673
2674 if(handle == user->handle_info && !privileged)
2675 {
2676 reply("CSMSG_NO_SELF_CLVL");
2677 return 0;
2678 }
2679
2680 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2681 {
2682 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2683 return 0;
2684 }
2685
2686 if(actor->access <= victim->access && !privileged)
2687 {
2688 reply("MSG_USER_OUTRANKED", handle->handle);
2689 return 0;
2690 }
2691
2692 new_access = user_level_from_name(argv[2], UL_OWNER);
2693
2694 if(!new_access)
2695 {
2696 reply("CSMSG_INVALID_ACCESS", argv[2]);
2697 return 0;
2698 }
2699
2700 if(new_access >= actor->access && !privileged)
2701 {
2702 reply("CSMSG_NO_BUMP_ACCESS");
2703 return 0;
2704 }
2705
2706 victim->access = new_access;
2707 reply("CSMSG_CHANGED_ACCESS", handle->handle, user_level_name_from_level(new_access), new_access, channel->name);
2708 return 1;
2709 }
2710
2711 static CHANSERV_FUNC(cmd_deluser)
2712 {
2713 struct handle_info *handle;
2714 struct userData *victim;
2715 struct userData *actor;
2716 unsigned short access;
2717 char *chan_name;
2718
2719 REQUIRE_PARAMS(2);
2720
2721 actor = GetChannelUser(channel->channel_info, user->handle_info);
2722
2723 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2724 return 0;
2725
2726 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2727 {
2728 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2729 return 0;
2730 }
2731
2732 if(argc > 2)
2733 {
2734 access = user_level_from_name(argv[1], UL_OWNER);
2735 if(!access)
2736 {
2737 reply("CSMSG_INVALID_ACCESS", argv[1]);
2738 return 0;
2739 }
2740 if(access != victim->access)
2741 {
2742 reply("CSMSG_INCORRECT_ACCESS", handle->handle, user_level_name_from_level(victim->access), argv[1]);
2743 return 0;
2744 }
2745 }
2746 else
2747 {
2748 access = victim->access;
2749 }
2750
2751 if((actor->access <= victim->access) && !IsHelping(user))
2752 {
2753 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2754 return 0;
2755 }
2756
2757 chan_name = strdup(channel->name);
2758 del_channel_user(victim, 1);
2759 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2760 free(chan_name);
2761 return 1;
2762 }
2763
2764 static int
2765 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2766 {
2767 struct userData *actor, *uData, *next;
2768
2769 actor = GetChannelUser(channel->channel_info, user->handle_info);
2770
2771 if(min_access > max_access)
2772 {
2773 reply("CSMSG_BAD_RANGE", min_access, max_access);
2774 return 0;
2775 }
2776
2777 if((actor->access <= max_access) && !IsHelping(user))
2778 {
2779 reply("CSMSG_NO_ACCESS");
2780 return 0;
2781 }
2782
2783 for(uData = channel->channel_info->users; uData; uData = next)
2784 {
2785 next = uData->next;
2786
2787 if((uData->access >= min_access)
2788 && (uData->access <= max_access)
2789 && match_ircglob(uData->handle->handle, mask))
2790 del_channel_user(uData, 1);
2791 }
2792
2793 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2794 return 1;
2795 }
2796
2797 static CHANSERV_FUNC(cmd_mdelowner)
2798 {
2799 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2800 }
2801
2802 static CHANSERV_FUNC(cmd_mdelcoowner)
2803 {
2804 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2805 }
2806
2807 static CHANSERV_FUNC(cmd_mdelmanager)
2808 {
2809 return cmd_mdel_user(user, channel, UL_MANAGER, UL_MANAGER, argv[1], cmd);
2810 }
2811
2812 static CHANSERV_FUNC(cmd_mdelop)
2813 {
2814 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2815 }
2816
2817 static CHANSERV_FUNC(cmd_mdelpeon)
2818 {
2819 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2820 }
2821
2822 static CHANSERV_FUNC(cmd_mdelhalfop)
2823 {
2824 return cmd_mdel_user(user, channel, UL_HALFOP, UL_HALFOP, argv[1], cmd);
2825 }
2826
2827
2828 /* trim_lamers.. */
2829 static int
2830 cmd_trim_bans(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, unsigned long duration)
2831 {
2832 struct banData *bData, *next;
2833 char interval[INTERVALLEN];
2834 unsigned int count;
2835 time_t limit;
2836
2837 count = 0;
2838 limit = now - duration;
2839 for(bData = channel->channel_info->bans; bData; bData = next)
2840 {
2841 next = bData->next;
2842
2843 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2844 continue;
2845
2846 del_channel_ban(bData);
2847 count++;
2848 }
2849
2850 intervalString(interval, duration, user->handle_info);
2851 reply("CSMSG_TRIMMED_LAMERS", count, channel->name, interval);
2852 return 1;
2853 }
2854
2855 static int
2856 cmd_trim_users(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2857 {
2858 struct userData *actor, *uData, *next;
2859 char interval[INTERVALLEN];
2860 unsigned int count;
2861 time_t limit;
2862
2863 actor = GetChannelUser(channel->channel_info, user->handle_info);
2864 if(min_access > max_access)
2865 {
2866 reply("CSMSG_BAD_RANGE", min_access, max_access);
2867 return 0;
2868 }
2869
2870 if((actor->access <= max_access) && !IsHelping(user))
2871 {
2872 reply("CSMSG_NO_ACCESS");
2873 return 0;
2874 }
2875
2876 count = 0;
2877 limit = now - duration;
2878 for(uData = channel->channel_info->users; uData; uData = next)
2879 {
2880 next = uData->next;
2881
2882 if((uData->seen > limit)
2883 || uData->present
2884 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2885 continue;
2886
2887 if(((uData->access >= min_access) && (uData->access <= max_access))
2888 || (!max_access && (uData->access < actor->access)))
2889 {
2890 del_channel_user(uData, 1);
2891 count++;
2892 }
2893 }
2894
2895 if(!max_access)
2896 {
2897 min_access = 1;
2898 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2899 }
2900 reply("CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2901 return 1;
2902 }
2903
2904 static CHANSERV_FUNC(cmd_trim)
2905 {
2906 unsigned long duration;
2907 unsigned short min_level, max_level;
2908 int vacation;
2909
2910 REQUIRE_PARAMS(3);
2911
2912 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2913 duration = ParseInterval(argv[2]);
2914 if(duration < 60)
2915 {
2916 reply("CSMSG_CANNOT_TRIM");
2917 return 0;
2918 }
2919
2920 if(!irccasecmp(argv[1], "lamers"))
2921 {
2922 cmd_trim_bans(cmd, user, channel, duration); /* trim_lamers.. */
2923 return 1;
2924 }
2925 else if(!irccasecmp(argv[1], "users"))
2926 {
2927 cmd_trim_users(cmd, user, channel, 0, 0, duration, vacation);
2928 return 1;
2929 }
2930 else if(parse_level_range(&min_level, &max_level, argv[1]))
2931 {
2932 cmd_trim_users(cmd, user, channel, min_level, max_level, duration, vacation);
2933 return 1;
2934 }
2935 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2936 {
2937 cmd_trim_users(cmd, user, channel, min_level, min_level, duration, vacation);
2938 return 1;
2939 }
2940 else
2941 {
2942 reply("CSMSG_INVALID_TRIM", argv[1]);
2943 return 0;
2944 }
2945 }
2946
2947 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2948 to the user. cmd_all takes advantage of this. */
2949 static CHANSERV_FUNC(cmd_up)
2950 {
2951 struct mod_chanmode change;
2952 struct userData *uData;
2953 const char *errmsg;
2954
2955 mod_chanmode_init(&change);
2956 change.argc = 1;
2957 change.args[0].u.member = GetUserMode(channel, user);
2958 if(!change.args[0].u.member)
2959 {
2960 if(argc)
2961 reply("MSG_CHANNEL_ABSENT", channel->name);
2962 return 0;
2963 }
2964
2965 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2966 if(!uData)
2967 {
2968 if(argc)
2969 reply("CSMSG_GODMODE_UP", argv[0]);
2970 return 0;
2971 }
2972 else if(uData->access >= UL_OP)
2973 {
2974 change.args[0].mode = MODE_CHANOP;
2975 errmsg = "CSMSG_ALREADY_OPPED";
2976 }
2977 else if(uData->access >= UL_HALFOP )
2978 {
2979 change.args[0].mode = MODE_HALFOP;
2980 errmsg = "CSMSG_ALREADY_HALFOPPED";
2981 }
2982 else if(uData->access >= UL_PEON && (channel->channel_info->chOpts[chAutomode] != 'm' ))
2983 {
2984 change.args[0].mode = MODE_VOICE;
2985 errmsg = "CSMSG_ALREADY_VOICED";
2986 }
2987 else
2988 {
2989 if(argc)
2990 reply("CSMSG_NO_ACCESS");
2991 return 0;
2992 }
2993 change.args[0].mode &= ~change.args[0].u.member->modes;
2994 if(!change.args[0].mode)
2995 {
2996 if(argc)
2997 reply(errmsg, channel->name);
2998 return 0;
2999 }
3000 modcmd_chanmode_announce(&change);
3001 return 1;
3002 }
3003
3004 static CHANSERV_FUNC(cmd_down)
3005 {
3006 struct mod_chanmode change;
3007
3008 mod_chanmode_init(&change);
3009 change.argc = 1;
3010 change.args[0].u.member = GetUserMode(channel, user);
3011 if(!change.args[0].u.member)
3012 {
3013 if(argc)
3014 reply("MSG_CHANNEL_ABSENT", channel->name);
3015 return 0;
3016 }
3017
3018 if(!change.args[0].u.member->modes)
3019 {
3020 if(argc)
3021 reply("CSMSG_ALREADY_DOWN", channel->name);
3022 return 0;
3023 }
3024
3025 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3026 modcmd_chanmode_announce(&change);
3027 return 1;
3028 }
3029
3030 static int cmd_all(struct userNode *user, UNUSED_ARG(struct chanNode *channel), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char *argv[]), struct svccmd *cmd, modcmd_func_t mcmd)
3031 {
3032 struct userData *cList;
3033
3034 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3035 {
3036 if(IsSuspended(cList->channel)
3037 || IsUserSuspended(cList)
3038 || !GetUserMode(cList->channel->channel, user))
3039 continue;
3040
3041 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3042 }
3043
3044 return 1;
3045 }
3046
3047 static CHANSERV_FUNC(cmd_upall)
3048 {
3049 return cmd_all(CSFUNC_ARGS, cmd_up);
3050 }
3051
3052 static CHANSERV_FUNC(cmd_downall)
3053 {
3054 return cmd_all(CSFUNC_ARGS, cmd_down);
3055 }
3056
3057 typedef int validate_func_t(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, struct userNode *victim);
3058 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3059
3060 static int
3061 modify_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, validate_func_t validate, chan_mode_t mode, char *action)
3062 {
3063 unsigned int ii, valid;
3064 struct userNode *victim;
3065 struct mod_chanmode *change;
3066
3067 change = mod_chanmode_alloc(argc - 1);
3068
3069 for(ii=valid=0; ++ii < argc; )
3070 {
3071 if(!(victim = GetUserH(argv[ii])))
3072 continue;
3073 change->args[valid].mode = mode;
3074 change->args[valid].u.member = GetUserMode(channel, victim);
3075 if(!change->args[valid].u.member)
3076 continue;
3077 if(validate && !validate(cmd, user, channel, victim))
3078 continue;
3079 valid++;
3080 }
3081
3082 change->argc = valid;
3083 if(valid < (argc-1))
3084 reply("CSMSG_PROCESS_FAILED");
3085 if(valid)
3086 {
3087 modcmd_chanmode_announce(change);
3088 reply(action, channel->name);
3089 }
3090 mod_chanmode_free(change);
3091 return 1;
3092 }
3093
3094 static CHANSERV_FUNC(cmd_op)
3095 {
3096 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3097 }
3098
3099 static CHANSERV_FUNC(cmd_hop)
3100 {
3101 return modify_users(CSFUNC_ARGS, validate_halfop, MODE_HALFOP, "CSMSG_HALFOPPED_USERS");
3102 }
3103
3104 static CHANSERV_FUNC(cmd_deop)
3105 {
3106 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3107 }
3108
3109 static CHANSERV_FUNC(cmd_dehop)
3110 {
3111 return modify_users(CSFUNC_ARGS, validate_dehop, MODE_REMOVE|MODE_HALFOP, "CSMSG_DEHALFOPPED_USERS");
3112 }
3113
3114 static CHANSERV_FUNC(cmd_voice)
3115 {
3116 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3117 }
3118
3119 static CHANSERV_FUNC(cmd_devoice)
3120 {
3121 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3122 }
3123
3124 static int
3125 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3126 {
3127 unsigned int ii;
3128
3129 if(victimCount)
3130 *victimCount = 0;
3131 for(ii=0; ii<channel->members.used; ii++)
3132 {
3133 struct modeNode *mn = channel->members.list[ii];
3134
3135 if(IsService(mn->user))
3136 continue;
3137
3138 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3139 continue;
3140
3141 if(protect_user(mn->user, user, channel->channel_info, false))
3142 return 1;
3143
3144 if(victims)
3145 victims[(*victimCount)++] = mn;
3146 }
3147 return 0;
3148 }
3149
3150 static int
3151 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3152 {
3153 struct userNode *victim;
3154 struct modeNode **victims;
3155 unsigned int offset, n, victimCount, duration = 0;
3156 char *reason = "Bye.", *ban, *name;
3157 char interval[INTERVALLEN];
3158
3159 offset = (action & ACTION_ADD_TIMED_LAMER) ? 3 : 2;
3160 REQUIRE_PARAMS(offset);
3161 if(argc > offset)
3162 {
3163 reason = unsplit_string(argv + offset, argc - offset, NULL);
3164 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3165 {
3166 /* Truncate the reason to a length of TOPICLEN, as
3167 the ircd does; however, leave room for an ellipsis
3168 and the kicker's nick. */
3169 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3170 }
3171 }
3172
3173 if((victim = GetUserH(argv[1])))
3174 {
3175 victims = alloca(sizeof(victims[0]));
3176 victims[0] = GetUserMode(channel, victim);
3177 /* XXX: The comparison with ACTION_KICK is just because all
3178 * other actions can work on users outside the channel, and we
3179 * want to allow those (e.g. unbans) in that case. If we add
3180 * some other ejection action for in-channel users, change
3181 * this too. */
3182 victimCount = victims[0] ? 1 : 0;
3183
3184 if(IsService(victim))
3185 {
3186 if(cmd)
3187 reply("MSG_SERVICE_IMMUNE", victim->nick);
3188 return 0;
3189 }
3190
3191 if((action == ACTION_KICK) && !victimCount)
3192 {
3193 if(cmd)
3194 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3195 return 0;
3196 }
3197
3198 if(protect_user(victim, user, channel->channel_info, false))
3199 {
3200 // This translates to send_message(user, cmd->parent->bot, ...)
3201 // if user is x3 (ctcp action) cmd is null and segfault.
3202 if(cmd)
3203 reply("CSMSG_USER_PROTECTED", victim->nick);
3204 return 0;
3205 }
3206
3207 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3208 name = victim->nick;
3209 }
3210 else
3211 {
3212 if(!is_ircmask(argv[1]))
3213 {
3214 if(cmd)
3215 reply("MSG_NICK_UNKNOWN", argv[1]);
3216 return 0;
3217 }
3218
3219 victims = alloca(sizeof(victims[0]) * channel->members.used);
3220
3221 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3222 {
3223 if(cmd)
3224 reply("CSMSG_MASK_PROTECTED", argv[1]);
3225 return 0;
3226 }
3227 /* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3228 // if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3229 And, ^^^^^^^^^ BAH!
3230 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3231 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3232 some creativity, but its not x3's job to be the ban censor anyway. */
3233 if(is_overmask(argv[1]))
3234 {
3235 if(cmd)
3236 reply("CSMSG_LAME_MASK", argv[1]);
3237 return 0;
3238 }
3239
3240 if((action == ACTION_KICK) && (victimCount == 0))
3241 {
3242 if(cmd)
3243 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3244 return 0;
3245 }
3246
3247 name = ban = strdup(argv[1]);
3248 }
3249
3250 /* Truncate the ban in place if necessary; we must ensure
3251 that 'ban' is a valid ban mask before sanitizing it. */
3252 sanitize_ircmask(ban);
3253
3254 if(action & ACTION_ADD_LAMER)
3255 {
3256 struct banData *bData, *next;
3257
3258 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans) /* ..lamers.. */
3259 {
3260 if(cmd)
3261 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf.max_chan_bans); /* ..lamers.. */
3262 free(ban);
3263 return 0;
3264 }
3265
3266 if(action & ACTION_ADD_TIMED_LAMER)
3267 {
3268 duration = ParseInterval(argv[2]);
3269
3270 if(duration < 15)
3271 {
3272 if(cmd)
3273 reply("CSMSG_DURATION_TOO_LOW");
3274 free(ban);
3275 return 0;
3276 }
3277 else if(duration > (86400 * 365 * 2))
3278 {
3279 if(cmd)
3280 reply("CSMSG_DURATION_TOO_HIGH");
3281 free(ban);
3282 return 0;
3283 }
3284 }
3285
3286 /* lamers... */
3287 for(bData = channel->channel_info->bans; bData; bData = next)
3288 {
3289 if(match_ircglobs(bData->mask, ban))
3290 {
3291 int exact = !irccasecmp(bData->mask, ban);
3292
3293 /* The ban is redundant; there is already a ban
3294 with the same effect in place. */
3295 if(exact)
3296 {
3297 if(bData->reason)
3298 free(bData->reason);
3299 bData->reason = strdup(reason);
3300 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3301 if(cmd)
3302 reply("CSMSG_REASON_CHANGE", ban);
3303 if(!bData->expires)
3304 goto post_add_ban;
3305 }
3306 if(exact && bData->expires)
3307 {
3308 int reset = 0;
3309
3310 /* If the ban matches an existing one exactly,
3311 extend the expiration time if the provided
3312 duration is longer. */
3313 if(duration && ((time_t)(now + duration) > bData->expires))
3314 {
3315 bData->expires = now + duration;
3316 reset = 1;
3317 }
3318 else if(!duration)
3319 {
3320 bData->expires = 0;
3321 reset = 1;
3322 }
3323
3324 if(reset)
3325 {
3326 /* Delete the expiration timeq entry and
3327 requeue if necessary. */
3328 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3329
3330 if(bData->expires)
3331 timeq_add(bData->expires, expire_ban, bData);
3332
3333 if(!cmd)
3334 {
3335 /* automated kickban, dont reply */
3336 }
3337 else if(duration)
3338 reply("CSMSG_LAMER_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3339 else
3340 reply("CSMSG_LAMER_ADDED", name, channel->name);
3341
3342 goto post_add_ban;
3343 }
3344 }
3345 if(cmd)
3346 reply("CSMSG_REDUNDANT_LAMER", name, channel->name);
3347
3348 free(ban);
3349 return 0;
3350 }
3351
3352 next = bData->next;
3353 if(match_ircglobs(ban, bData->mask))
3354 {
3355 /* The ban we are adding makes previously existing
3356 bans redundant; silently remove them. */
3357 del_channel_ban(bData);
3358 }
3359 }
3360
3361 bData = add_channel_ban(channel->channel_info, ban, (user->handle_info ? user->handle_info->handle : user->nick), now, (victimCount ? now : 0), (duration ? now + duration : 0), reason);
3362 free(ban);
3363 name = ban = strdup(bData->mask);
3364 }
3365 else if(ban)
3366 {
3367 /* WHAT DOES THIS DO?? -Rubin */
3368 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3369 {
3370 extern const char *hidden_host_suffix;
3371 const char *old_name = chanserv_conf.old_ban_names->list[n];
3372 char *new_mask;
3373 unsigned int l1, l2;
3374
3375 l1 = strlen(ban);
3376 l2 = strlen(old_name);
3377 if(l2+2 > l1)
3378 continue;
3379 if(irccasecmp(ban + l1 - l2, old_name))
3380 continue;
3381 new_mask = malloc(MAXLEN);
3382 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3383 free(ban);
3384 name = ban = new_mask;
3385 }
3386 }
3387
3388 post_add_ban:
3389 if(action & ACTION_BAN)
3390 {
3391 unsigned int exists;
3392 struct mod_chanmode *change;
3393
3394 if(channel->banlist.used >= MAXBANS)
3395 {
3396 if(cmd)
3397 reply("CSMSG_BANLIST_FULL", channel->name);
3398 free(ban);
3399 return 0;
3400 }
3401
3402 exists = ChannelBanExists(channel, ban);
3403 change = mod_chanmode_alloc(victimCount + 1);
3404 for(n = 0; n < victimCount; ++n)
3405 {
3406 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_HALFOP|MODE_VOICE;
3407 change->args[n].u.member = victims[n];
3408 }
3409 if(!exists)
3410 {
3411 change->args[n].mode = MODE_BAN;
3412 change->args[n++].u.hostmask = ban;
3413 }
3414 change->argc = n;
3415 if(cmd)
3416 modcmd_chanmode_announce(change);
3417 else
3418 mod_chanmode_announce(chanserv, channel, change);
3419 mod_chanmode_free(change);
3420
3421 if(exists && (action == ACTION_BAN))
3422 {
3423 if(cmd)
3424 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3425 free(ban);
3426 return 0;
3427 }
3428 }
3429
3430 if(action & ACTION_ADD_LAMER)
3431 {
3432 char kick_reason[MAXLEN];
3433 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3434
3435 for(n = 0; n < victimCount; n++) {
3436 if(!protect_user(victims[n]->user, user, channel->channel_info, true)) {
3437 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3438 }
3439 }
3440 }
3441 else if(action & ACTION_KICK)
3442 {
3443 char kick_reason[MAXLEN];
3444 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3445
3446 for(n = 0; n < victimCount; n++) {
3447 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3448 }
3449 }
3450
3451 if(!cmd)
3452 {
3453 /* No response, since it was automated. */
3454 }
3455 else if(action & ACTION_ADD_LAMER)
3456 {
3457 if(duration)
3458 reply("CSMSG_TIMED_LAMER_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3459 else
3460 reply("CSMSG_LAMER_ADDED", name, channel->name);
3461 }
3462 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3463 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3464 else if(action & ACTION_BAN)
3465 reply("CSMSG_BAN_DONE", name, channel->name);
3466 else if(action & ACTION_KICK && victimCount)
3467 reply("CSMSG_KICK_DONE", name, channel->name);
3468
3469 free(ban);
3470 return 1;
3471 }
3472
3473 static CHANSERV_FUNC(cmd_kickban)
3474 {
3475 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3476 }
3477
3478 static CHANSERV_FUNC(cmd_kick)
3479 {
3480 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3481 }
3482
3483 static CHANSERV_FUNC(cmd_ban)
3484 {
3485 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3486 }
3487
3488 static CHANSERV_FUNC(cmd_addlamer)
3489 {
3490 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_LAMER);
3491 }
3492
3493 static CHANSERV_FUNC(cmd_addtimedlamer)
3494 {
3495 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_LAMER | ACTION_ADD_TIMED_LAMER);
3496 }
3497
3498 static struct mod_chanmode *
3499 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3500 {
3501 struct mod_chanmode *change;
3502 unsigned char *match;
3503 unsigned int ii, count;
3504
3505 match = alloca(bans->used);
3506 if(actee)
3507 {
3508 for(ii = count = 0; ii < bans->used; ++ii)
3509 {
3510 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3511 MATCH_USENICK | MATCH_VISIBLE);
3512 if(match[ii])
3513 count++;
3514 }
3515 }
3516 else
3517 {
3518 for(ii = count = 0; ii < bans->used; ++ii)
3519 {
3520 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3521 if(match[ii])
3522 count++;
3523 }
3524 }
3525 if(!count)
3526 return NULL;
3527 change = mod_chanmode_alloc(count);
3528 for(ii = count = 0; ii < bans->used; ++ii)
3529 {
3530 if(!match[ii])
3531 continue;
3532 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3533 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3534 }
3535 assert(count == change->argc);
3536 return change;
3537 }
3538
3539 void expire_bans(UNUSED_ARG(void* data)) /* Real bans, not lamers */
3540 {
3541 unsigned int jj, ii, count;
3542 struct banNode *bn;
3543 struct chanData *channel;
3544 time_t bantimeout;
3545 struct mod_chanmode *change;
3546
3547 log_module(CS_LOG, LOG_DEBUG, "Checking for expired bans");
3548 /* Walk through every channel */
3549 for(channel = channelList; channel; channel = channel->next) {
3550 switch(channel->chOpts[chBanTimeout])
3551 {
3552 default: case '0': continue; /* Dont remove bans in this chan */
3553 case '1': bantimeout = now - (10 * 60); break; /* 10 minutes */
3554 case '2': bantimeout = now - (2 * 60 * 60); break; /* 2 hours */
3555 case '3': bantimeout = now - (4 * 60 * 60); break; /* 4 hours */
3556 case '4': bantimeout = now - (24 * 60 * 60); break; /* 24 hours */
3557 case '5': bantimeout = now - (7 * 24 * 60 * 60); break; /* 1 week */
3558 }
3559 count = 0;
3560 /* First find out how many bans were going to unset */
3561 for (jj=0; jj < channel->channel->banlist.used; ++jj) {
3562 if(channel->channel->banlist.list[jj]->set < bantimeout)
3563 count++;
3564 }
3565 if(count > 0) {
3566 /* At least one ban, so setup a removal */
3567 change = mod_chanmode_alloc(count);
3568 ii = 0;
3569 /* Walk over every ban in this channel.. */
3570 for (jj=0; jj < channel->channel->banlist.used; ++jj) {
3571 bn = channel->channel->banlist.list[jj];
3572 if (bn->set < bantimeout) {
3573 log_module(CS_LOG, LOG_DEBUG, "Removing ban %s from %s", bn->ban, channel->channel->name);
3574
3575 /* Add this ban to the mode change */
3576 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3577 change->args[ii].u.hostmask = strdup(bn->ban);
3578 ii++;
3579 /* Pull this ban out of the list */
3580 banList_remove(&(channel->channel->banlist), bn);
3581 jj--;
3582 free(bn);
3583 }
3584 }
3585 /* Send the modes to IRC */
3586 mod_chanmode_announce(chanserv, channel->channel, change);
3587
3588 /* free memory from strdup above */
3589 for(ii = 0; ii < count; ++ii)
3590 free((char*)change->args[ii].u.hostmask);
3591
3592 mod_chanmode_free(change);
3593 }
3594 }
3595 /* Set this function to run again */
3596 if(chanserv_conf.ban_timeout_frequency)
3597 timeq_add(now + chanserv_conf.ban_timeout_frequency, expire_bans, NULL);
3598 }
3599
3600
3601 static int
3602 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3603 {
3604 struct userNode *actee;
3605 char *mask = NULL;
3606 int acted = 0;
3607
3608 REQUIRE_PARAMS(2);
3609
3610 /* may want to allow a comma delimited list of users... */
3611 if(!(actee = GetUserH(argv[1])))
3612 {
3613 if(!is_ircmask(argv[1]))
3614 {
3615 reply("MSG_NICK_UNKNOWN", argv[1]);
3616 return 0;
3617 }
3618
3619 mask = strdup(argv[1]);
3620 }
3621
3622 /* We don't sanitize the mask here because ircu
3623 doesn't do it. */
3624 if(action & ACTION_UNBAN)
3625 {
3626 struct mod_chanmode *change;
3627 change = find_matching_bans(&channel->banlist, actee, mask);
3628 if(change)
3629 {
3630 unsigned int ii;
3631
3632 modcmd_chanmode_announce(change);
3633 for(ii = 0; ii < change->argc; ++ii)
3634 free((char*)change->args[ii].u.hostmask);
3635 mod_chanmode_free(change);
3636 acted = 1;
3637 }
3638 }
3639
3640 if(action & ACTION_DEL_LAMER)
3641 {
3642 struct banData *ban, *next;
3643
3644 ban = channel->channel_info->bans; /* lamers */
3645 while(ban)
3646 {
3647 if(actee)
3648 for( ; ban && !user_matches_glob(actee, ban->mask, MATCH_USENICK | MATCH_VISIBLE);
3649 ban = ban->next);
3650 else
3651 for( ; ban && !match_ircglobs(mask, ban->mask);
3652 ban = ban->next);
3653 if(!ban)
3654 break;
3655 next = ban->next;
3656 del_channel_ban(ban);
3657 ban = next;
3658 acted = 1;
3659 }
3660 }
3661
3662 if(!acted)
3663 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3664 else
3665 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3666 if(mask)
3667 free(mask);
3668 return 1;
3669 }
3670
3671 static CHANSERV_FUNC(cmd_unban)
3672 {
3673 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3674 }
3675
3676 static CHANSERV_FUNC(cmd_dellamer)
3677 {
3678 /* it doesn't necessarily have to remove the channel ban - may want
3679 to make that an option. */
3680 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_LAMER);
3681 }
3682
3683 static CHANSERV_FUNC(cmd_unbanme)
3684 {
3685 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3686 long flags = ACTION_UNBAN;
3687
3688 /* remove permanent bans if the user has the proper access. */
3689 if(uData->access >= UL_MANAGER)
3690 flags |= ACTION_DEL_LAMER;
3691
3692 argv[1] = user->nick;
3693 return unban_user(user, channel, 2, argv, cmd, flags);
3694 }
3695
3696 static CHANSERV_FUNC(cmd_unbanall)
3697 {
3698 struct mod_chanmode *change;
3699 unsigned int ii;
3700
3701 if(!channel->banlist.used)
3702 {
3703 reply("CSMSG_NO_BANS", channel->name);
3704 return 0;
3705 }
3706
3707 change = mod_chanmode_alloc(channel->banlist.used);
3708 for(ii=0; ii<channel->banlist.used; ii++)
3709 {
3710 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3711 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3712 }
3713 modcmd_chanmode_announce(change);
3714 for(ii = 0; ii < change->argc; ++ii)
3715 free((char*)change->args[ii].u.hostmask);
3716 mod_chanmode_free(change);
3717 reply("CSMSG_BANS_REMOVED", channel->name);
3718 return 1;
3719 }
3720
3721 static CHANSERV_FUNC(cmd_open)
3722 {
3723 struct mod_chanmode *change;
3724 unsigned int ii;
3725
3726 change = find_matching_bans(&channel->banlist, user, NULL);
3727 if(!change)
3728 change = mod_chanmode_alloc(0);
3729 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3730 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3731 && channel->channel_info->modes.modes_set)
3732 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3733 modcmd_chanmode_announce(change);
3734 reply("CSMSG_CHANNEL_OPENED", channel->name);
3735 for(ii = 0; ii < change->argc; ++ii)
3736 free((char*)change->args[ii].u.hostmask);
3737 mod_chanmode_free(change);
3738 return 1;
3739 }
3740
3741 static CHANSERV_FUNC(cmd_myaccess)
3742 {
3743 static struct string_buffer sbuf;
3744 struct handle_info *target_handle;
3745 struct userData *uData;
3746
3747 if(argc < 2)
3748 target_handle = user->handle_info;
3749 else if(!IsHelping(user))
3750 {
3751 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3752 return 0;
3753 }
3754 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3755 return 0;
3756
3757 if(!target_handle->channels)
3758 {
3759 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3760 return 1;
3761 }
3762
3763 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3764 for(uData = target_handle->channels; uData; uData = uData->u_next)
3765 {
3766 struct chanData *cData = uData->channel;
3767
3768 if(uData->access > UL_OWNER)
3769 continue;
3770 if(IsProtected(cData)
3771 && (target_handle != user->handle_info)
3772 && !GetTrueChannelAccess(cData, user->handle_info))
3773 continue;
3774 sbuf.used = 0;
3775 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3776 if(uData->flags == USER_AUTO_OP)
3777 string_buffer_append(&sbuf, ',');
3778 if(IsUserSuspended(uData))
3779 string_buffer_append(&sbuf, 's');
3780 if(IsUserAutoOp(uData))
3781 {
3782 if(uData->access >= UL_OP )
3783 string_buffer_append(&sbuf, 'o');
3784 else if(uData->access >= UL_HALFOP )
3785 string_buffer_append(&sbuf, 'h');
3786 else if(uData->access >= UL_PEON )
3787 string_buffer_append(&sbuf, 'v');
3788 }
3789 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3790 string_buffer_append(&sbuf, 'i');
3791 if(IsUserAutoJoin(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3792 string_buffer_append(&sbuf, 'j');
3793 if(uData->info)
3794 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3795 else
3796 string_buffer_append_string(&sbuf, ")]");
3797 string_buffer_append(&sbuf, '\0');
3798 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3799 }
3800
3801 return 1;
3802 }
3803
3804 static CHANSERV_FUNC(cmd_access)
3805 {
3806 struct userNode *target;
3807 struct handle_info *target_handle;
3808 struct userData *uData;
3809 int helping;
3810 char prefix[MAXLEN];
3811
3812 if(argc < 2)
3813 {
3814 target = user;
3815 target_handle = target->handle_info;
3816 }
3817 else if((target = GetUserH(argv[1])))
3818 {
3819 target_handle = target->handle_info;
3820 }
3821 else if(argv[1][0] == '*')
3822 {
3823 if(!(target_handle = get_handle_info(argv[1]+1)))
3824 {
3825 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3826 return 0;
3827 }
3828 }
3829 else
3830 {
3831 reply("MSG_NICK_UNKNOWN", argv[1]);
3832 return 0;
3833 }
3834
3835 assert(target || target_handle);
3836
3837 if(target == chanserv)
3838 {
3839 reply("CSMSG_IS_CHANSERV");
3840 return 1;
3841 }
3842
3843 if(!target_handle)
3844 {
3845 if(IsOper(target))
3846 {
3847 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3848 return 0;
3849 }
3850 if(target != user)
3851 {
3852 reply("MSG_USER_AUTHENTICATE", target->nick);
3853 return 0;
3854 }
3855 reply("MSG_AUTHENTICATE");
3856 return 0;
3857 }
3858
3859 if(target)
3860 {
3861 const char *epithet = NULL, *type = NULL;
3862 if(IsOper(target))
3863 {
3864 epithet = chanserv_conf.irc_operator_epithet;
3865 type = "IRCOp";
3866 }
3867 else if(IsNetworkHelper(target))
3868 {
3869 epithet = chanserv_conf.network_helper_epithet;
3870 type = "network helper";
3871 }
3872 else if(IsSupportHelper(target))
3873 {
3874 epithet = chanserv_conf.support_helper_epithet;
3875 type = "support helper";
3876 }
3877 if(epithet)
3878 {
3879 if(target_handle->epithet)
3880 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3881 else if(epithet)
3882 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3883 }
3884 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3885 }
3886 else
3887 {
3888 sprintf(prefix, "%s", target_handle->handle);
3889 }
3890
3891 if(!channel->channel_info)
3892 {
3893 reply("CSMSG_NOT_REGISTERED", channel->name);
3894 return 1;
3895 }
3896
3897 helping = HANDLE_FLAGGED(target_handle, HELPING)
3898 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3899 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3900 {
3901 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, user_level_name_from_level(uData->access), uData->access, channel->name);
3902 /* To prevent possible information leaks, only show infolines
3903 * if the requestor is in the channel or it's their own
3904 * handle. */
3905 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3906 {
3907 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3908 }
3909 /* Likewise, only say it's suspended if the user has active
3910 * access in that channel or it's their own entry. */
3911 if(IsUserSuspended(uData)
3912 && (GetChannelUser(channel->channel_info, user->handle_info)
3913 || (user->handle_info == uData->handle)))
3914 {
3915 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3916 }
3917 }
3918 else
3919 {
3920 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3921 }
3922
3923 return 1;
3924 }
3925
3926 /* This is never used...
3927 static void
3928 zoot_list(struct listData *list)
3929 {
3930 struct userData *uData;
3931 unsigned int start, curr, highest, lowest;
3932 struct helpfile_table tmp_table;
3933 const char **temp, *msg;
3934
3935 if(list->table.length == 1)
3936 {
3937 if(list->search)
3938 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
3939 else
3940 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
3941 msg = user_find_message(list->user, "MSG_NONE");
3942 send_message_type(4, list->user, list->bot, " %s", msg);
3943 }
3944 tmp_table.width = list->table.width;
3945 tmp_table.flags = list->table.flags;
3946 list->table.contents[0][0] = " ";
3947 highest = list->highest;
3948 if(list->lowest != 0)
3949 lowest = list->lowest;
3950 else if(highest < 100)
3951 lowest = 1;
3952 else
3953 lowest = highest - 100;
3954 for(start = curr = 1; curr < list->table.length; )
3955 {
3956 uData = list->users[curr-1];
3957 list->table.contents[curr++][0] = " ";
3958 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3959 {
3960 if(list->search)
3961 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, user_level_name_from_level(lowest), user_level_name_from_level(highest), list->search);
3962 else
3963 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, user_level_name_from_level(lowest), user_level_name_from_level(highest));
3964 temp = list->table.contents[--start];
3965 list->table.contents[start] = list->table.contents[0];
3966 tmp_table.contents = list->table.contents + start;
3967 tmp_table.length = curr - start;
3968 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3969 list->table.contents[start] = temp;
3970 start = curr;
3971 highest = lowest - 1;
3972 lowest = (highest < 100) ? 0 : (highest - 99);
3973 }
3974 }
3975 }
3976 */
3977
3978 static void
3979 normal_list(struct listData *list)
3980 {
3981 const char *msg;
3982 if(list->search)
3983 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
3984 else
3985 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_NORMAL", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
3986 if(list->table.length == 1)
3987 {
3988 msg = user_find_message(list->user, "MSG_NONE");
3989 send_message_type(4, list->user, list->bot, " %s", msg);
3990 }
3991 else
3992 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3993 }
3994
3995 /* if these need changed, uncomment and customize
3996 static void
3997 clean_list(struct listData *list)
3998 {
3999 const char *msg;
4000 if(list->search)
4001 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
4002 else
4003 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLEAN", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
4004 if(list->table.length == 1)
4005 {
4006 msg = user_find_message(list->user, "MSG_NONE");
4007 send_message_type(4, list->user, list->bot, " %s", msg);
4008 }
4009 else
4010 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4011 }
4012
4013 static void
4014 advanced_list(struct listData *list)
4015 {
4016 const char *msg;
4017 if(list->search)
4018 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
4019 else
4020 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_ADVANCED", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
4021 if(list->table.length == 1)
4022 {
4023 msg = user_find_message(list->user, "MSG_NONE");
4024 send_message_type(4, list->user, list->bot, " %s", msg);
4025 }
4026 else
4027 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4028 }
4029
4030 static void
4031 classic_list(struct listData *list)
4032 {
4033 const char *msg;
4034 if(list->search)
4035 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
4036 else
4037 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
4038 if(list->table.length == 1)
4039 {
4040 msg = user_find_message(list->user, "MSG_NONE");
4041 send_message_type(4, list->user, list->bot, " %s", msg);
4042 }
4043 else
4044 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4045 }
4046 */
4047
4048 static int
4049 userData_access_comp(const void *arg_a, const void *arg_b)
4050 {
4051 const struct userData *a = *(struct userData**)arg_a;
4052 const struct userData *b = *(struct userData**)arg_b;
4053 int res;
4054 if(a->access != b->access)
4055 res = b->access - a->access;
4056 else
4057 res = irccasecmp(a->handle->handle, b->handle->handle);
4058 return res;
4059 }
4060
4061 static int
4062 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4063 {
4064 void (*send_list)(struct listData *);
4065 struct userData *uData;
4066 struct listData lData;
4067 unsigned int matches;
4068 const char **ary;
4069 int i = 0;
4070 int seen_index;
4071
4072 lData.user = user;
4073 lData.bot = cmd->parent->bot;
4074 lData.channel = channel;
4075 lData.lowest = lowest;
4076 lData.highest = highest;
4077 lData.search = (argc > 1) ? argv[1] : NULL;
4078 send_list = normal_list;
4079 /* What does the following line do exactly?? */
4080 /*(void)zoot_list; ** since it doesn't show user levels */
4081
4082 /*
4083 if(user->handle_info)
4084 {
4085 switch(user->handle_info->userlist_style)
4086 {
4087 case HI_STYLE_CLEAN:
4088 send_list = clean_list;
4089 break;
4090 case HI_STYLE_ADVANCED:
4091 send_list = advanced_list;
4092 break;
4093 case HI_STYLE_CLASSIC:
4094 send_list = classic_list;
4095 break;
4096 case HI_STYLE_NORMAL:
4097 default:
4098 send_list = normal_list;
4099 break;
4100 }
4101 }
4102 */
4103 send_list = normal_list;
4104
4105 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4106 matches = 0;
4107 for(uData = channel->channel_info->users; uData; uData = uData->next)
4108 {
4109 if((uData->access < lowest)
4110 || (uData->access > highest)
4111 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4112 continue;
4113 lData.users[matches++] = uData;
4114 }
4115 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4116
4117 lData.table.length = matches+1;
4118 lData.table.flags = TABLE_NO_FREE;
4119 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4120
4121 if(user->handle_info && user->handle_info->userlist_style == HI_STYLE_ADVANCED)
4122 lData.table.width = 5; /* with level = 5 */
4123 else
4124 lData.table.width = 4; /* without = 4 */
4125 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4126 lData.table.contents[0] = ary;
4127 if(user->handle_info) {
4128 switch(user->handle_info->userlist_style) {
4129 case HI_STYLE_CLASSIC:
4130 ary[i++] = "Level";
4131 break;
4132 case HI_STYLE_ADVANCED:
4133 ary[i++] = "Access";
4134 ary[i++] = "Level";
4135 break;
4136 case HI_STYLE_CLEAN:
4137 ary[i++] = "Access";
4138 break;
4139 case HI_STYLE_NORMAL:
4140 default:
4141 ary[i++] = "Access";
4142 break;
4143 }
4144 }
4145 else {
4146 ary[i++] = "Access";
4147 }
4148 ary[i++] = "Account";
4149 ary[i] = "Last Seen";
4150 seen_index = i++;
4151 ary[i++] = "Status";
4152 for(matches = 1; matches < lData.table.length; ++matches)
4153 {
4154 struct userData *uData = lData.users[matches-1];
4155 char seen[INTERVALLEN];
4156
4157 i = 0;
4158 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4159 lData.table.contents[matches] = ary;
4160 if(user->handle_info) {
4161 switch(user->handle_info->userlist_style) {
4162 case HI_STYLE_CLASSIC:
4163 ary[i++] = strtab(uData->access);
4164 break;
4165 case HI_STYLE_ADVANCED:
4166 ary[i++] = user_level_name_from_level(uData->access);
4167 ary[i++] = strtab(uData->access);
4168 break;
4169 case HI_STYLE_CLEAN:
4170 ary[i++] = user_level_name_from_level(uData->access);
4171 break;
4172 case HI_STYLE_NORMAL:
4173 default:
4174 ary[i++] = user_level_name_from_level(uData->access);
4175 break;
4176 }
4177 }
4178 else {
4179 ary[i++] = user_level_name_from_level(uData->access);
4180 }
4181 ary[i++] = uData->handle->handle;
4182 if(uData->present)
4183 ary[i] = "Here";
4184 else if(!uData->seen)
4185 ary[i] = "Never";
4186 else
4187 ary[i] = intervalString(seen, now - uData->seen, user->handle_info);
4188 ary[i] = strdup(ary[i]);
4189 i++;
4190 if(IsUserSuspended(uData))
4191 ary[i++] = "Suspended";
4192 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4193 ary[i++] = "Vacation";
4194 else
4195 ary[i++] = "Normal";
4196 }
4197 send_list(&lData);
4198 for(matches = 1; matches < lData.table.length; ++matches)
4199 {
4200 /* Free strdup above */
4201 free((char*)lData.table.contents[matches][seen_index]);
4202 free(lData.table.contents[matches]);
4203 }
4204 free(lData.table.contents[0]);
4205 free(lData.table.contents);
4206 return 1;
4207 }
4208
4209 /* Remove this now that debugging is over? or improve it for
4210 * users? Would it be better tied into USERS somehow? -Rubin */
4211 static CHANSERV_FUNC(cmd_pending)
4212 {
4213 struct adduserPending *ap;
4214 reply("CSMSG_ADDUSER_PENDING_HEADER");
4215 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
4216 reply("CSMSG_BAR");
4217 for(ap = adduser_pendings;ap;ap = ap->next)
4218 reply("CSMSG_ADDUSER_PENDING_LIST", ap->channel->name, ap->user->nick);
4219 reply("CSMSG_ADDUSER_PENDING_FOOTER");
4220 return 1;
4221 }
4222
4223 static CHANSERV_FUNC(cmd_users)
4224 {
4225 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4226 }
4227
4228 static CHANSERV_FUNC(cmd_wlist)
4229 {
4230 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4231 }
4232
4233 static CHANSERV_FUNC(cmd_clist)
4234 {
4235 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4236 }
4237
4238 static CHANSERV_FUNC(cmd_mlist)
4239 {
4240 return cmd_list_users(CSFUNC_ARGS, UL_MANAGER, UL_COOWNER-1);
4241 }
4242
4243 static CHANSERV_FUNC(cmd_olist)
4244 {
4245 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MANAGER-1);
4246 }
4247
4248 static CHANSERV_FUNC(cmd_hlist)
4249 {
4250 return cmd_list_users(CSFUNC_ARGS, UL_HALFOP, UL_OP-1);
4251 }
4252
4253 static CHANSERV_FUNC(cmd_plist)
4254 {
4255 return cmd_list_users(CSFUNC_ARGS, 1, UL_HALFOP-1);
4256 }
4257
4258 static CHANSERV_FUNC(cmd_lamers)
4259 {
4260 struct helpfile_table tbl;
4261 unsigned int matches = 0, timed = 0, ii;
4262 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4263 const char *msg_never, *triggered, *expires;
4264 struct banData *ban, **bans; /* lamers */
4265
4266 if(argc > 1)
4267 search = argv[1];
4268 else
4269 search = NULL;
4270
4271 reply("CSMSG_LAMERS_HEADER", channel->name);
4272 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *)); /* lamers */
4273
4274 /* lamers */
4275 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4276 {
4277 if(search && !match_ircglobs(search, ban->mask))
4278 continue;
4279 bans[matches++] = ban;
4280 if(ban->expires)
4281 timed = 1;
4282 }
4283
4284 tbl.length = matches + 1;
4285 tbl.width = 4 + timed;
4286 tbl.flags = 0;
4287 tbl.flags = TABLE_NO_FREE;
4288 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4289 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4290 tbl.contents[0][0] = "Mask";
4291 tbl.contents[0][1] = "Set By";
4292 tbl.contents[0][2] = "Triggered";
4293 if(timed)
4294 {
4295 tbl.contents[0][3] = "Expires";
4296 tbl.contents[0][4] = "Reason";
4297 }
4298 else
4299 tbl.contents[0][3] = "Reason";
4300 if(!matches)
4301 {
4302 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4303 /* reply("MSG_NONE"); */
4304 free(tbl.contents[0]);
4305 free(tbl.contents);
4306 return 0;
4307 }
4308
4309 msg_never = user_find_message(user, "MSG_NEVER");
4310 for(ii = 0; ii < matches; )
4311 {
4312 ban = bans[ii];
4313
4314 if(!timed)
4315 expires = "";
4316 else if(ban->expires)
4317 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4318 else
4319 expires = msg_never;
4320
4321 if(ban->triggered)
4322 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4323 else
4324 triggered = msg_never;
4325
4326 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4327 tbl.contents[ii][0] = ban->mask;
4328 tbl.contents[ii][1] = ban->owner;
4329 tbl.contents[ii][2] = strdup(triggered);
4330 if(timed)
4331 {
4332 tbl.contents[ii][3] = strdup(expires);
4333 tbl.contents[ii][4] = ban->reason;
4334 }
4335 else
4336 tbl.contents[ii][3] = ban->reason;
4337 }
4338 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4339 /* reply("MSG_MATCH_COUNT", matches); */
4340 for(ii = 1; ii < tbl.length; ++ii)
4341 {
4342 free((char*)tbl.contents[ii][2]);
4343 if(timed)
4344 free((char*)tbl.contents[ii][3]);
4345 free(tbl.contents[ii]);
4346 }
4347 free(tbl.contents[0]);
4348 free(tbl.contents);
4349 return 1;
4350 }
4351
4352 /* bad_topic
4353 *
4354 * return + if the user does NOT have the right to set the topic, and
4355 * the topic is changed.
4356 */
4357 static int
4358 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4359 {
4360 struct chanData *cData = channel->channel_info;
4361 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4362 return 0;
4363 else if(cData->topic)
4364 return irccasecmp(new_topic, cData->topic);
4365 else
4366 return 0;
4367 }
4368
4369 /* conform_topic
4370 *
4371 * Makes a givin topic fit into a givin topic mask and returns
4372 * the results.
4373 *
4374 * topic_mask - the mask to conform to
4375 * topic - the topic to make conform
4376 * new_topic - the pre-allocated char* to put the new topic into
4377 *
4378 * modifies: new_topic
4379 */
4380 void
4381 conform_topic(char* topic_mask, char* topic, char *new_topic)
4382 {
4383 //char *topic_mask = cData->topic_mask;
4384 char tchar;
4385 int pos=0, starpos=-1, dpos=0, len;
4386
4387 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4388 {
4389 switch(tchar)
4390 {
4391 case '*':
4392 if(starpos != -1)
4393 {
4394 strcpy(new_topic, "");
4395 return;
4396 }
4397 len = strlen(topic);
4398 if((dpos + len) > TOPICLEN)
4399 len = TOPICLEN + 1 - dpos;
4400 memcpy(new_topic+dpos, topic, len);
4401 dpos += len;
4402 starpos = pos;
4403 break;
4404 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4405 default: new_topic[dpos++] = tchar; break;
4406 }
4407 }
4408 if((dpos > TOPICLEN) || tchar)
4409 {
4410 strcpy(new_topic, "");
4411 return;
4412 }
4413 new_topic[dpos] = 0;
4414 return;
4415 }
4416
4417 static CHANSERV_FUNC(cmd_topic)
4418 {
4419 struct chanData *cData;
4420 char *topic;
4421 int p10 = 0;
4422
4423 #ifdef WITH_PROTOCOL_P10
4424 p10 = 1;
4425 #endif
4426
4427 cData = channel->channel_info;
4428 if(argc < 2)
4429 {
4430 if(cData->topic)
4431 {
4432 /*XXX Why would we ever want to send chanserv as the setter? I dont understand -Rubin */
4433 SetChannelTopic(channel, chanserv, p10 ? user : chanserv, cData->topic, 1);
4434 reply("CSMSG_TOPIC_SET", cData->topic);
4435 return 1;
4436 }
4437
4438 reply("CSMSG_NO_TOPIC", channel->name);
4439 return 0;
4440 }
4441
4442 topic = unsplit_string(argv + 1, argc - 1, NULL);
4443 /* If they say "!topic *", use an empty topic. */
4444 if((topic[0] == '*') && (topic[1] == 0))
4445 topic[0] = 0;
4446
4447 if(bad_topic(channel, user, topic))
4448 {
4449 reply("CSMSG_TOPIC_LOCKED", channel->name);
4450 return 0;
4451 }
4452 else
4453 {
4454 /* If there is a topicmask set, and the new topic doesnt match, make it */
4455 if(cData->topic_mask && !match_ircglob(topic, cData->topic_mask))
4456 {
4457 char *topic_mask = cData->topic_mask;
4458 char new_topic[TOPICLEN+1];
4459
4460 /* make a new topic fitting mask */
4461 conform_topic(topic_mask, topic, new_topic);
4462 if(!*new_topic)
4463 {
4464 /* Topic couldnt fit into mask, was too long */
4465 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4466 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4467 return 0;
4468 }
4469 SetChannelTopic(channel, chanserv, p10 ? user : chanserv, new_topic, 1);
4470 }
4471 else /* No mask set, just set the topic */
4472 SetChannelTopic(channel, chanserv, p10 ? user : chanserv, topic, 1);
4473 }
4474
4475 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4476 {
4477 /* Grab the topic and save it as the default topic. */
4478 free(cData->topic);
4479 cData->topic = strdup(channel->topic);
4480 }
4481
4482 return 1;
4483 }
4484
4485 static CHANSERV_FUNC(cmd_mode)
4486 {
4487 struct userData *uData;
4488 struct mod_chanmode *change;
4489 short base_oplevel;
4490
4491 if(argc < 2)
4492 {
4493 if (checkDefCon(DEFCON_NO_MODE_CHANGE) && !IsOper(user)) {
4494 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
4495 return 0;
4496 }
4497
4498 change = &channel->channel_info->modes;
4499 if(change->modes_set || change->modes_clear) {
4500 modcmd_chanmode_announce(change);
4501 reply("CSMSG_DEFAULTED_MODES", channel->name);
4502 } else
4503 reply("CSMSG_NO_MODES", channel->name);
4504 return 1;
4505 }
4506
4507 uData = GetChannelUser(channel->channel_info, user->handle_info);
4508 if (!uData)
4509 base_oplevel = MAXOPLEVEL;
4510 else if (uData->access >= UL_OWNER)
4511 base_oplevel = 1;
4512 else
4513 base_oplevel = 1 + UL_OWNER - uData->access;
4514 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
4515
4516 if(!change)
4517 {
4518 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4519 return 0;
4520 }
4521
4522 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4523 && mode_lock_violated(&channel->channel_info->modes, change))
4524 {
4525 char modes[MAXLEN];
4526 mod_chanmode_format(&channel->channel_info->modes, modes);
4527 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4528 return 0;
4529 }
4530
4531 modcmd_chanmode_announce(change);
4532 mod_chanmode_free(change);
4533 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
4534 return 1;
4535 }
4536
4537 static CHANSERV_FUNC(cmd_invite)
4538 {
4539 struct userData *uData;
4540 struct userNode *invite;
4541
4542 uData = GetChannelUser(channel->channel_info, user->handle_info);
4543
4544 if(argc > 1)
4545 {
4546 if(!(invite = GetUserH(argv[1])))
4547 {
4548 reply("MSG_NICK_UNKNOWN", argv[1]);
4549 return 0;
4550 }
4551 }
4552 else
4553 invite = user;
4554
4555 if(GetUserMode(channel, invite))
4556 {
4557 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4558 return 0;
4559 }
4560
4561 if(user != invite)
4562 {
4563 if(argc > 2)
4564 {
4565 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4566 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4567 }
4568 else
4569 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4570 }
4571
4572 if (invite->handle_info && invite->handle_info->ignores->used && (argc > 1)) {
4573 unsigned int i;
4574 for (i=0; i < invite->handle_info->ignores->used; i++) {
4575 if (user_matches_glob(user, invite->handle_info->ignores->list[i], MATCH_USENICK)) {
4576 reply("CSMSG_CANNOT_INVITE", argv[1], channel->name);
4577 return 0;
4578 }
4579 }
4580 }
4581
4582 irc_invite(chanserv, invite, channel);
4583 if(argc > 1)
4584 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4585
4586 return 1;
4587 }
4588
4589 static CHANSERV_FUNC(cmd_inviteme)
4590 {
4591 if(GetUserMode(channel, user))
4592 {
4593 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4594 return 0;
4595 }
4596 if(channel->channel_info
4597 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4598 {
4599 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4600 return 0;
4601 }
4602 irc_invite(cmd->parent->bot, user, channel);
4603 return 1;
4604 }
4605
4606 static void
4607 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4608 {
4609 unsigned int combo;
4610 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4611
4612 /* We display things based on two dimensions:
4613 * - Issue time: present or absent
4614 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4615 * (in order of precedence, so something both expired and revoked
4616 * only counts as revoked)
4617 */
4618 combo = (suspended->issued ? 4 : 0)
4619 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4620 switch(combo) {
4621 case 0: /* no issue time, indefinite expiration */
4622 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4623 break;
4624 case 1: /* no issue time, expires in future */
4625 intervalString(buf1, suspended->expires-now, user->handle_info);
4626 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4627 break;
4628 case 2: /* no issue time, expired */
4629 intervalString(buf1, now-suspended->expires, user->handle_info);
4630 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4631 break;
4632 case 3: /* no issue time, revoked */
4633 intervalString(buf1, now-suspended->revoked, user->handle_info);
4634 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4635 break;
4636 case 4: /* issue time set, indefinite expiration */
4637 intervalString(buf1, now-suspended->issued, user->handle_info);
4638 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4639 break;
4640 case 5: /* issue time set, expires in future */
4641 intervalString(buf1, now-suspended->issued, user->handle_info);
4642 intervalString(buf2, suspended->expires-now, user->handle_info);
4643 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4644 break;
4645 case 6: /* issue time set, expired */
4646 intervalString(buf1, now-suspended->issued, user->handle_info);
4647 intervalString(buf2, now-suspended->expires, user->handle_info);
4648 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4649 break;
4650 case 7: /* issue time set, revoked */
4651 intervalString(buf1, now-suspended->issued, user->handle_info);
4652 intervalString(buf2, now-suspended->revoked, user->handle_info);
4653 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4654 break;
4655 default:
4656 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4657 return;
4658 }
4659 }
4660
4661 static void
4662 show_giveownership_info(struct svccmd *cmd, struct userNode *user, struct giveownership *giveownership)
4663 {
4664 char buf[MAXLEN];
4665 const char *fmt = "%a %b %d %H:%M %Y";
4666 strftime(buf, sizeof(buf), fmt, localtime(&giveownership->issued));
4667
4668 if(giveownership->staff_issuer)
4669 {
4670 if(giveownership->reason)
4671 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership->old_owner,
4672 giveownership->target, giveownership->target_access,
4673 giveownership->staff_issuer, buf, giveownership->reason);
4674 else
4675 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership->old_owner,
4676 giveownership->target, giveownership->target_access,
4677 giveownership->staff_issuer, buf);
4678 }
4679 else
4680 {
4681 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership->old_owner, giveownership->target, giveownership->target_access, buf);
4682 }
4683 }
4684
4685
4686 static CHANSERV_FUNC(cmd_info)
4687 {
4688 char modes[MAXLEN], buffer[INTERVALLEN];
4689 struct userData *uData, *owner;
4690 struct chanData *cData;
4691 struct do_not_register *dnr;
4692 struct note *note;
4693 dict_iterator_t it;
4694 int privileged;
4695
4696 cData = channel->channel_info;
4697 reply("CSMSG_CHANNEL_INFO", channel->name);
4698 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
4699 reply("CSMSG_BAR");
4700
4701 uData = GetChannelUser(cData, user->handle_info);
4702 if(uData && (uData->access >= UL_OP /*cData->lvlOpts[lvlGiveOps]*/))
4703 {
4704 mod_chanmode_format(&cData->modes, modes);
4705 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4706 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4707 }
4708
4709 for(it = dict_first(cData->notes); it; it = iter_next(it))
4710 {
4711 int padding;
4712
4713 note = iter_data(it);
4714 if(!note_type_visible_to_user(cData, note->type, user))
4715 continue;
4716
4717 padding = PADLEN - 1 - strlen(iter_key(it));
4718 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4719 }
4720
4721 reply("CSMSG_CHANNEL_MAX", cData->max);
4722 for(owner = cData->users; owner; owner = owner->next)
4723 if(owner->access == UL_OWNER)
4724 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4725 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4726 reply("CSMSG_CHANNEL_LAMERS", cData->banCount);
4727 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4728
4729 privileged = IsStaff(user);
4730 /* if(privileged) */
4731 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4732 if(/*((uData && uData->access >= UL_COOWNER) || privileged) && */cData->registrar)
4733 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4734
4735 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4736 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4737
4738 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4739 {
4740 struct suspended *suspended;
4741 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4742 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4743 show_suspension_info(cmd, user, suspended);
4744 }
4745 else if(IsSuspended(cData))
4746 {
4747 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4748 show_suspension_info(cmd, user, cData->suspended);
4749 }
4750 if(cData->giveownership && ((uData && (uData->access >= UL_COOWNER)) || IsStaff(user)))
4751 {
4752 struct giveownership *giveownership;
4753 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel->name);
4754 for(giveownership = cData->giveownership; giveownership; giveownership = giveownership->previous)
4755 show_giveownership_info(cmd, user, giveownership);
4756 }
4757 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
4758 reply("CSMSG_CHANNEL_END");
4759 else
4760 reply("CSMSG_CHANNEL_END_CLEAN");
4761 return 1;
4762 }
4763
4764 static CHANSERV_FUNC(cmd_netinfo)
4765 {
4766 extern time_t boot_time;
4767 extern unsigned long burst_length;
4768 char interval[INTERVALLEN];
4769
4770 reply("CSMSG_NETWORK_INFO");
4771 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4772 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4773 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4774 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4775 reply("CSMSG_NETWORK_LAMERS", banCount);
4776 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4777 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4778 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4779 return 1;
4780 }
4781
4782 static void
4783 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4784 {
4785 struct helpfile_table table;
4786 unsigned int nn;
4787 struct userNode *user;
4788 char *nick;
4789
4790 table.length = 0;
4791 table.width = 1;
4792 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4793 table.contents = alloca(list->used*sizeof(*table.contents));
4794 for(nn=0; nn<list->used; nn++)
4795 {
4796 user = list->list[nn];
4797 if(user->modes & skip_flags)
4798 continue;
4799 if(IsBot(user))
4800 continue;
4801 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4802 if(IsAway(user))
4803 {
4804 nick = alloca(strlen(user->nick)+3);
4805 sprintf(nick, "(%s)", user->nick);
4806 }
4807 else
4808 nick = user->nick;
4809 table.contents[table.length][0] = nick;
4810 table.length++;
4811 }
4812 table_send(chanserv, to->nick, 0, NULL, table);
4813 }
4814
4815 static CHANSERV_FUNC(cmd_ircops)
4816 {
4817 reply("CSMSG_STAFF_OPERS");
4818 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4819 return 1;
4820 }
4821
4822 static CHANSERV_FUNC(cmd_helpers)
4823 {
4824 reply("CSMSG_STAFF_HELPERS");
4825 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4826 return 1;
4827 }
4828
4829 static CHANSERV_FUNC(cmd_staff)
4830 {
4831 reply("CSMSG_NETWORK_STAFF");
4832 cmd_ircops(CSFUNC_ARGS);
4833 cmd_helpers(CSFUNC_ARGS);
4834 return 1;
4835 }
4836
4837 static CHANSERV_FUNC(cmd_peek)
4838 {
4839 struct modeNode *mn;
4840 char modes[MODELEN];
4841 unsigned int n;
4842 struct helpfile_table table;
4843
4844 irc_make_chanmode(channel, modes);
4845
4846 reply("CSMSG_PEEK_INFO", channel->name);
4847 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
4848 reply("CSMSG_BAR");
4849 reply("CSMSG_PEEK_TOPIC", channel->topic);
4850 reply("CSMSG_PEEK_MODES", modes);
4851 reply("CSMSG_PEEK_USERS", channel->members.used);
4852
4853 table.length = 0;
4854 table.width = 1;
4855 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4856 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4857 for(n = 0; n < channel->members.used; n++)
4858 {
4859 mn = channel->members.list[n];
4860 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4861 continue;
4862 table.contents[table.length] = alloca(sizeof(**table.contents));
4863 table.contents[table.length][0] = mn->user->nick;
4864 table.length++;
4865 }
4866 if(table.length)
4867 {
4868 reply("CSMSG_PEEK_OPS");
4869 table_send(chanserv, user->nick, 0, NULL, table);
4870 }
4871 else
4872 reply("CSMSG_PEEK_NO_OPS");
4873 reply("CSMSG_PEEK_END");
4874 return 1;
4875 }
4876
4877 static MODCMD_FUNC(cmd_wipeinfo)
4878 {
4879 struct handle_info *victim;
4880 struct userData *ud, *actor;
4881
4882 REQUIRE_PARAMS(2);
4883 actor = GetChannelUser(channel->channel_info, user->handle_info);
4884 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4885 return 0;
4886 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4887 {
4888 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4889 return 0;
4890 }
4891 if((ud->access >= actor->access) && (ud != actor))
4892 {
4893 reply("MSG_USER_OUTRANKED", victim->handle);
4894 return 0;
4895 }
4896 if(ud->info)
4897 free(ud->info);
4898 ud->info = NULL;
4899 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4900 return 1;
4901 }
4902
4903 static void
4904 resync_channel(struct chanNode *channel)
4905 {
4906 struct mod_chanmode *changes;
4907 struct chanData *cData = channel->channel_info;
4908 unsigned int ii, used;
4909
4910 /* 6 = worst case -ovh+ovh on everyone */
4911 changes = mod_chanmode_alloc(channel->members.used * 6);
4912 for(ii = used = 0; ii < channel->members.used; ++ii)
4913 {
4914 struct modeNode *mn = channel->members.list[ii];
4915 struct userData *uData;
4916
4917 if(IsService(mn->user))
4918 continue;
4919
4920
4921 uData = GetChannelAccess(cData, mn->user->handle_info);
4922
4923 /* If the channel is in no-mode mode, de-mode EVERYONE */
4924 if(cData->chOpts[chAutomode] == 'n')
4925 {
4926 if(mn->modes)
4927 {
4928 changes->args[used].mode = MODE_REMOVE | mn->modes;
4929 changes->args[used++].u.member = mn;
4930 }
4931 }
4932 else /* Give various userlevels their modes.. */
4933 {
4934 if(uData && uData->access >= UL_OP )
4935 {
4936 if(!(mn->modes & MODE_CHANOP))
4937 {
4938 changes->args[used].mode = MODE_CHANOP;
4939 changes->args[used++].u.member = mn;
4940 }
4941 }
4942 else if(uData && uData->access >= UL_HALFOP)
4943 {
4944 if(mn->modes & MODE_CHANOP)
4945 {
4946 changes->args[used].mode = MODE_REMOVE | MODE_CHANOP;
4947 changes->args[used++].u.member = mn;
4948 }
4949 if(!(mn->modes & MODE_HALFOP))
4950 {
4951 changes->args[used].mode = MODE_HALFOP;
4952 changes->args[used++].u.member = mn;
4953 }
4954 }
4955 else if(uData && uData->access >= UL_PEON )
4956 {
4957 if(mn->modes & MODE_CHANOP)
4958 {
4959 changes->args[used].mode = MODE_REMOVE | MODE_CHANOP;
4960 changes->args[used++].u.member = mn;
4961 }
4962 if(mn->modes & MODE_HALFOP)
4963 {
4964 changes->args[used].mode = MODE_REMOVE | MODE_HALFOP;
4965 changes->args[used++].u.member = mn;
4966 }
4967 /* Don't voice peons if were in mode m */
4968 if( cData->chOpts[chAutomode] == 'm')
4969 {
4970 if(mn->modes & MODE_VOICE)
4971 {
4972 changes->args[used].mode = MODE_REMOVE | MODE_VOICE;
4973 changes->args[used++].u.member = mn;
4974 }
4975 }
4976 /* otherwise, make user they do have voice */
4977 else if(!(mn->modes & MODE_VOICE))
4978 {
4979 changes->args[used].mode = MODE_VOICE;
4980 changes->args[used++].u.member = mn;
4981 }
4982 }
4983 else /* They arnt on the userlist.. */
4984 {
4985 /* If we voice everyone, but they dont.. */
4986 if(cData->chOpts[chAutomode] == 'v')
4987 {
4988 /* Remove anything except v */
4989 if(mn->modes & ~MODE_VOICE)
4990 {
4991 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4992 changes->args[used++].u.member = mn;
4993 }
4994 /* Add v */
4995 if(!(mn->modes & MODE_VOICE))
4996 {
4997 changes->args[used].mode = MODE_VOICE;
4998 changes->args[used++].u.member = mn;
4999 }
5000 }
5001 /* If we hop everyone, but they dont.. */
5002 else if(cData->chOpts[chAutomode] == 'h')
5003 {
5004 /* Remove anything except h */
5005 if(mn->modes & ~MODE_HALFOP)
5006 {
5007 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_HALFOP);
5008 changes->args[used++].u.member = mn;
5009 }
5010 /* Add h */
5011 if(!(mn->modes & MODE_HALFOP))
5012 {
5013 changes->args[used].mode = MODE_HALFOP;
5014 changes->args[used++].u.member = mn;
5015 }
5016 }
5017 /* If we op everyone, but they dont.. */
5018 else if(cData->chOpts[chAutomode] == 'o')
5019 {
5020 /* Remove anything except h */
5021 if(mn->modes & ~MODE_CHANOP)
5022 {
5023 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_CHANOP);
5024 changes->args[used++].u.member = mn;
5025 }
5026 /* Add h */
5027 if(!(mn->modes & MODE_CHANOP))
5028 {
5029 changes->args[used].mode = MODE_CHANOP;
5030 changes->args[used++].u.member = mn;
5031 }
5032 }
5033 /* they have no excuse for having modes, de-everything them */
5034 else
5035 {
5036 if(mn->modes)
5037 {
5038 changes->args[used].mode = MODE_REMOVE | mn->modes;
5039 changes->args[used++].u.member = mn;
5040 }
5041 }
5042 }
5043 }
5044 }
5045 changes->argc = used;
5046 mod_chanmode_announce(chanserv, channel, changes);
5047 mod_chanmode_free(changes);
5048 }
5049
5050 static CHANSERV_FUNC(cmd_resync)
5051 {
5052 resync_channel(channel);
5053 reply("CSMSG_RESYNCED_USERS", channel->name);
5054 return 1;
5055 }
5056
5057 static CHANSERV_FUNC(cmd_seen)
5058 {
5059 struct userData *uData;
5060 struct handle_info *handle;
5061 char seen[INTERVALLEN];
5062
5063 REQUIRE_PARAMS(2);
5064
5065 if(!irccasecmp(argv[1], chanserv->nick))
5066 {
5067 reply("CSMSG_IS_CHANSERV");
5068 return 1;
5069 }
5070
5071 if(!(handle = get_handle_info(argv[1])))
5072 {
5073 reply("MSG_HANDLE_UNKNOWN", argv[1]);
5074 return 0;
5075 }
5076
5077 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
5078 {
5079 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
5080 return 0;
5081 }
5082
5083 if(uData->present)
5084 reply("CSMSG_USER_PRESENT", handle->handle);
5085 else if(uData->seen)
5086 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
5087 else
5088 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
5089
5090 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
5091 reply("CSMSG_USER_VACATION", handle->handle);
5092
5093 return 1;
5094 }
5095
5096 static MODCMD_FUNC(cmd_names)
5097 {
5098 struct userNode *targ;
5099 struct userData *targData;
5100 unsigned int ii, pos;
5101 char buf[400];
5102
5103 for(ii=pos=0; ii<channel->members.used; ++ii)
5104 {
5105 targ = channel->members.list[ii]->user;
5106 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
5107 if(!targData)
5108 continue;
5109 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
5110 {
5111 buf[pos] = 0;
5112 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5113 pos = 0;
5114 }
5115 buf[pos++] = ' ';
5116 if(IsUserSuspended(targData))
5117 buf[pos++] = 's';
5118 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
5119 }
5120 buf[pos] = 0;
5121 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5122 reply("CSMSG_END_NAMES", channel->name);
5123 return 1;
5124 }
5125
5126 static int
5127 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
5128 {
5129 switch(ntype->visible_type)
5130 {
5131 case NOTE_VIS_ALL: return 1;
5132 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
5133 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
5134 }
5135 }
5136
5137 static int
5138 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
5139 {
5140 struct userData *uData;
5141
5142 switch(ntype->set_access_type)
5143 {
5144 case NOTE_SET_CHANNEL_ACCESS:
5145 if(!user->handle_info)
5146 return 0;
5147 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
5148 return 0;
5149 return uData->access >= ntype->set_access.min_ulevel;
5150 case NOTE_SET_CHANNEL_SETTER:
5151 return check_user_level(channel, user, lvlSetters, 1, 0);
5152 case NOTE_SET_PRIVILEGED: default:
5153 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
5154 }
5155 }
5156
5157 static CHANSERV_FUNC(cmd_note)
5158 {
5159 struct chanData *cData;
5160 struct note *note;
5161 struct note_type *ntype;
5162
5163 cData = channel->channel_info;
5164 if(!cData)
5165 {
5166 reply("CSMSG_NOT_REGISTERED", channel->name);
5167 return 0;
5168 }
5169
5170 /* If no arguments, show all visible notes for the channel. */
5171 if(argc < 2)
5172 {
5173 dict_iterator_t it;
5174 unsigned int count;
5175
5176 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5177 {
5178 note = iter_data(it);
5179 if(!note_type_visible_to_user(cData, note->type, user))
5180 continue;
5181 if(!count++)
5182 reply("CSMSG_NOTELIST_HEADER", channel->name);
5183 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5184 }
5185 if(count)
5186 reply("CSMSG_NOTELIST_END", channel->name);
5187 else
5188 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5189 }
5190 /* If one argument, show the named note. */
5191 else if(argc == 2)
5192 {
5193 if((note = dict_find(cData->notes, argv[1], NULL))
5194 && note_type_visible_to_user(cData, note->type, user))
5195 {
5196 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5197 }
5198 else if((ntype = dict_find(note_types, argv[1], NULL))
5199 && note_type_visible_to_user(NULL, ntype, user))
5200 {
5201 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5202 return 0;
5203 }
5204 else
5205 {
5206 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5207 return 0;
5208 }
5209 }
5210 /* Assume they're trying to set a note. */
5211 else
5212 {
5213 char *note_text;
5214 ntype = dict_find(note_types, argv[1], NULL);
5215 if(!ntype)
5216 {
5217 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5218 return 0;
5219 }
5220 else if(note_type_settable_by_user(channel, ntype, user))
5221 {
5222 note_text = unsplit_string(argv+2, argc-2, NULL);
5223 if((note = dict_find(cData->notes, argv[1], NULL)))
5224 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5225 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5226 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5227
5228 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5229 {
5230 /* The note is viewable to staff only, so return 0
5231 to keep the invocation from getting logged (or
5232 regular users can see it in !events). */
5233 return 0;
5234 }
5235 }
5236 else
5237 {
5238 reply("CSMSG_NO_ACCESS");
5239 return 0;
5240 }
5241 }
5242 return 1;
5243 }
5244
5245 static CHANSERV_FUNC(cmd_delnote)
5246 {
5247 struct note *note;
5248
5249 REQUIRE_PARAMS(2);
5250 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5251 || !note_type_settable_by_user(channel, note->type, user))
5252 {
5253 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5254 return 0;
5255 }
5256 dict_remove(channel->channel_info->notes, note->type->name);
5257 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5258 return 1;
5259 }
5260
5261 static CHANSERV_FUNC(cmd_last)
5262 {
5263 int numoflines;
5264
5265 REQUIRE_PARAMS(1);
5266
5267 numoflines = (argc > 1) ? atoi(argv[1]) : 10;
5268
5269 if(numoflines < 1 || numoflines > 200)
5270 {
5271 reply("CSMSG_LAST_INVALID");
5272 return 0;
5273 }
5274 ShowLog(user, channel, "*", "*", "*", "*", numoflines);
5275 return 1;
5276 }
5277
5278 static CHANSERV_FUNC(cmd_events)
5279 {
5280 struct logSearch discrim;
5281 struct logReport report;
5282 unsigned int matches, limit;
5283
5284 limit = (argc > 1) ? atoi(argv[1]) : 10;
5285 if(limit < 1 || limit > 200)
5286 limit = 10;
5287
5288 memset(&discrim, 0, sizeof(discrim));
5289 discrim.masks.bot = chanserv;
5290 discrim.masks.channel_name = channel->name;
5291 if(argc > 2)
5292 discrim.masks.command = argv[2];
5293 discrim.limit = limit;
5294 discrim.max_time = INT_MAX;
5295 discrim.severities = 1 << LOG_COMMAND;
5296 report.reporter = chanserv;
5297 report.user = user;
5298 reply("CSMSG_EVENT_SEARCH_RESULTS", channel->name);
5299 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
5300 reply("CSMSG_BAR");
5301 matches = log_entry_search(&discrim, log_report_entry, &report);
5302 if(matches)
5303 reply("MSG_MATCH_COUNT", matches);
5304 else
5305 reply("MSG_NO_MATCHES");
5306 return 1;
5307 }
5308
5309 static CHANSERV_FUNC(cmd_say)
5310 {
5311 char *msg;
5312 if(channel)
5313 {
5314 REQUIRE_PARAMS(2);
5315 msg = unsplit_string(argv + 1, argc - 1, NULL);
5316 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5317 }
5318 else if(GetUserH(argv[1]))
5319 {
5320 REQUIRE_PARAMS(3);
5321 msg = unsplit_string(argv + 2, argc - 2, NULL);
5322 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5323 }
5324 else
5325 {
5326 reply("MSG_NOT_TARGET_NAME");
5327 return 0;
5328 }
5329 return 1;
5330 }
5331
5332 static CHANSERV_FUNC(cmd_emote)
5333 {
5334 char *msg;
5335 assert(argc >= 2);
5336 if(channel)
5337 {
5338 /* CTCP is so annoying. */
5339 msg = unsplit_string(argv + 1, argc - 1, NULL);
5340 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5341 }
5342 else if(GetUserH(argv[1]))
5343 {
5344 msg = unsplit_string(argv + 2, argc - 2, NULL);
5345 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5346 }
5347 else
5348 {
5349 reply("MSG_NOT_TARGET_NAME");
5350 return 0;
5351 }
5352 return 1;
5353 }
5354
5355 struct channelList *
5356 chanserv_support_channels(void)
5357 {
5358 return &chanserv_conf.support_channels;
5359 }
5360
5361 static CHANSERV_FUNC(cmd_expire)
5362 {
5363 int channel_count = registered_channels;
5364 expire_channels(NULL);
5365 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5366 return 1;
5367 }
5368
5369 static void
5370 chanserv_expire_suspension(void *data)
5371 {
5372 struct suspended *suspended = data;
5373 struct chanNode *channel;
5374
5375 if(!suspended->expires || (now < suspended->expires))
5376 suspended->revoked = now;
5377 channel = suspended->cData->channel;
5378 suspended->cData->channel = channel;
5379 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5380 if(!IsOffChannel(suspended->cData))
5381 {
5382 spamserv_cs_suspend(channel, 0, 0, NULL);
5383 ss_cs_join_channel(channel, 1);
5384 }
5385 }
5386
5387 static CHANSERV_FUNC(cmd_csuspend)
5388 {
5389 struct suspended *suspended;
5390 char reason[MAXLEN];
5391 time_t expiry, duration;
5392 struct userData *uData;
5393
5394 REQUIRE_PARAMS(3);
5395
5396 if(IsProtected(channel->channel_info))
5397 {
5398 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5399 return 0;
5400 }
5401
5402 if(argv[1][0] == '!')
5403 argv[1]++;
5404 else if(IsSuspended(channel->channel_info))
5405 {
5406 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5407 show_suspension_info(cmd, user, channel->channel_info->suspended);
5408 return 0;
5409 }
5410
5411 if(!strcmp(argv[1], "0"))
5412 expiry = 0;
5413 else if((duration = ParseInterval(argv[1])))
5414 expiry = now + duration;
5415 else
5416 {
5417 reply("MSG_INVALID_DURATION", argv[1]);
5418 return 0;
5419 }
5420
5421 unsplit_string(argv + 2, argc - 2, reason);
5422
5423 suspended = calloc(1, sizeof(*suspended));
5424 suspended->revoked = 0;
5425 suspended->issued = now;
5426 suspended->suspender = strdup(user->handle_info->handle);
5427 suspended->expires = expiry;
5428 suspended->reason = strdup(reason);
5429 suspended->cData = channel->channel_info;
5430 suspended->previous = suspended->cData->suspended;
5431 suspended->cData->suspended = suspended;
5432
5433 if(suspended->expires)
5434 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5435
5436 if(IsSuspended(channel->channel_info))
5437 {
5438 suspended->previous->revoked = now;
5439 if(suspended->previous->expires)
5440 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5441
5442 global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "CSMSG_SUSPENSION_MODIFIED",
5443 channel->name, suspended->suspender);
5444 }
5445 else
5446 {
5447 /* Mark all users in channel as absent. */
5448 for(uData = channel->channel_info->users; uData; uData = uData->next)
5449 {
5450 if(uData->present)
5451 {
5452 uData->seen = now;
5453 uData->present = 0;
5454 }
5455 }
5456
5457 /* Mark the channel as suspended, then part. */
5458 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5459 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5460 DelChannelUser(chanserv, channel, suspended->reason, 0);
5461 reply("CSMSG_SUSPENDED", channel->name);
5462 global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "CSMSG_SUSPENDED_BY",
5463 channel->name, suspended->suspender);
5464 }
5465 return 1;
5466 }
5467
5468 static CHANSERV_FUNC(cmd_cunsuspend)
5469 {
5470 struct suspended *suspended;
5471
5472 if(!IsSuspended(channel->channel_info))
5473 {
5474 reply("CSMSG_NOT_SUSPENDED", channel->name);
5475 return 0;
5476 }
5477
5478 suspended = channel->channel_info->suspended;
5479
5480 /* Expire the suspension and join ChanServ to the channel. */
5481 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5482 chanserv_expire_suspension(suspended);
5483 reply("CSMSG_UNSUSPENDED", channel->name);
5484 global_message_args(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, "CSMSG_UNSUSPENDED_BY",
5485 channel->name, user->handle_info->handle);
5486 return 1;
5487 }
5488
5489 typedef struct chanservSearch
5490 {
5491 char *name;
5492 char *registrar;
5493
5494 time_t unvisited;
5495 time_t registered;
5496
5497 unsigned long flags;
5498 unsigned int limit;
5499 } *search_t;
5500
5501 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5502
5503 static search_t
5504 chanserv_search_create(struct svccmd *cmd, struct userNode *user, unsigned int argc, char *argv[])
5505 {
5506 search_t search;
5507 unsigned int i;
5508
5509 search = malloc(sizeof(struct chanservSearch));
5510 memset(search, 0, sizeof(*search));
5511 search->limit = 25;
5512
5513 for(i = 0; i < argc; i++)
5514 {
5515 /* Assume all criteria require arguments. */
5516 if(i == (argc - 1))
5517 {
5518 reply("MSG_MISSING_PARAMS", argv[i]);
5519 goto fail;
5520 }
5521
5522 if(!irccasecmp(argv[i], "name"))
5523 search->name = argv[++i];
5524 else if(!irccasecmp(argv[i], "registrar"))
5525 search->registrar = argv[++i];
5526 else if(!irccasecmp(argv[i], "unvisited"))
5527 search->unvisited = ParseInterval(argv[++i]);
5528 else if(!irccasecmp(argv[i], "registered"))
5529 search->registered = ParseInterval(argv[++i]);
5530 else if(!irccasecmp(argv[i], "flags"))
5531 {
5532 i++;
5533 if(!irccasecmp(argv[i], "nodelete"))
5534 search->flags |= CHANNEL_NODELETE;
5535 else if(!irccasecmp(argv[i], "suspended"))
5536 search->flags |= CHANNEL_SUSPENDED;
5537 else
5538 {
5539 reply("CSMSG_INVALID_CFLAG", argv[i]);
5540 goto fail;
5541 }
5542 }
5543 else if(!irccasecmp(argv[i], "limit"))
5544 search->limit = strtoul(argv[++i], NULL, 10);
5545 else
5546 {
5547 reply("MSG_INVALID_CRITERIA", argv[i]);
5548 goto fail;
5549 }
5550 }
5551
5552 if(search->name && !strcmp(search->name, "*"))
5553 search->name = 0;
5554 if(search->registrar && !strcmp(search->registrar, "*"))
5555 search->registrar = 0;
5556
5557 return search;
5558 fail:
5559 free(search);
5560 return NULL;
5561 }
5562
5563 static int
5564 chanserv_channel_match(struct chanData *channel, search_t search)
5565 {
5566 const char *name = channel->channel->name;
5567 if((search->name && !match_ircglob(name, search->name)) ||
5568 (search->registrar && !channel->registrar) ||
5569 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5570 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5571 (search->registered && (now - channel->registered) > search->registered) ||
5572 (search->flags && ((search->flags & channel->flags) != search->flags)))
5573 return 0;
5574
5575 return 1;
5576 }
5577
5578 static unsigned int
5579 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5580 {
5581 struct chanData *channel;
5582 unsigned int matches = 0;
5583
5584 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5585 {
5586 if(!chanserv_channel_match(channel, search))
5587 continue;
5588 matches++;
5589 smf(channel, data);
5590 }
5591
5592 return matches;
5593 }
5594
5595 static void
5596 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5597 {
5598 }
5599
5600 static void
5601 search_print(struct chanData *channel, void *data)
5602 {
5603 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5604 }
5605
5606 static CHANSERV_FUNC(cmd_search)
5607 {
5608 search_t search;
5609 unsigned int matches;
5610 channel_search_func action;
5611
5612 REQUIRE_PARAMS(3);
5613
5614 if(!irccasecmp(argv[1], "count"))
5615 action = search_count;
5616 else if(!irccasecmp(argv[1], "print"))
5617 action = search_print;
5618 else
5619 {
5620 reply("CSMSG_ACTION_INVALID", argv[1]);
5621 return 0;
5622 }
5623
5624 search = chanserv_search_create(cmd, user, argc - 2, argv + 2);
5625 if(!search)
5626 return 0;
5627
5628 if(action == search_count)
5629 search->limit = INT_MAX;
5630
5631 if(action == search_print)
5632 {
5633 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5634 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
5635 reply("CSMSG_BAR");
5636 }
5637
5638 matches = chanserv_channel_search(search, action, user);
5639
5640 if(matches)
5641 reply("MSG_MATCH_COUNT", matches);
5642 else
5643 reply("MSG_NO_MATCHES");
5644
5645 free(search);
5646 return 1;
5647 }
5648
5649 static CHANSERV_FUNC(cmd_unvisited)
5650 {
5651 struct chanData *cData;
5652 time_t interval = chanserv_conf.channel_expire_delay;
5653 char buffer[INTERVALLEN];
5654 unsigned int limit = 25, matches = 0;
5655
5656 if(argc > 1)
5657 {
5658 interval = ParseInterval(argv[1]);
5659 if(argc > 2)
5660 limit = atoi(argv[2]);
5661 }
5662
5663 intervalString(buffer, interval, user->handle_info);
5664 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5665
5666 for(cData = channelList; cData && matches < limit; cData = cData->next)
5667 {
5668 if((now - cData->visited) < interval)
5669 continue;
5670
5671 intervalString(buffer, now - cData->visited, user->handle_info);
5672 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5673 matches++;
5674 }
5675
5676 return 1;
5677 }
5678
5679 static MODCMD_FUNC(chan_opt_defaulttopic)
5680 {
5681 if(argc > 1)
5682 {
5683 char *topic;
5684
5685 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5686 {
5687 reply("CSMSG_TOPIC_LOCKED", channel->name);
5688 return 0;
5689 }
5690
5691 topic = unsplit_string(argv+1, argc-1, NULL);
5692
5693 free(channel->channel_info->topic);
5694 if(topic[0] == '*' && topic[1] == 0)
5695 {
5696 topic = channel->channel_info->topic = NULL;
5697 }
5698 else
5699 {
5700 topic = channel->channel_info->topic = strdup(topic);
5701 if(channel->channel_info->topic_mask
5702 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5703 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5704 }
5705 SetChannelTopic(channel, chanserv, user, topic ? topic : "", 1);
5706 }
5707
5708 if(channel->channel_info->topic)
5709 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5710 else
5711 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5712 return 1;
5713 }
5714
5715 static MODCMD_FUNC(chan_opt_topicmask)
5716 {
5717 if(argc > 1)
5718 {
5719 struct chanData *cData = channel->channel_info;
5720 char *mask;
5721
5722 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5723 {
5724 reply("CSMSG_TOPIC_LOCKED", channel->name);
5725 return 0;
5726 }
5727
5728 mask = unsplit_string(argv+1, argc-1, NULL);
5729
5730 if(cData->topic_mask)
5731 free(cData->topic_mask);
5732 if(mask[0] == '*' && mask[1] == 0)
5733 {
5734 cData->topic_mask = 0;
5735 }
5736 else
5737 {
5738 cData->topic_mask = strdup(mask);
5739 if(!cData->topic)
5740 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5741 else if(!match_ircglob(cData->topic, cData->topic_mask))
5742 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5743 }
5744 }
5745
5746 if(channel->channel_info->topic_mask)
5747 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5748 else
5749 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5750 return 1;
5751 }
5752
5753 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5754 {
5755 if(argc > 1)
5756 {
5757 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5758 char *previous;
5759
5760 previous = *data;
5761 if(greeting[0] == '*' && greeting[1] == 0)
5762 *data = NULL;
5763 else
5764 {
5765 unsigned int length = strlen(greeting);
5766 if(length > chanserv_conf.greeting_length)
5767 {
5768 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5769 return 0;
5770 }
5771 *data = strdup(greeting);
5772 }
5773 if(previous)
5774 free(previous);
5775 }
5776
5777 if(*data)
5778 reply(name, *data);
5779 else
5780 reply(name, user_find_message(user, "MSG_NONE"));
5781 return 1;
5782 }
5783
5784 static MODCMD_FUNC(chan_opt_greeting)
5785 {
5786 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5787 }
5788
5789 static MODCMD_FUNC(chan_opt_usergreeting)
5790 {
5791 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5792 }
5793
5794 static MODCMD_FUNC(chan_opt_modes)
5795 {
5796 struct mod_chanmode *new_modes;
5797 char modes[MODELEN];
5798
5799 if(argc > 1)
5800 {
5801 if (checkDefCon(DEFCON_NO_MODE_CHANGE) && !IsOper(user)) {
5802 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
5803 return 0;
5804 }
5805
5806 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5807 {
5808 reply("CSMSG_NO_ACCESS");
5809 return 0;
5810 }
5811 if(argv[1][0] == '*' && argv[1][1] == 0)
5812 {
5813 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5814 }
5815 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1,MCP_KEY_FREE|MCP_REGISTERED, 0)))
5816 {
5817 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5818 return 0;
5819 }
5820 else if(new_modes->argc > 1)
5821 {
5822 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5823 mod_chanmode_free(new_modes);
5824 return 0;
5825 }
5826 else
5827 {
5828 channel->channel_info->modes = *new_modes;
5829 modcmd_chanmode_announce(new_modes);
5830 mod_chanmode_free(new_modes);
5831 }
5832 }
5833
5834 mod_chanmode_format(&channel->channel_info->modes, modes);
5835 if(modes[0])
5836 reply("CSMSG_SET_MODES", modes);
5837 else
5838 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5839 return 1;
5840 }
5841
5842 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5843 static int
5844 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5845 {
5846 struct chanData *cData = channel->channel_info;
5847 int value;
5848
5849 if(argc > 1)
5850 {
5851 /* Set flag according to value. */
5852 if(enabled_string(argv[1]))
5853 {
5854 cData->flags |= mask;
5855 value = 1;
5856 }
5857 else if(disabled_string(argv[1]))
5858 {
5859 cData->flags &= ~mask;
5860 value = 0;
5861 }
5862 else
5863 {
5864 reply("MSG_INVALID_BINARY", argv[1]);
5865 return 0;
5866 }
5867 }
5868 else
5869 {
5870 /* Find current option value. */
5871 value = (cData->flags & mask) ? 1 : 0;
5872 }
5873
5874 if(value)
5875 reply(name, user_find_message(user, "MSG_ON"));
5876 else
5877 reply(name, user_find_message(user, "MSG_OFF"));
5878 return 1;
5879 }
5880
5881 static MODCMD_FUNC(chan_opt_nodelete)
5882 {
5883 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5884 {
5885 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5886 return 0;
5887 }
5888
5889 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5890 }
5891
5892 static MODCMD_FUNC(chan_opt_dynlimit)
5893 {
5894 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5895 }
5896
5897 static MODCMD_FUNC(chan_opt_offchannel)
5898 {
5899 struct chanData *cData = channel->channel_info;
5900 int value;
5901
5902 if(argc > 1)
5903 {
5904 /* Set flag according to value. */
5905 if(enabled_string(argv[1]))
5906 {
5907 if(!IsOffChannel(cData))
5908 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5909 cData->flags |= CHANNEL_OFFCHANNEL;
5910 value = 1;
5911 }
5912 else if(disabled_string(argv[1]))
5913 {
5914 if(IsOffChannel(cData))
5915 {
5916 struct mod_chanmode change;
5917 mod_chanmode_init(&change);
5918 change.argc = 1;
5919 change.args[0].mode = MODE_CHANOP;
5920 change.args[0].u.member = AddChannelUser(chanserv, channel);
5921 mod_chanmode_announce(chanserv, channel, &change);
5922 }
5923 cData->flags &= ~CHANNEL_OFFCHANNEL;
5924 value = 0;
5925 }
5926 else
5927 {
5928 reply("MSG_INVALID_BINARY", argv[1]);
5929 return 0;
5930 }
5931 }
5932 else
5933 {
5934 /* Find current option value. */
5935 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5936 }
5937
5938 if(value)
5939 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5940 else
5941 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5942 return 1;
5943 }
5944
5945 static MODCMD_FUNC(chan_opt_defaults)
5946 {
5947 struct userData *uData;
5948 struct chanData *cData;
5949 const char *confirm;
5950 enum levelOption lvlOpt;
5951 enum charOption chOpt;
5952
5953 cData = channel->channel_info;
5954 uData = GetChannelUser(cData, user->handle_info);
5955 if(!uData || (uData->access < UL_OWNER))
5956 {
5957 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5958 return 0;
5959 }
5960 confirm = make_confirmation_string(uData);
5961 if((argc < 2) || strcmp(argv[1], confirm))
5962 {
5963 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5964 return 0;
5965 }
5966 cData->flags = CHANNEL_DEFAULT_FLAGS;
5967 cData->modes = chanserv_conf.default_modes;
5968 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5969 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5970 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5971 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5972 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5973 return 1;
5974 }
5975
5976 static int
5977 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5978 {
5979 struct chanData *cData = channel->channel_info;
5980 struct userData *uData;
5981 unsigned short value;
5982
5983 if(argc > 1)
5984 {
5985 if(!check_user_level(channel, user, option, 1, 1))
5986 {
5987 reply("CSMSG_CANNOT_SET");
5988 return 0;
5989 }
5990 value = user_level_from_name(argv[1], UL_OWNER+1);
5991 if(!value && strcmp(argv[1], "0"))
5992 {
5993 reply("CSMSG_INVALID_ACCESS", argv[1]);
5994 return 0;
5995 }
5996 uData = GetChannelUser(cData, user->handle_info);
5997 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5998 {
5999 reply("CSMSG_BAD_SETLEVEL");
6000 return 0;
6001 }
6002 switch(option)
6003 {
6004 case lvlSetters:
6005 /* This test only applies to owners, since non-owners
6006 * trying to set an option to above their level get caught
6007 * by the CSMSG_BAD_SETLEVEL test above.
6008 */
6009 if(value > uData->access)
6010 {
6011 reply("CSMSG_BAD_SETTERS");
6012 return 0;
6013 }
6014 break;
6015 default:
6016 break;
6017 }
6018 cData->lvlOpts[option] = value;
6019 }
6020 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
6021 return argc > 1;
6022 }
6023
6024 static MODCMD_FUNC(chan_opt_enfops)
6025 {
6026 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
6027 }
6028
6029 static MODCMD_FUNC(chan_opt_enfhalfops)
6030 {
6031 return channel_level_option(lvlEnfHalfOps, CSFUNC_ARGS);
6032 }
6033 static MODCMD_FUNC(chan_opt_enfmodes)
6034 {
6035 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
6036 }
6037
6038 static MODCMD_FUNC(chan_opt_enftopic)
6039 {
6040 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
6041 }
6042
6043 static MODCMD_FUNC(chan_opt_pubcmd)
6044 {
6045 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
6046 }
6047
6048 static MODCMD_FUNC(chan_opt_setters)
6049 {
6050 return channel_level_option(lvlSetters, CSFUNC_ARGS);
6051 }
6052
6053 static MODCMD_FUNC(chan_opt_userinfo)
6054 {
6055 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
6056 }
6057
6058 static MODCMD_FUNC(chan_opt_topicsnarf)
6059 {
6060 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6061 }
6062
6063 static MODCMD_FUNC(chan_opt_inviteme)
6064 {
6065 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6066 }
6067
6068 /* TODO: Make look like this when no args are
6069 * given:
6070 * -X3- -------------------------------
6071 * -X3- BanTimeout: Bans are removed:
6072 * -X3- ----- * indicates current -----
6073 * -X3- 0: [*] Never.
6074 * -X3- 1: [ ] After 10 minutes.
6075 * -X3- 2: [ ] After 2 hours.
6076 * -X3- 3: [ ] After 4 hours.
6077 * -X3- 4: [ ] After 24 hours.
6078 * -X3- 5: [ ] After one week.
6079 * -X3- ------------- End -------------
6080 */
6081 static int
6082 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6083 {
6084 struct chanData *cData = channel->channel_info;
6085 int count = charOptions[option].count, index;
6086
6087 if(argc > 1)
6088 {
6089 index = atoi(argv[1]);
6090
6091 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
6092 {
6093 reply("CSMSG_INVALID_NUMERIC", index);
6094 /* Show possible values. */
6095 for(index = 0; index < count; index++)
6096 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
6097 return 0;
6098 }
6099
6100 cData->chOpts[option] = charOptions[option].values[index].value;
6101 }
6102 else
6103 {
6104 /* Find current option value. */
6105 find_value:
6106 for(index = 0;
6107 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
6108 index++);
6109 if(index == count)
6110 {
6111 /* Somehow, the option value is corrupt; reset it to the default. */
6112 cData->chOpts[option] = charOptions[option].default_value;
6113 goto find_value;
6114 }
6115 }
6116
6117 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
6118 return 1;
6119 }
6120
6121 static MODCMD_FUNC(chan_opt_automode)
6122 {
6123 return channel_multiple_option(chAutomode, CSFUNC_ARGS);
6124 }
6125
6126 static MODCMD_FUNC(chan_opt_protect)
6127 {
6128 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6129 }
6130
6131 static MODCMD_FUNC(chan_opt_toys)
6132 {
6133 return channel_multiple_option(chToys, CSFUNC_ARGS);
6134 }
6135
6136 static MODCMD_FUNC(chan_opt_ctcpreaction)
6137 {
6138 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6139 }
6140
6141 static MODCMD_FUNC(chan_opt_bantimeout)
6142 {
6143 return channel_multiple_option(chBanTimeout, CSFUNC_ARGS);
6144 }
6145
6146 static MODCMD_FUNC(chan_opt_topicrefresh)
6147 {
6148 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6149 }
6150
6151 static MODCMD_FUNC(chan_opt_resync)
6152 {
6153 return channel_multiple_option(chResync, CSFUNC_ARGS);
6154 }
6155
6156 static struct svccmd_list set_shows_list;
6157
6158 static void
6159 handle_svccmd_unbind(struct svccmd *target) {
6160 unsigned int ii;
6161 for(ii=0; ii<set_shows_list.used; ++ii)
6162 if(target == set_shows_list.list[ii])
6163 set_shows_list.used = 0;
6164 }
6165
6166 static CHANSERV_FUNC(cmd_set)
6167 {
6168 struct svccmd *subcmd;
6169 char buf[MAXLEN];
6170 unsigned int ii;
6171
6172 /* Check if we need to (re-)initialize set_shows_list. */
6173 if(!set_shows_list.used)
6174 {
6175 if(!set_shows_list.size)
6176 {
6177 set_shows_list.size = chanserv_conf.set_shows->used;
6178 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6179 }
6180 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6181 {
6182 const char *name = chanserv_conf.set_shows->list[ii];
6183 sprintf(buf, "%s %s", argv[0], name);
6184 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6185 if(!subcmd)
6186 {
6187 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6188 continue;
6189 }
6190 svccmd_list_append(&set_shows_list, subcmd);
6191 }
6192 }
6193
6194 if(argc < 2)
6195 {
6196 reply("CSMSG_CHANNEL_OPTIONS", channel->name);
6197 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
6198 reply("CSMSG_BAR");
6199 for(ii = 0; ii < set_shows_list.used; ii++)
6200 {
6201 subcmd = set_shows_list.list[ii];
6202 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6203 }
6204 reply("CSMSG_CHANNEL_OPTIONS_END");
6205 return 1;
6206 }
6207
6208 sprintf(buf, "%s %s", argv[0], argv[1]);
6209 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6210 if(!subcmd)
6211 {
6212 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6213 return 0;
6214 }
6215 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6216 {
6217 reply("CSMSG_NO_ACCESS");
6218 return 0;
6219 }
6220
6221 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6222 }
6223
6224 static int
6225 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6226 {
6227 struct userData *uData;
6228
6229 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6230 if(!uData)
6231 {
6232 reply("CSMSG_NOT_USER", channel->name);
6233 return 0;
6234 }
6235
6236 if(argc < 2)
6237 {
6238 /* Just show current option value. */
6239 }
6240 else if(enabled_string(argv[1]))
6241 {
6242 uData->flags |= mask;
6243 }
6244 else if(disabled_string(argv[1]))
6245 {
6246 uData->flags &= ~mask;
6247 }
6248 else
6249 {
6250 reply("MSG_INVALID_BINARY", argv[1]);
6251 return 0;
6252 }
6253
6254 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6255 return 1;
6256 }
6257
6258 static MODCMD_FUNC(user_opt_autoop)
6259 {
6260 struct userData *uData;
6261
6262 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6263 if(!uData)
6264 {
6265 reply("CSMSG_NOT_USER", channel->name);
6266 return 0;
6267 }
6268 if(uData->access < UL_HALFOP /*channel->channel_info->lvlOpts[lvlGiveOps]*/)
6269 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6270 else
6271 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6272 }
6273
6274 static MODCMD_FUNC(user_opt_autoinvite)
6275 {
6276 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6277 }
6278
6279 static MODCMD_FUNC(user_opt_autojoin)
6280 {
6281 return user_binary_option("CSMSG_USET_AUTOJOIN", USER_AUTO_JOIN, CSFUNC_ARGS);
6282 }
6283
6284 static MODCMD_FUNC(user_opt_info)
6285 {
6286 struct userData *uData;
6287 char *infoline;
6288
6289 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6290
6291 if(!uData)
6292 {
6293 /* If they got past the command restrictions (which require access)
6294 * but fail this test, we have some fool with security override on.
6295 */
6296 reply("CSMSG_NOT_USER", channel->name);
6297 return 0;
6298 }
6299
6300 if(argc > 1)
6301 {
6302 size_t bp;
6303 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6304 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6305 {
6306 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6307 return 0;
6308 }
6309 bp = strcspn(infoline, "\001");
6310 if(infoline[bp])
6311 {
6312 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6313 return 0;
6314 }
6315 if(uData->info)
6316 free(uData->info);
6317 if(infoline[0] == '*' && infoline[1] == 0)
6318 uData->info = NULL;
6319 else
6320 uData->info = strdup(infoline);
6321 }
6322 if(uData->info)
6323 reply("CSMSG_USET_INFO", uData->info);
6324 else
6325 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6326 return 1;
6327 }
6328
6329 struct svccmd_list uset_shows_list;
6330
6331 static CHANSERV_FUNC(cmd_uset)
6332 {
6333 struct svccmd *subcmd;
6334 char buf[MAXLEN];
6335 unsigned int ii;
6336
6337 /* Check if we need to (re-)initialize uset_shows_list. */
6338 if(!uset_shows_list.used)
6339 {
6340 char *options[] =
6341 {
6342 "AutoOp", "AutoInvite", "AutoJoin", "Info"
6343 };
6344
6345 if(!uset_shows_list.size)
6346 {
6347 uset_shows_list.size = ArrayLength(options);
6348 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6349 }
6350 for(ii = 0; ii < ArrayLength(options); ii++)
6351 {
6352 const char *name = options[ii];
6353 sprintf(buf, "%s %s", argv[0], name);
6354 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6355 if(!subcmd)
6356 {
6357 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6358 continue;
6359 }
6360 svccmd_list_append(&uset_shows_list, subcmd);
6361 }
6362 }
6363
6364 if(argc < 2)
6365 {
6366 /* Do this so options are presented in a consistent order. */
6367 reply("CSMSG_USER_OPTIONS");
6368 for(ii = 0; ii < uset_shows_list.used; ii++)
6369 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6370 return 1;
6371 }
6372
6373 sprintf(buf, "%s %s", argv[0], argv[1]);
6374 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6375 if(!subcmd)
6376 {
6377 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6378 return 0;
6379 }
6380
6381 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6382 }
6383
6384 static CHANSERV_FUNC(cmd_giveownership)
6385 {
6386 struct handle_info *new_owner_hi;
6387 struct userData *new_owner, *curr_user;
6388 struct chanData *cData = channel->channel_info;
6389 struct do_not_register *dnr;
6390 struct giveownership *giveownership;
6391 unsigned int force, override;
6392 unsigned short co_access, new_owner_old_access;
6393 char transfer_reason[MAXLEN];
6394
6395 REQUIRE_PARAMS(2);
6396 curr_user = GetChannelAccess(cData, user->handle_info);
6397 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6398
6399 struct userData *uData = _GetChannelUser(channel->channel_info, user->handle_info, 1, 0);
6400 override = ((cmd->effective_flags & MODCMD_REQUIRE_CHANUSER)
6401 && (uData->access > 500)
6402 && (!(uData = _GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
6403 || uData->access < 500));
6404
6405
6406 if(!curr_user || (curr_user->access != UL_OWNER))
6407 {
6408 struct userData *owner = NULL;
6409 for(curr_user = channel->channel_info->users;
6410 curr_user;
6411 curr_user = curr_user->next)
6412 {
6413 if(curr_user->access != UL_OWNER)
6414 continue;
6415 if(owner)
6416 {
6417 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6418 return 0;
6419 }
6420 owner = curr_user;
6421 }
6422 curr_user = owner;
6423 }
6424 else if (!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
6425 {
6426 char delay[INTERVALLEN];
6427 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6428 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6429 return 0;
6430 }
6431 if (!curr_user) {
6432 reply("CSMSG_NO_OWNER", channel->name);
6433 return 0;
6434 }
6435 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6436 return 0;
6437 if(new_owner_hi == user->handle_info)
6438 {
6439 reply("CSMSG_NO_TRANSFER_SELF");
6440 return 0;
6441 }
6442 new_owner = GetChannelAccess(cData, new_owner_hi);
6443 if(!new_owner)
6444 {
6445 if(force)
6446 {
6447 new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
6448 }
6449 else
6450 {
6451 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6452 return 0;
6453 }
6454 }
6455 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6456 {
6457 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6458 return 0;
6459 }
6460 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6461 if(!IsHelping(user))
6462 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6463 else
6464 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6465 return 0;
6466 }
6467
6468 new_owner_old_access = new_owner->access;
6469 if(new_owner->access >= UL_COOWNER)
6470 co_access = new_owner->access;
6471 else
6472 co_access = UL_COOWNER;
6473 new_owner->access = UL_OWNER;
6474 if(curr_user)
6475 curr_user->access = co_access;
6476 cData->ownerTransfer = now;
6477
6478 giveownership = calloc(1, sizeof(*giveownership));
6479 giveownership->issued = now;
6480 giveownership->old_owner = curr_user->handle->handle;
6481 giveownership->target = new_owner_hi->handle;
6482 giveownership->target_access = new_owner_old_access;
6483 if(override)
6484 {
6485 if(argc > (2 + force))
6486 {
6487 unsplit_string(argv + 2 + force, argc - 2 - force, transfer_reason);
6488 giveownership->reason = strdup(transfer_reason);
6489 }
6490 giveownership->staff_issuer = strdup(user->handle_info->handle);
6491 }
6492
6493 giveownership->previous = channel->channel_info->giveownership;
6494 channel->channel_info->giveownership = giveownership;
6495
6496 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6497 global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "CSMSG_OWNERSHIP_TRANSFERRED",
6498 channel->name, new_owner_hi->handle, user->handle_info->handle);
6499 return 1;
6500 }
6501
6502 static void
6503 chanserv_expire_user_suspension(void *data)
6504 {
6505 struct userData *target = data;
6506
6507 target->expires = 0;
6508 target->flags &= ~USER_SUSPENDED;
6509 }
6510
6511 static CHANSERV_FUNC(cmd_suspend)
6512 {
6513 struct handle_info *hi;
6514 struct userData *self, *target;
6515 time_t expiry;
6516
6517 REQUIRE_PARAMS(3);
6518 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6519 self = GetChannelUser(channel->channel_info, user->handle_info);
6520 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6521 {
6522 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6523 return 0;
6524 }
6525 if(target->access >= self->access)
6526 {
6527 reply("MSG_USER_OUTRANKED", hi->handle);
6528 return 0;
6529 }
6530 if(target->flags & USER_SUSPENDED)
6531 {
6532 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6533 return 0;
6534 }
6535 if(target->present)
6536 {
6537 target->present = 0;
6538 target->seen = now;
6539 }
6540 if(!strcmp(argv[2], "0"))
6541 expiry = 0;
6542 else
6543 {
6544 unsigned int duration;
6545 if(!(duration = ParseInterval(argv[2])))
6546 {
6547 reply("MSG_INVALID_DURATION", argv[2]);
6548 return 0;
6549 }
6550 expiry = now + duration;
6551 }
6552
6553 target->expires = expiry;
6554
6555 if(target->expires)
6556 timeq_add(target->expires, chanserv_expire_user_suspension, target);
6557
6558 target->flags |= USER_SUSPENDED;
6559 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6560 return 1;
6561 }
6562
6563 static CHANSERV_FUNC(cmd_unsuspend)
6564 {
6565 struct handle_info *hi;
6566 struct userData *self, *target;
6567
6568 REQUIRE_PARAMS(2);
6569 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6570 self = GetChannelUser(channel->channel_info, user->handle_info);
6571 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6572 {
6573 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6574 return 0;
6575 }
6576 if(target->access >= self->access)
6577 {
6578 reply("MSG_USER_OUTRANKED", hi->handle);
6579 return 0;
6580 }
6581 if(!(target->flags & USER_SUSPENDED))
6582 {
6583 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6584 return 0;
6585 }
6586 target->flags &= ~USER_SUSPENDED;
6587 scan_user_presence(target, NULL);
6588 timeq_del(target->expires, chanserv_expire_user_suspension, target, 0);
6589 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6590 return 1;
6591 }
6592
6593 static MODCMD_FUNC(cmd_deleteme)
6594 {
6595 struct handle_info *hi;
6596 struct userData *target;
6597 const char *confirm_string;
6598 unsigned short access;
6599 char *channel_name;
6600
6601 hi = user->handle_info;
6602 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6603 {
6604 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6605 return 0;
6606 }
6607 if(target->access == UL_OWNER)
6608 {
6609 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6610 return 0;
6611 }
6612 confirm_string = make_confirmation_string(target);
6613 if((argc < 2) || strcmp(argv[1], confirm_string))
6614 {
6615 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6616 return 0;
6617 }
6618 access = target->access;
6619 channel_name = strdup(channel->name);
6620 del_channel_user(target, 1);
6621 reply("CSMSG_DELETED_YOU", access, channel_name);
6622 free(channel_name);
6623 return 1;
6624 }
6625
6626 static void
6627 chanserv_refresh_topics(UNUSED_ARG(void *data))
6628 {
6629 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
6630 struct chanData *cData;
6631 char opt;
6632
6633 for(cData = channelList; cData; cData = cData->next)
6634 {
6635 if(IsSuspended(cData))
6636 continue;
6637 opt = cData->chOpts[chTopicRefresh];
6638 if(opt == 'n')
6639 continue;
6640 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6641 continue;
6642 if(cData->topic)
6643 SetChannelTopic(cData->channel, chanserv, chanserv, cData->topic, 1);
6644 cData->last_refresh = refresh_num;
6645 }
6646 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6647 }
6648
6649 static void
6650 chanserv_auto_resync(UNUSED_ARG(void *data))
6651 {
6652 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
6653 struct chanData *cData;
6654 char opt;
6655
6656 for(cData = channelList; cData; cData = cData->next)
6657 {
6658 if(IsSuspended(cData)) continue;
6659 opt = cData->chOpts[chResync];
6660 if(opt == 'n') continue;
6661 if((refresh_num - cData->last_resync) < (unsigned int)(1 << (opt - '1'))) continue;
6662 resync_channel(cData->channel);
6663 cData->last_resync = refresh_num;
6664 }
6665 timeq_add(now + chanserv_conf.refresh_period, chanserv_auto_resync, NULL);
6666 }
6667
6668 static CHANSERV_FUNC(cmd_unf)
6669 {
6670 if(channel)
6671 {
6672 char response[MAXLEN];
6673 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6674 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6675 irc_privmsg(cmd->parent->bot, channel->name, response);
6676 }
6677 else
6678 reply("CSMSG_UNF_RESPONSE");
6679 return 1;
6680 }
6681
6682 static CHANSERV_FUNC(cmd_ping)
6683 {
6684 if(channel)
6685 {
6686 char response[MAXLEN];
6687 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6688 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6689 irc_privmsg(cmd->parent->bot, channel->name, response);
6690 }
6691 else
6692 reply("CSMSG_PING_RESPONSE");
6693 return 1;
6694 }
6695
6696 static CHANSERV_FUNC(cmd_wut)
6697 {
6698 if(channel)
6699 {
6700 char response[MAXLEN];
6701 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6702 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6703 irc_privmsg(cmd->parent->bot, channel->name, response);
6704 }
6705 else
6706 reply("CSMSG_WUT_RESPONSE");
6707 return 1;
6708 }
6709
6710 #ifdef lame8ball
6711 static CHANSERV_FUNC(cmd_8ball)
6712 {
6713 unsigned int i, j, accum;
6714 const char *resp;
6715
6716 REQUIRE_PARAMS(2);
6717 accum = 0;
6718 for(i=1; i<argc; i++)
6719 for(j=0; argv[i][j]; j++)
6720 accum = (accum << 5) - accum + toupper(argv[i][j]);
6721 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6722 if(channel)
6723 {
6724 char response[MAXLEN];
6725 sprintf(response, "\ 2%s\ 2: %s", user->nick, resp);
6726 irc_privmsg(cmd->parent->bot, channel->name, response);
6727 }
6728 else
6729 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6730 return 1;
6731 }
6732
6733 #else /* Use cool 8ball instead */
6734
6735 void eightball(char *outcome, int method, unsigned int seed)
6736 {
6737 int answer = 0;
6738
6739 #define NUMOFCOLORS 18
6740 char ballcolors[50][50] = {"blue", "red", "green", "yellow",
6741 "white", "black", "grey", "brown",
6742 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6743 "fuchsia","turquoise","magenta", "cyan"};
6744 #define NUMOFLOCATIONS 50
6745 char balllocations[50][55] = {
6746 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6747 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6748 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6749 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6750 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6751 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6752 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6753 "your bra", "your hair", "your bed", "the couch", "the wall",
6754 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
6755 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
6756 #define NUMOFPREPS 15
6757 char ballpreps[50][50] = {
6758 "Near", "Somewhere near", "In", "In", "In",
6759 "In", "Hiding in", "Under", "Next to", "Over",
6760 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
6761 #define NUMOFNUMS 34
6762 char ballnums[50][50] = {
6763 "A hundred", "A thousand", "A few", "42",
6764 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
6765 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6766 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6767 };
6768 #define NUMOFMULTS 8
6769 char ballmults[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
6770
6771 /* Method:
6772 * 0: normal (Not used in x3)
6773 * 1: color
6774 * 2: where is
6775 * 3: how many
6776 */
6777
6778 srand(seed);
6779 if (method == 1) /* A Color */
6780 {
6781 char tmp[MAXLEN];
6782
6783 answer = (rand() % 12); /* Make sure this is the # of entries */
6784 switch(answer)
6785 {
6786 case 0: strcpy(tmp, "Very bright %s, I'd say.");
6787 break;
6788 case 1: strcpy(tmp, "Sort of a light %s color.");
6789 break;
6790 case 2: strcpy(tmp, "Dark and dreary %s.");
6791 break;
6792 case 3: strcpy(tmp, "Quite a pale shade of %s.");
6793 break;
6794 case 4: strcpy(tmp, "A gross kind of mucky %s.");
6795 break;
6796 case 5: strcpy(tmp, "Brilliant whiteish %s.");
6797 break;
6798 case 6: case 7: case 8: case 9: strcpy(tmp, "%s.");
6799 break;
6800 case 10: strcpy(tmp, "Solid %s.");
6801 break;
6802 case 11: strcpy(tmp, "Transparent %s.");
6803 break;
6804 default: strcpy(outcome, "An invalid random number was generated.");
6805 return;
6806 }
6807 sprintf(outcome, tmp, ballcolors[rand() % NUMOFCOLORS]);
6808 return;
6809 }
6810 else if (method == 2) /* Location */
6811 {
6812 sprintf(outcome, "%s %s.", ballpreps[rand() % NUMOFPREPS], balllocations[rand() % NUMOFLOCATIONS]);
6813 }
6814 else if (method == 3) /* Number of ___ */
6815 {
6816 sprintf(outcome, "%s%s.", ballnums[rand() % NUMOFNUMS], ballmults[rand() % NUMOFMULTS]);
6817 }
6818 else
6819 {
6820 //Debug(DBGWARNING, "Error in 8ball.");
6821 }
6822 return;
6823 }
6824
6825 static CHANSERV_FUNC(cmd_8ball)
6826 {
6827 char *word1, *word2, *word3;
6828 static char eb[MAXLEN];
6829 unsigned int accum, i, j;
6830
6831 REQUIRE_PARAMS(2);
6832 accum = 0;
6833 for(i=1; i<argc; i++)
6834 for(j=0; argv[i][j]; j++)
6835 accum = (accum << 5) - accum + toupper(argv[i][j]);
6836
6837 accum += time(NULL)/3600;
6838 word1 = argv[1];
6839 word2 = argc>2?argv[2]:"";
6840 word3 = argc>3?argv[3]:"";
6841
6842 /*** COLOR *****/
6843 if((word2) && strcasecmp(word1, "what") == 0 && strcasecmp(word2, "color") == 0)
6844 eightball(eb, 1, accum);
6845 else if((word3) && strcasecmp(word1, "what's") == 0 && strcasecmp(word2, "the") == 0 && strcasecmp(word3, "color") == 0)
6846 eightball(eb, 1, accum);
6847 else if((word3) && strcasecmp(word1, "whats") == 0 && strcasecmp(word2, "the") == 0 && strcasecmp(word3, "color") == 0)
6848 eightball(eb, 1, accum);
6849 /*** LOCATION *****/
6850 else if(
6851 (
6852 word2 &&
6853 (
6854 (strcasecmp(word1, "where") == 0) &&
6855 (strcasecmp(word2, "is") == 0)
6856 )
6857 ) ||
6858 (
6859 strcasecmp(word1, "where's") == 0
6860 )
6861 )
6862 eightball(eb, 2, accum);
6863 /*** NUMBER *****/
6864 else if((word2) && strcasecmp(word1, "how") == 0 && strcasecmp(word2, "many") == 0)
6865 eightball(eb, 3, accum);
6866 /*** GENERIC *****/
6867 else
6868 {
6869 /* Generic 8ball question.. so pull from x3.conf srvx style */
6870 const char *resp;
6871
6872 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6873 if(channel)
6874 {
6875 char response[MAXLEN];
6876 sprintf(response, "\002%s\002: %s", user->nick, resp);
6877 irc_privmsg(cmd->parent->bot, channel->name, response);
6878 }
6879 else
6880 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6881 return 1;
6882 }
6883
6884 if(channel)
6885 {
6886 char response[MAXLEN];
6887 sprintf(response, "\002%s\002: %s", user->nick, eb);
6888 irc_privmsg(cmd->parent->bot, channel->name, response);
6889 }
6890 else
6891 send_message_type(4, user, cmd->parent->bot, "%s", eb);
6892 return 1;
6893 }
6894 #endif
6895
6896 static CHANSERV_FUNC(cmd_d)
6897 {
6898 unsigned long sides, count, modifier, ii, total;
6899 char response[MAXLEN], *sep;
6900 const char *fmt;
6901
6902 REQUIRE_PARAMS(2);
6903 if((count = strtoul(argv[1], &sep, 10)) < 1)
6904 goto no_dice;
6905 if(sep[0] == 0)
6906 {
6907 if(count == 1)
6908 goto no_dice;
6909 sides = count;
6910 count = 1;
6911 modifier = 0;
6912 }
6913 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6914 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6915 {
6916 if(sep[0] == 0)
6917 modifier = 0;
6918 else if((sep[0] == '-') && isdigit(sep[1]))
6919 modifier = strtoul(sep, NULL, 10);
6920 else if((sep[0] == '+') && isdigit(sep[1]))
6921 modifier = strtoul(sep+1, NULL, 10);
6922 else
6923 goto no_dice;
6924 }
6925 else
6926 {
6927 no_dice:
6928 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6929 return 0;
6930 }
6931 if(count > 10)
6932 {
6933 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6934 return 0;
6935 }
6936 for(total = ii = 0; ii < count; ++ii)
6937 total += (rand() % sides) + 1;
6938 total += modifier;
6939
6940 if((count > 1) || modifier)
6941 {
6942 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6943 sprintf(response, fmt, total, count, sides, modifier);
6944 }
6945 else
6946 {
6947 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6948 sprintf(response, fmt, total, sides);
6949 }
6950 if(channel)
6951 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6952 else
6953 send_message_type(4, user, cmd->parent->bot, "%s", response);
6954 return 1;
6955 }
6956
6957 static CHANSERV_FUNC(cmd_huggle)
6958 {
6959 /* CTCP must be via PRIVMSG, never notice */
6960 if(channel)
6961 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6962 else
6963 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6964 return 1;
6965 }
6966
6967 static CHANSERV_FUNC(cmd_calc)
6968 {
6969 char response[MAXLEN];
6970
6971 REQUIRE_PARAMS(2);
6972 do_math(response, unsplit_string(argv + 1, argc - 1, NULL));
6973
6974 if(channel)
6975 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6976 else
6977 send_message_type(4, user, cmd->parent->bot, "%s", response);
6978 return 1;
6979 }
6980
6981 static CHANSERV_FUNC(cmd_reply)
6982 {
6983
6984 REQUIRE_PARAMS(2);
6985 unsplit_string(argv + 1, argc - 1, NULL);
6986
6987 if(channel)
6988 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, unsplit_string(argv + 1, argc - 1, NULL));
6989 else
6990 send_message_type(4, user, cmd->parent->bot, "%s", unsplit_string(argv + 1, argc - 1, NULL));
6991 return 1;
6992 }
6993
6994 static void
6995 chanserv_adjust_limit(void *data)
6996 {
6997 struct mod_chanmode change;
6998 struct chanData *cData = data;
6999 struct chanNode *channel = cData->channel;
7000 unsigned int limit;
7001
7002 if(IsSuspended(cData))
7003 return;
7004
7005 cData->limitAdjusted = now;
7006 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7007 if(cData->modes.modes_set & MODE_LIMIT)
7008 {
7009 if(limit > cData->modes.new_limit)
7010 limit = cData->modes.new_limit;
7011 else if(limit == cData->modes.new_limit)
7012 return;
7013 }
7014
7015 mod_chanmode_init(&change);
7016 change.modes_set = MODE_LIMIT;
7017 change.new_limit = limit;
7018 mod_chanmode_announce(chanserv, channel, &change);
7019 }
7020
7021 static void
7022 handle_new_channel(struct chanNode *channel)
7023 {
7024 struct chanData *cData;
7025
7026 if(!(cData = channel->channel_info))
7027 return;
7028
7029 if(cData->modes.modes_set || cData->modes.modes_clear)
7030 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7031
7032 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7033 SetChannelTopic(channel, chanserv, chanserv, channel->channel_info->topic, 1);
7034 }
7035
7036 /* Welcome to my worst nightmare. Warning: Read (or modify)
7037 the code below at your own risk. */
7038 static int
7039 handle_join(struct modeNode *mNode)
7040 {
7041 struct mod_chanmode change;
7042 struct userNode *user = mNode->user;
7043 struct chanNode *channel = mNode->channel;
7044 struct chanData *cData;
7045 struct userData *uData = NULL;
7046 struct banData *bData;
7047 struct handle_info *handle;
7048 unsigned int modes = 0, info = 0;
7049 char *greeting;
7050
7051 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7052 return 0;
7053
7054 cData = channel->channel_info;
7055 if(channel->members.used > cData->max)
7056 cData->max = channel->members.used;
7057
7058 #ifdef notdef
7059 /* Check for bans. If they're joining through a ban, one of two
7060 * cases applies:
7061 * 1: Join during a netburst, by riding the break. Kick them
7062 * unless they have ops or voice in the channel.
7063 * 2: They're allowed to join through the ban (an invite in
7064 * ircu2.10, or a +e on Hybrid, or something).
7065 * If they're not joining through a ban, and the banlist is not
7066 * full, see if they're on the banlist for the channel. If so,
7067 * kickban them.
7068 */
7069 if(user->uplink->burst && !mNode->modes)
7070 {
7071 unsigned int ii;
7072 for(ii = 0; ii < channel->banlist.used; ii++)
7073 {
7074 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7075 {
7076 /* Riding a netburst. Naughty. */
7077 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7078 return 1;
7079 }
7080 }
7081 }
7082 #endif
7083
7084 if(user->handle_info)
7085 {
7086 handle = user->handle_info;
7087 if(handle)
7088 {
7089 uData = GetTrueChannelAccess(cData, handle);
7090 }
7091 }
7092
7093
7094
7095 mod_chanmode_init(&change);
7096 change.argc = 1;
7097
7098 /* TODO: maybe only people above inviteme level? -Rubin */
7099 /* We don't kick people with access */
7100 if(!uData)
7101 {
7102 if(channel->banlist.used < MAXBANS)
7103 {
7104 /* Not joining through a ban. */
7105 for(bData = cData->bans;
7106 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7107 bData = bData->next);
7108
7109 if(bData)
7110 {
7111 char kick_reason[MAXLEN];
7112 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7113
7114 bData->triggered = now;
7115 if(bData != cData->bans)
7116 {
7117 /* Shuffle the ban to the head of the list. */
7118 if(bData->next)
7119 bData->next->prev = bData->prev;
7120 if(bData->prev)
7121 bData->prev->next = bData->next;
7122
7123 bData->prev = NULL;
7124 bData->next = cData->bans;
7125
7126 if(cData->bans)
7127 cData->bans->prev = bData;
7128 cData->bans = bData;
7129 }
7130
7131 change.args[0].mode = MODE_BAN;
7132 change.args[0].u.hostmask = bData->mask;
7133 mod_chanmode_announce(chanserv, channel, &change);
7134 KickChannelUser(user, channel, chanserv, kick_reason);
7135 return 1;
7136 }
7137 }
7138 }
7139
7140 /* ChanServ will not modify the limits in join-flooded channels.
7141 It will also skip DynLimit processing when the user (or srvx)
7142 is bursting in, because there are likely more incoming. */
7143 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7144 && !user->uplink->burst
7145 && !channel->join_flooded
7146 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7147 {
7148 /* The user count has begun "bumping" into the channel limit,
7149 so set a timer to raise the limit a bit. Any previous
7150 timers are removed so three incoming users within the delay
7151 results in one limit change, not three. */
7152
7153 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7154 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7155 }
7156
7157 /* Give automodes exept during join-floods */
7158 if(!channel->join_flooded)
7159 {
7160 if(cData->chOpts[chAutomode] == 'v')
7161 modes |= MODE_VOICE;
7162 else if(cData->chOpts[chAutomode] == 'h')
7163 modes |= MODE_HALFOP;
7164 else if(cData->chOpts[chAutomode] == 'o')
7165 modes |= MODE_CHANOP;
7166 }
7167
7168 greeting = cData->greeting;
7169 if(user->handle_info)
7170 {
7171 /* handle = user->handle_info; */
7172
7173 if(IsHelper(user) && !IsHelping(user))
7174 {
7175 unsigned int ii;
7176 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7177 {
7178 if(channel == chanserv_conf.support_channels.list[ii])
7179 {
7180 HANDLE_SET_FLAG(user->handle_info, HELPING);
7181 break;
7182 }
7183 }
7184 }
7185
7186 /* uData = GetTrueChannelAccess(cData, handle); */
7187 if(uData && !IsUserSuspended(uData))
7188 {
7189 /* non users getting automodes are handled above. */
7190 if(IsUserAutoOp(uData) && cData->chOpts[chAutomode] != 'n')
7191 {
7192 /* just op everyone with access */
7193 if(uData->access >= UL_PEON && cData->chOpts[chAutomode] == 'l')
7194 modes |= MODE_VOICE;
7195 /* or do their access level */
7196 else if(uData->access >= UL_OP )
7197 modes |= MODE_CHANOP;
7198 else if(uData->access >= UL_HALFOP )
7199 modes |= MODE_HALFOP;
7200 else if(uData->access >= UL_PEON && cData->chOpts[chAutomode] != 'm')
7201 modes |= MODE_VOICE;
7202 }
7203 if(uData->access >= UL_PRESENT)
7204 cData->visited = now;
7205 if(cData->user_greeting)
7206 greeting = cData->user_greeting;
7207 if(uData->info
7208 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7209 && ((now - uData->seen) >= chanserv_conf.info_delay)
7210 && !uData->present)
7211 info = 1;
7212 uData->seen = now;
7213 uData->present = 1;
7214 }
7215 }
7216
7217 /* If user joining normally (not during burst), apply op or voice,
7218 * and send greeting/userinfo as appropriate.
7219 */
7220 if(!user->uplink->burst)
7221 {
7222 if(modes)
7223 {
7224 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
7225 if(modes & MODE_CHANOP) {
7226 modes &= ~MODE_HALFOP;
7227 modes &= ~MODE_VOICE;
7228 }
7229 */
7230 change.args[0].mode = modes;
7231 change.args[0].u.member = mNode;
7232 mod_chanmode_announce(chanserv, channel, &change);
7233 }
7234 if(greeting)
7235 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7236 if(uData && info)
7237 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7238 }
7239 return 0;
7240 }
7241
7242 static void
7243 chanserv_autojoin_channels(struct userNode *user)
7244 {
7245 struct userData *channel;
7246
7247 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7248 {
7249 struct chanNode *cn;
7250 struct modeNode *mn;
7251
7252 if(IsUserSuspended(channel)
7253 || IsSuspended(channel->channel)
7254 || !(cn = channel->channel->channel))
7255 continue;
7256
7257 mn = GetUserMode(cn, user);
7258 if(!mn)
7259 {
7260 if(!IsUserSuspended(channel)
7261 && IsUserAutoJoin(channel)
7262 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7263 && !self->burst
7264 && !user->uplink->burst)
7265 irc_svsjoin(chanserv, user, cn);
7266 }
7267 }
7268 }
7269
7270 static void
7271 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7272 {
7273 struct mod_chanmode change;
7274 struct userData *channel;
7275 unsigned int ii, jj, i;
7276
7277 if(!user->handle_info)
7278 return;
7279
7280 mod_chanmode_init(&change);
7281 change.argc = 1;
7282 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7283 {
7284 struct chanNode *cn;
7285 struct chanData *cData;
7286 struct modeNode *mn;
7287 if(IsUserSuspended(channel)
7288 || IsSuspended(channel->channel)
7289 || !(cn = channel->channel->channel))
7290 continue;
7291
7292 cData = cn->channel_info;
7293 mn = GetUserMode(cn, user);
7294 if(!mn)
7295 {
7296 if(!IsUserSuspended(channel)
7297 && IsUserAutoInvite(channel)
7298 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7299 && !self->burst
7300 && !user->uplink->burst)
7301 irc_invite(chanserv, user, cn);
7302 continue;
7303 }
7304
7305 if(channel->access >= UL_PRESENT)
7306 channel->channel->visited = now;
7307
7308 if(IsUserAutoOp(channel) && cData->chOpts[chAutomode] != 'n')
7309 {
7310 if(channel->access >= UL_OP )
7311 change.args[0].mode = MODE_CHANOP;
7312 else if(channel->access >= UL_HALFOP )
7313 change.args[0].mode = MODE_HALFOP;
7314 else if(channel->access >= UL_PEON )
7315 change.args[0].mode = MODE_VOICE;
7316 else
7317 change.args[0].mode = 0;
7318 change.args[0].u.member = mn;
7319 if(change.args[0].mode)
7320 mod_chanmode_announce(chanserv, cn, &change);
7321 }
7322
7323 channel->seen = now;
7324 channel->present = 1;
7325 }
7326
7327 for(ii = 0; ii < user->channels.used; ++ii)
7328 {
7329 struct chanNode *channel = user->channels.list[ii]->channel;
7330 struct banData *ban;
7331
7332 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE))
7333 || !channel->channel_info
7334 || IsSuspended(channel->channel_info))
7335 continue;
7336 if(protect_user(user, chanserv, channel->channel_info, true))
7337 continue;
7338 for(jj = 0; jj < channel->banlist.used; ++jj)
7339 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7340 break;
7341 if(jj < channel->banlist.used)
7342 continue;
7343 for(ban = channel->channel_info->bans; ban; ban = ban->next)
7344 {
7345 char kick_reason[MAXLEN];
7346 if(!user_matches_glob(user, ban->mask,MATCH_USENICK | MATCH_VISIBLE))
7347 continue;
7348 change.args[0].mode = MODE_BAN;
7349 change.args[0].u.hostmask = ban->mask;
7350 mod_chanmode_announce(chanserv, channel, &change);
7351 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7352 KickChannelUser(user, channel, chanserv, kick_reason);
7353 ban->triggered = now;
7354 break;
7355 }
7356 }
7357
7358 if(IsSupportHelper(user))
7359 {
7360 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7361 {
7362 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7363 {
7364 HANDLE_SET_FLAG(user->handle_info, HELPING);
7365 break;
7366 }
7367 }
7368 }
7369
7370 if (user->handle_info->ignores->used) {
7371 for (i=0; i < user->handle_info->ignores->used; i++) {
7372 irc_silence(user, user->handle_info->ignores->list[i], 1);
7373 }
7374 }
7375
7376 if (user->handle_info->epithet)
7377 irc_swhois(chanserv, user, user->handle_info->epithet);
7378
7379 /* process autojoin channels 5 seconds later as this sometimes
7380 happens before autohide */
7381 // timeq_add(now + 5, chanserv_autojoin_channels, user);
7382 chanserv_autojoin_channels(user);
7383 }
7384
7385 static void
7386 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7387 {
7388 struct chanData *cData;
7389 struct userData *uData;
7390
7391 cData = mn->channel->channel_info;
7392 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7393 return;
7394
7395 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7396 {
7397 /* Allow for a bit of padding so that the limit doesn't
7398 track the user count exactly, which could get annoying. */
7399 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7400 {
7401 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7402 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7403 }
7404 }
7405
7406 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7407 {
7408 scan_user_presence(uData, mn->user);
7409 uData->seen = now;
7410 }
7411
7412 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7413 {
7414 unsigned int ii, jj;
7415 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7416 {
7417 for(jj = 0; jj < mn->user->channels.used; ++jj)
7418 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
7419 break;
7420 if(jj < mn->user->channels.used)
7421 break;
7422 }
7423 if(ii == chanserv_conf.support_channels.used)
7424 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7425 }
7426 }
7427
7428 static void
7429 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7430 {
7431 struct userData *uData;
7432
7433 if(!channel->channel_info || !kicker || IsService(kicker)
7434 || (kicker == victim) || IsSuspended(channel->channel_info)
7435 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7436 return;
7437
7438 if(protect_user(victim, kicker, channel->channel_info, false))
7439 {
7440 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_KICK");
7441 KickChannelUser(kicker, channel, chanserv, reason);
7442 }
7443
7444 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7445 uData->seen = now;
7446 }
7447
7448 static int
7449 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7450 {
7451 struct chanData *cData;
7452
7453 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7454 return 0;
7455
7456 cData = channel->channel_info;
7457 if(bad_topic(channel, user, channel->topic))
7458 { /* User doesnt have privs to set topics. Undo it */
7459 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7460 SetChannelTopic(channel, chanserv, chanserv, old_topic, 1);
7461 return 1;
7462 }
7463 /* If there is a topic mask set, and the new topic doesnt match,
7464 * set the topic to mask + new_topic */
7465 if(cData->topic_mask && !match_ircglob(channel->topic, cData->topic_mask))
7466 {
7467 char new_topic[TOPICLEN+1];
7468 conform_topic(cData->topic_mask, channel->topic, new_topic);
7469 if(*new_topic)
7470 {
7471 SetChannelTopic(channel, chanserv, user, new_topic, 1);
7472 /* and fall through to topicsnarf code below.. */
7473 }
7474 else /* Topic couldnt fit into mask, was too long */
7475 {
7476 SetChannelTopic(channel, chanserv, user, old_topic, 1);
7477 send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT1", channel->name, cData->topic_mask);
7478 send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
7479 return 1;
7480 }
7481 }
7482 /* With topicsnarf, grab the topic and save it as the default topic. */
7483 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7484 {
7485 free(cData->topic);
7486 cData->topic = strdup(channel->topic);
7487 }
7488 return 0;
7489 }
7490
7491 static void
7492 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7493 {
7494 struct mod_chanmode *bounce = NULL;
7495 unsigned int bnc, ii;
7496 char deopped = 0;
7497
7498 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7499 return;
7500
7501 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7502 && mode_lock_violated(&channel->channel_info->modes, change))
7503 {
7504 char correct[MAXLEN];
7505 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7506 mod_chanmode_format(&channel->channel_info->modes, correct);
7507 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7508 }
7509 for(ii = bnc = 0; ii < change->argc; ++ii)
7510 {
7511 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7512 {
7513 const struct userNode *victim = change->args[ii].u.member->user;
7514 if(!protect_user(victim, user, channel->channel_info, false))
7515 continue;
7516 if(!bounce)
7517 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7518 if(!deopped)
7519 {
7520 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7521 bounce->args[bnc].u.member = GetUserMode(channel, user);
7522 if(bounce->args[bnc].u.member)
7523 bnc++;
7524 deopped = 1;
7525 }
7526 bounce->args[bnc].mode = MODE_CHANOP;
7527 bounce->args[bnc].u.member = change->args[ii].u.member;
7528 bnc++;
7529 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7530 }
7531 else if(change->args[ii].mode & MODE_CHANOP)
7532 {
7533 const struct userNode *victim = change->args[ii].u.member->user;
7534 if(IsService(victim) || validate_op(NULL, user, channel, (struct userNode*)victim))
7535 continue;
7536 if(!bounce)
7537 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7538 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7539 bounce->args[bnc].u.member = change->args[ii].u.member;
7540 bnc++;
7541 }
7542 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7543 {
7544 const char *ban = change->args[ii].u.hostmask;
7545 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7546 continue;
7547 if(!bounce)
7548 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7549 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7550 bounce->args[bnc].u.hostmask = strdup(ban);
7551 bnc++;
7552 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7553 }
7554 }
7555 if(bounce)
7556 {
7557 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7558 mod_chanmode_announce(chanserv, channel, bounce);
7559 for(ii = 0; ii < change->argc; ++ii)
7560 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7561 free((char*)bounce->args[ii].u.hostmask);
7562 mod_chanmode_free(bounce);
7563 }
7564 }
7565
7566 static void
7567 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7568 {
7569 struct chanNode *channel;
7570 struct banData *bData;
7571 struct mod_chanmode change;
7572 unsigned int ii, jj;
7573 char kick_reason[MAXLEN];
7574
7575 mod_chanmode_init(&change);
7576 change.argc = 1;
7577 change.args[0].mode = MODE_BAN;
7578 for(ii = 0; ii < user->channels.used; ++ii)
7579 {
7580 channel = user->channels.list[ii]->channel;
7581 /* Need not check for bans if they're opped or voiced. */
7582 /* TODO: does this make sense in automode v, h, and o? *
7583 * lets still enforce on voice people anyway, and see how that goes -Rubin */
7584 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE ))
7585 continue;
7586 /* Need not check for bans unless channel registration is active. */
7587 if(!channel->channel_info || IsSuspended(channel->channel_info))
7588 continue;
7589 /* Look for a matching ban already on the channel. */
7590 for(jj = 0; jj < channel->banlist.used; ++jj)
7591 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7592 break;
7593 /* Need not act if we found one. */
7594 if(jj < channel->banlist.used)
7595 continue;
7596 /* don't kick someone on the userlist */
7597 if(protect_user(user, chanserv, channel->channel_info, true))
7598 continue;
7599 /* Look for a matching ban in this channel. */
7600 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7601 {
7602 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7603 continue;
7604 change.args[0].u.hostmask = bData->mask;
7605 mod_chanmode_announce(chanserv, channel, &change);
7606 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7607 KickChannelUser(user, channel, chanserv, kick_reason);
7608 bData->triggered = now;
7609 break; /* we don't need to check any more bans in the channel */
7610 }
7611 }
7612 }
7613
7614 static void handle_rename(struct handle_info *handle, const char *old_handle)
7615 {
7616 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7617
7618 if(dnr)
7619 {
7620 dict_remove2(handle_dnrs, old_handle, 1);
7621 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7622 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7623 }
7624 }
7625
7626 static void
7627 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7628 {
7629 struct userNode *h_user;
7630
7631 if(handle->channels)
7632 {
7633 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7634 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7635
7636 while(handle->channels)
7637 del_channel_user(handle->channels, 1);
7638 }
7639 }
7640
7641 static void
7642 handle_server_link(UNUSED_ARG(struct server *server))
7643 {
7644 struct chanData *cData;
7645
7646 for(cData = channelList; cData; cData = cData->next)
7647 {
7648 if(!IsSuspended(cData))
7649 cData->may_opchan = 1;
7650 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7651 && !cData->channel->join_flooded
7652 && ((cData->channel->limit - cData->channel->members.used)
7653 < chanserv_conf.adjust_threshold))
7654 {
7655 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7656 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7657 }
7658 }
7659 }
7660
7661 static void
7662 chanserv_conf_read(void)
7663 {
7664 dict_t conf_node;
7665 const char *str;
7666 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7667 struct mod_chanmode *change;
7668 struct string_list *strlist;
7669 struct chanNode *chan;
7670 unsigned int ii;
7671
7672 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7673 {
7674 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7675 return;
7676 }
7677 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7678 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7679 chanserv_conf.support_channels.used = 0;
7680 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7681 {
7682 for(ii = 0; ii < strlist->used; ++ii)
7683 {
7684 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7685 if(!str2)
7686 str2 = "+nt";
7687 chan = AddChannel(strlist->list[ii], now, str2, NULL, NULL);
7688 LockChannel(chan);
7689 channelList_append(&chanserv_conf.support_channels, chan);
7690 }
7691 }
7692 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7693 {
7694 const char *str2;
7695 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7696 if(!str2)
7697 str2 = "+nt";
7698 chan = AddChannel(str, now, str2, NULL, NULL);
7699 LockChannel(chan);
7700 channelList_append(&chanserv_conf.support_channels, chan);
7701 }
7702 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7703 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7704 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7705 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7706 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7707 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7708 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7709 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7710 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7711 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7712 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7713 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7714 str = database_get_data(conf_node, KEY_BAN_TIMEOUT_FREQ, RECDB_QSTRING);
7715 chanserv_conf.ban_timeout_frequency = str ? ParseInterval(str) : 600;
7716 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7717 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7718 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7719 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7720 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7721 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7722 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7723 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7724 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7725 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7726 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7727 if(chanserv && str)
7728 NickChange(chanserv, str, 0);
7729 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7730 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7731 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7732 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7733 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7734 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7735 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7736 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7737 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7738 chanserv_conf.max_owned = str ? atoi(str) : 5;
7739 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7740 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7741 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7742 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7743 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7744 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7745 str = database_get_data(conf_node, KEY_GOD_TIMEOUT, RECDB_QSTRING);
7746 god_timeout = str ? ParseInterval(str) : 60*15;
7747 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7748 if(!str)
7749 str = "+nt";
7750 safestrncpy(mode_line, str, sizeof(mode_line));
7751 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7752 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
7753 && (change->argc < 2))
7754 {
7755 chanserv_conf.default_modes = *change;
7756 mod_chanmode_free(change);
7757 }
7758 free_string_list(chanserv_conf.set_shows);
7759 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7760 if(strlist)
7761 strlist = string_list_copy(strlist);
7762 else
7763 {
7764 static const char *list[] = {
7765 /* free form text */
7766 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7767 /* options based on user level */
7768 "PubCmd", "InviteMe", "UserInfo","EnfOps",
7769 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
7770 /* multiple choice options */
7771 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
7772 /* binary options */
7773 "DynLimit", "NoDelete", "BanTimeout",
7774 /* delimiter */
7775 NULL
7776 };
7777 unsigned int ii;
7778 strlist = alloc_string_list(ArrayLength(list)-1);
7779 for(ii=0; list[ii]; ii++)
7780 string_list_append(strlist, strdup(list[ii]));
7781 }
7782 chanserv_conf.set_shows = strlist;
7783 /* We don't look things up now, in case the list refers to options
7784 * defined by modules initialized after this point. Just mark the
7785 * function list as invalid, so it will be initialized.
7786 */
7787 set_shows_list.used = 0;
7788 free_string_list(chanserv_conf.eightball);
7789 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7790 if(strlist)
7791 {
7792 strlist = string_list_copy(strlist);
7793 }
7794 else
7795 {
7796 strlist = alloc_string_list(4);
7797 string_list_append(strlist, strdup("Yes."));
7798 string_list_append(strlist, strdup("No."));
7799 string_list_append(strlist, strdup("Maybe so."));
7800 }
7801 chanserv_conf.eightball = strlist;
7802 free_string_list(chanserv_conf.old_ban_names);
7803 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7804 if(strlist)
7805 strlist = string_list_copy(strlist);
7806 else
7807 strlist = alloc_string_list(2);
7808 chanserv_conf.old_ban_names = strlist;
7809 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7810 off_channel = str ? atoi(str) : 0;
7811 }
7812
7813 static void
7814 chanserv_note_type_read(const char *key, struct record_data *rd)
7815 {
7816 dict_t obj;
7817 struct note_type *ntype;
7818 const char *str;
7819
7820 if(!(obj = GET_RECORD_OBJECT(rd)))
7821 {
7822 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7823 return;
7824 }
7825 if(!(ntype = chanserv_create_note_type(key)))
7826 {
7827 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7828 return;
7829 }
7830
7831 /* Figure out set access */
7832 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7833 {
7834 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7835 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7836 }
7837 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7838 {
7839 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7840 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7841 }
7842 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7843 {
7844 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7845 }
7846 else
7847 {
7848 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7849 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7850 ntype->set_access.min_opserv = 0;
7851 }
7852
7853 /* Figure out visibility */
7854 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7855 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7856 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7857 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7858 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7859 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7860 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7861 ntype->visible_type = NOTE_VIS_ALL;
7862 else
7863 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7864
7865 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7866 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7867 }
7868
7869 static void
7870 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7871 {
7872 struct handle_info *handle;
7873 struct userData *uData;
7874 char *seen, *inf, *flags, *expires;
7875 time_t last_seen;
7876 unsigned short access;
7877
7878 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7879 {
7880 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7881 return;
7882 }
7883
7884 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7885 if(access > UL_OWNER)
7886 {
7887 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7888 return;
7889 }
7890
7891 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7892 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7893 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
7894 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7895 expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7896 handle = get_handle_info(key);
7897 if(!handle)
7898 {
7899 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7900 return;
7901 }
7902
7903 uData = add_channel_user(chan, handle, access, last_seen, inf);
7904 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7905 uData->expires = expires ? strtoul(expires, NULL, 0) : 0;
7906
7907 if((uData->flags & USER_SUSPENDED) && uData->expires)
7908 {
7909 if(uData->expires > now)
7910 timeq_add(uData->expires, chanserv_expire_user_suspension, uData);
7911 else
7912 uData->flags &= ~USER_SUSPENDED;
7913 }
7914
7915 /* Upgrade: set autoop to the inverse of noautoop */
7916 if(chanserv_read_version < 2)
7917 {
7918 /* if noautoop is true, set autoop false, and vice versa */
7919 if(uData->flags & USER_NOAUTO_OP)
7920 uData->flags = uData->flags & ~USER_AUTO_OP;
7921 else
7922 uData->flags = uData->flags | USER_AUTO_OP;
7923 log_module(CS_LOG, LOG_INFO, "UPGRADE: to db version 2 from %u. Changing flag to %d for %s in %s.", chanserv_read_version, uData->flags, key, chan->channel->name);
7924 }
7925
7926 }
7927
7928 static void
7929 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7930 {
7931 struct banData *bData;
7932 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7933 time_t set_time, triggered_time, expires_time;
7934
7935 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7936 {
7937 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7938 return;
7939 }
7940
7941 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7942 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7943 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7944 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7945 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7946 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7947 if (!reason || !owner)
7948 return;
7949
7950 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
7951 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
7952 if(s_expires)
7953 expires_time = (time_t)strtoul(s_expires, NULL, 0);
7954 else if(s_duration)
7955 expires_time = set_time + atoi(s_duration);
7956 else
7957 expires_time = 0;
7958
7959 if(!reason || (expires_time && (expires_time < now)))
7960 return;
7961
7962 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7963 }
7964
7965 static struct suspended *
7966 chanserv_read_suspended(dict_t obj)
7967 {
7968 struct suspended *suspended = calloc(1, sizeof(*suspended));
7969 char *str;
7970 dict_t previous;
7971
7972 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7973 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
7974 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7975 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
7976 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7977 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
7978 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7979 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7980 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7981 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7982 return suspended;
7983 }
7984
7985 static struct giveownership *
7986 chanserv_read_giveownership(dict_t obj)
7987 {
7988 struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
7989 char *str;
7990 dict_t previous;
7991
7992 str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
7993 giveownership->staff_issuer = str ? strdup(str) : NULL;
7994
7995 giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
7996
7997 giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
7998 giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
7999
8000 str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
8001 giveownership->reason = str ? strdup(str) : NULL;
8002 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8003 giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
8004
8005 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8006 giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
8007 return giveownership;
8008 }
8009
8010 static int
8011 chanserv_channel_read(const char *key, struct record_data *hir)
8012 {
8013 struct suspended *suspended;
8014 struct giveownership *giveownership;
8015 struct mod_chanmode *modes;
8016 struct chanNode *cNode;
8017 struct chanData *cData;
8018 struct dict *channel, *obj;
8019 char *str, *argv[10];
8020 dict_iterator_t it;
8021 unsigned int argc;
8022
8023 channel = hir->d.object;
8024
8025 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
8026 if(!str)
8027 str = "<unknown>";
8028 cNode = AddChannel(key, now, NULL, NULL, NULL);
8029 if(!cNode)
8030 {
8031 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
8032 return 0;
8033 }
8034 cData = register_channel(cNode, str);
8035 if(!cData)
8036 {
8037 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
8038 return 0;
8039 }
8040
8041 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
8042 {
8043 enum levelOption lvlOpt;
8044 enum charOption chOpt;
8045
8046 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
8047 cData->flags = atoi(str);
8048
8049 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8050 {
8051 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
8052 if(str)
8053 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
8054 else if(levelOptions[lvlOpt].old_flag)
8055 {
8056 if(cData->flags & levelOptions[lvlOpt].old_flag)
8057 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
8058 else
8059 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
8060 }
8061 }
8062
8063 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8064 {
8065 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8066 continue;
8067 cData->chOpts[chOpt] = str[0];
8068 }
8069 }
8070 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8071 {
8072 enum levelOption lvlOpt;
8073 enum charOption chOpt;
8074 unsigned int count;
8075
8076 cData->flags = base64toint(str, 5);
8077 count = strlen(str += 5);
8078 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8079 {
8080 unsigned short lvl;
8081 if(levelOptions[lvlOpt].old_flag)
8082 {
8083 if(cData->flags & levelOptions[lvlOpt].old_flag)
8084 lvl = levelOptions[lvlOpt].flag_value;
8085 else
8086 lvl = levelOptions[lvlOpt].default_value;
8087 }
8088 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8089 {
8090 case 'c': lvl = UL_COOWNER; break;
8091 case 'm': lvl = UL_MANAGER; break;
8092 case 'n': lvl = UL_OWNER+1; break;
8093 case 'o': lvl = UL_OP; break;
8094 case 'p': lvl = UL_PEON; break;
8095 case 'h': lvl = UL_HALFOP; break;
8096 case 'w': lvl = UL_OWNER; break;
8097 default: lvl = 0; break;
8098 }
8099 cData->lvlOpts[lvlOpt] = lvl;
8100 }
8101 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8102 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8103 }
8104
8105 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8106 {
8107 suspended = chanserv_read_suspended(obj);
8108 cData->suspended = suspended;
8109 suspended->cData = cData;
8110 /* We could use suspended->expires and suspended->revoked to
8111 * set the CHANNEL_SUSPENDED flag, but we don't. */
8112 }
8113 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8114 {
8115 suspended = calloc(1, sizeof(*suspended));
8116 suspended->issued = 0;
8117 suspended->revoked = 0;
8118 suspended->suspender = strdup(str);
8119 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8120 suspended->expires = str ? atoi(str) : 0;
8121 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8122 suspended->reason = strdup(str ? str : "No reason");
8123 suspended->previous = NULL;
8124 cData->suspended = suspended;
8125 suspended->cData = cData;
8126 }
8127 else
8128 {
8129 cData->flags &= ~CHANNEL_SUSPENDED;
8130 suspended = NULL; /* to squelch a warning */
8131 }
8132
8133 if(IsSuspended(cData)) {
8134 if(suspended->expires > now)
8135 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8136 else if(suspended->expires)
8137 cData->flags &= ~CHANNEL_SUSPENDED;
8138 }
8139
8140 if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
8141 {
8142 giveownership = chanserv_read_giveownership(obj);
8143 cData->giveownership = giveownership;
8144 }
8145
8146 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8147 struct mod_chanmode change;
8148 mod_chanmode_init(&change);
8149 change.argc = 1;
8150 change.args[0].mode = MODE_CHANOP;
8151 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8152 mod_chanmode_announce(chanserv, cNode, &change);
8153 }
8154
8155 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8156 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
8157 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8158 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
8159 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8160 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
8161 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8162 cData->max = str ? atoi(str) : 0;
8163 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8164 cData->greeting = str ? strdup(str) : NULL;
8165 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8166 cData->user_greeting = str ? strdup(str) : NULL;
8167 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8168 cData->topic_mask = str ? strdup(str) : NULL;
8169 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8170 cData->topic = str ? strdup(str) : NULL;
8171
8172 if(!IsSuspended(cData)
8173 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8174 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8175 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
8176 cData->modes = *modes;
8177 if(off_channel > 0)
8178 cData->modes.modes_set |= MODE_REGISTERED;
8179 if(cData->modes.argc > 1)
8180 cData->modes.argc = 1;
8181 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8182 mod_chanmode_free(modes);
8183 }
8184
8185 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8186 for(it = dict_first(obj); it; it = iter_next(it))
8187 user_read_helper(iter_key(it), iter_data(it), cData);
8188
8189 if(!cData->users && !IsProtected(cData))
8190 {
8191 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8192 unregister_channel(cData, "has empty user list.");
8193 return 0;
8194 }
8195
8196 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8197 for(it = dict_first(obj); it; it = iter_next(it))
8198 ban_read_helper(iter_key(it), iter_data(it), cData);
8199
8200 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8201 for(it = dict_first(obj); it; it = iter_next(it))
8202 {
8203 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8204 struct record_data *rd = iter_data(it);
8205 const char *note, *setter;
8206
8207 if(rd->type != RECDB_OBJECT)
8208 {
8209 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8210 }
8211 else if(!ntype)
8212 {
8213 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8214 }
8215 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8216 {
8217 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8218 }
8219 else
8220 {
8221 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8222 if(!setter) setter = "<unknown>";
8223 chanserv_add_channel_note(cData, ntype, setter, note);
8224 }
8225 }
8226
8227 return 0;
8228 }
8229
8230 static void
8231 chanserv_dnr_read(const char *key, struct record_data *hir)
8232 {
8233 const char *setter, *reason, *str;
8234 struct do_not_register *dnr;
8235
8236 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8237 if(!setter)
8238 {
8239 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8240 return;
8241 }
8242 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8243 if(!reason)
8244 {
8245 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8246 return;
8247 }
8248 dnr = chanserv_add_dnr(key, setter, reason);
8249 if(!dnr)
8250 return;
8251 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8252 if(str)
8253 dnr->set = atoi(str);
8254 else
8255 dnr->set = 0;
8256 }
8257
8258 static void
8259 chanserv_version_read(struct dict *section)
8260 {
8261 /* global var.. */
8262 char *str;
8263 str = database_get_data(section, KEY_VERSION_NUMBER, RECDB_QSTRING);
8264 if(str)
8265 chanserv_read_version = atoi(str);
8266 log_module(CS_LOG, LOG_DEBUG, "Chanserv db version is %d.", chanserv_read_version);
8267 }
8268
8269 static int
8270 chanserv_saxdb_read(struct dict *database)
8271 {
8272 struct dict *section;
8273 dict_iterator_t it;
8274
8275 if((section = database_get_data(database, KEY_VERSION_CONTROL, RECDB_OBJECT)))
8276 chanserv_version_read(section);
8277
8278 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8279 for(it = dict_first(section); it; it = iter_next(it))
8280 chanserv_note_type_read(iter_key(it), iter_data(it));
8281
8282 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8283 for(it = dict_first(section); it; it = iter_next(it))
8284 chanserv_channel_read(iter_key(it), iter_data(it));
8285
8286 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8287 for(it = dict_first(section); it; it = iter_next(it))
8288 chanserv_dnr_read(iter_key(it), iter_data(it));
8289
8290 return 0;
8291 }
8292
8293 static int
8294 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8295 {
8296 int high_present = 0;
8297 saxdb_start_record(ctx, KEY_USERS, 1);
8298 for(; uData; uData = uData->next)
8299 {
8300 if((uData->access >= UL_PRESENT) && uData->present)
8301 high_present = 1;
8302 saxdb_start_record(ctx, uData->handle->handle, 0);
8303 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8304 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8305 if(uData->flags)
8306 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8307 if(uData->expires)
8308 saxdb_write_int(ctx, KEY_EXPIRES, uData->expires);
8309 if(uData->info)
8310 saxdb_write_string(ctx, KEY_INFO, uData->info);
8311 saxdb_end_record(ctx);
8312 }
8313 saxdb_end_record(ctx);
8314 return high_present;
8315 }
8316
8317 static void
8318 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8319 {
8320 if(!bData)
8321 return;
8322 saxdb_start_record(ctx, KEY_BANS, 1);
8323 for(; bData; bData = bData->next)
8324 {
8325 saxdb_start_record(ctx, bData->mask, 0);
8326 saxdb_write_int(ctx, KEY_SET, bData->set);
8327 if(bData->triggered)
8328 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8329 if(bData->expires)
8330 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8331 if(bData->owner[0])
8332 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8333 if(bData->reason)
8334 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8335 saxdb_end_record(ctx);
8336 }
8337 saxdb_end_record(ctx);
8338 }
8339
8340 static void
8341 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8342 {
8343 saxdb_start_record(ctx, name, 0);
8344 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8345 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8346 if(susp->issued)
8347 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8348 if(susp->expires)
8349 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8350 if(susp->revoked)
8351 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8352 if(susp->previous)
8353 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8354 saxdb_end_record(ctx);
8355 }
8356
8357 static void
8358 chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
8359 {
8360 saxdb_start_record(ctx, name, 0);
8361 if(giveownership->staff_issuer)
8362 saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
8363 if(giveownership->old_owner)
8364 saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
8365 if(giveownership->target)
8366 saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
8367 if(giveownership->target_access)
8368 saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
8369 if(giveownership->reason)
8370 saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
8371 if(giveownership->issued)
8372 saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
8373 if(giveownership->previous)
8374 chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
8375 saxdb_end_record(ctx);
8376 }
8377
8378 static void
8379 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8380 {
8381 char buf[MAXLEN];
8382 int high_present;
8383 enum levelOption lvlOpt;
8384 enum charOption chOpt;
8385
8386 saxdb_start_record(ctx, channel->channel->name, 1);
8387
8388 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8389 saxdb_write_int(ctx, KEY_MAX, channel->max);
8390 if(channel->topic)
8391 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8392 if(channel->registrar)
8393 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8394 if(channel->greeting)
8395 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8396 if(channel->user_greeting)
8397 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8398 if(channel->topic_mask)
8399 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8400 if(channel->suspended)
8401 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8402 if(channel->giveownership)
8403 chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
8404
8405 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8406 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8407 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8408 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8409 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8410 {
8411 buf[0] = channel->chOpts[chOpt];
8412 buf[1] = '\0';
8413 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8414 }
8415 saxdb_end_record(ctx);
8416
8417 if(channel->modes.modes_set || channel->modes.modes_clear)
8418 {
8419 mod_chanmode_format(&channel->modes, buf);
8420 saxdb_write_string(ctx, KEY_MODES, buf);
8421 }
8422
8423 high_present = chanserv_write_users(ctx, channel->users);
8424 chanserv_write_bans(ctx, channel->bans);
8425
8426 if(dict_size(channel->notes))
8427 {
8428 dict_iterator_t it;
8429
8430 saxdb_start_record(ctx, KEY_NOTES, 1);
8431 for(it = dict_first(channel->notes); it; it = iter_next(it))
8432 {
8433 struct note *note = iter_data(it);
8434 saxdb_start_record(ctx, iter_key(it), 0);
8435 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8436 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8437 saxdb_end_record(ctx);
8438 }
8439 saxdb_end_record(ctx);
8440 }
8441
8442 if(channel->ownerTransfer)
8443 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8444 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8445 saxdb_end_record(ctx);
8446 }
8447
8448 static void
8449 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8450 {
8451 const char *str;
8452
8453 saxdb_start_record(ctx, ntype->name, 0);
8454 switch(ntype->set_access_type)
8455 {
8456 case NOTE_SET_CHANNEL_ACCESS:
8457 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8458 break;
8459 case NOTE_SET_CHANNEL_SETTER:
8460 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8461 break;
8462 case NOTE_SET_PRIVILEGED: default:
8463 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8464 break;
8465 }
8466 switch(ntype->visible_type)
8467 {
8468 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8469 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8470 case NOTE_VIS_PRIVILEGED:
8471 default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8472 }
8473 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8474 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8475 saxdb_end_record(ctx);
8476 }
8477
8478 static void
8479 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8480 {
8481 struct do_not_register *dnr;
8482 dict_iterator_t it;
8483
8484 for(it = dict_first(dnrs); it; it = iter_next(it))
8485 {
8486 dnr = iter_data(it);
8487 saxdb_start_record(ctx, dnr->chan_name, 0);
8488 if(dnr->set)
8489 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8490 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8491 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8492 saxdb_end_record(ctx);
8493 }
8494 }
8495
8496 static int
8497 chanserv_saxdb_write(struct saxdb_context *ctx)
8498 {
8499 dict_iterator_t it;
8500 struct chanData *channel;
8501
8502 /* Version Control*/
8503 saxdb_start_record(ctx, KEY_VERSION_CONTROL, 1);
8504 saxdb_write_int(ctx, KEY_VERSION_NUMBER, CHANSERV_DB_VERSION);
8505 saxdb_end_record(ctx);
8506
8507 /* Notes */
8508 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8509 for(it = dict_first(note_types); it; it = iter_next(it))
8510 chanserv_write_note_type(ctx, iter_data(it));
8511 saxdb_end_record(ctx);
8512
8513 /* DNRs */
8514 saxdb_start_record(ctx, KEY_DNR, 1);
8515 write_dnrs_helper(ctx, handle_dnrs);
8516 write_dnrs_helper(ctx, plain_dnrs);
8517 write_dnrs_helper(ctx, mask_dnrs);
8518 saxdb_end_record(ctx);
8519
8520 /* Channels */
8521 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8522 for(channel = channelList; channel; channel = channel->next)
8523 chanserv_write_channel(ctx, channel);
8524 saxdb_end_record(ctx);
8525
8526 return 0;
8527 }
8528
8529 static void
8530 chanserv_db_cleanup(void) {
8531 unsigned int ii;
8532 unreg_part_func(handle_part);
8533 while(channelList)
8534 unregister_channel(channelList, "terminating.");
8535 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8536 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8537 free(chanserv_conf.support_channels.list);
8538 dict_delete(handle_dnrs);
8539 dict_delete(plain_dnrs);
8540 dict_delete(mask_dnrs);
8541 dict_delete(note_types);
8542 free_string_list(chanserv_conf.eightball);
8543 free_string_list(chanserv_conf.old_ban_names);
8544 free_string_list(chanserv_conf.set_shows);
8545 free(set_shows_list.list);
8546 free(uset_shows_list.list);
8547 while(helperList)
8548 {
8549 struct userData *helper = helperList;
8550 helperList = helperList->next;
8551 free(helper);
8552 }
8553 }
8554
8555 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
8556 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8557 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8558
8559 void
8560 init_chanserv(const char *nick)
8561 {
8562 struct chanNode *chan;
8563 unsigned int i;
8564 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8565 conf_register_reload(chanserv_conf_read);
8566
8567 reg_server_link_func(handle_server_link);
8568
8569 reg_new_channel_func(handle_new_channel);
8570 reg_join_func(handle_join);
8571 reg_part_func(handle_part);
8572 reg_kick_func(handle_kick);
8573 reg_topic_func(handle_topic);
8574 reg_mode_change_func(handle_mode);
8575 reg_nick_change_func(handle_nick_change);
8576
8577 reg_auth_func(handle_auth);
8578 reg_handle_rename_func(handle_rename);
8579 reg_unreg_func(handle_unreg);
8580
8581 handle_dnrs = dict_new();
8582 dict_set_free_data(handle_dnrs, free);
8583 plain_dnrs = dict_new();
8584 dict_set_free_data(plain_dnrs, free);
8585 mask_dnrs = dict_new();
8586 dict_set_free_data(mask_dnrs, free);
8587
8588 reg_svccmd_unbind_func(handle_svccmd_unbind);
8589 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8590 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8591 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8592 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8593 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8594 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8595 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8596 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8597 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8598
8599 DEFINE_COMMAND(pending, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8600
8601 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8602 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8603
8604 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8605 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8606 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8607 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8608 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8609
8610 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8611 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8612 DEFINE_COMMAND(mdelmanager, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8613 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8614 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8615 DEFINE_COMMAND(mdelhalfop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8616
8617 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8618 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8619 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8620 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8621
8622 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8623 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8624 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8625 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8626 DEFINE_COMMAND(hop, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8627 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8628 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8629 DEFINE_COMMAND(dehop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8630 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8631 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8632
8633 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
8634 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
8635 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
8636 DEFINE_COMMAND(unban, 2, 0, "template", "hop", NULL);
8637 DEFINE_COMMAND(unbanall, 1, 0, "template", "hop", NULL);
8638 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "hop", NULL);
8639 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8640 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "hop", "flags", "+never_csuspend", NULL);
8641 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8642 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8643 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "manager", NULL);
8644 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8645 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8646 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8647
8648 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "manager", NULL);
8649 DEFINE_COMMAND(last, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "manager", NULL);
8650 DEFINE_COMMAND(addlamer, 2, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
8651 DEFINE_COMMAND(addtimedlamer, 3, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
8652
8653 /* if you change dellamer access, see also places
8654 * like unbanme which have manager hardcoded. */
8655 DEFINE_COMMAND(dellamer, 2, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
8656 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8657
8658 DEFINE_COMMAND(lamers, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8659
8660 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8661
8662 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8663 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8664 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8665 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8666 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8667 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8668 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8669 DEFINE_COMMAND(hlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8670 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8671 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8672 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8673 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8674
8675 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8676 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8677
8678 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8679 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8680 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8681 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8682
8683 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8684 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8685 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8686 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8687 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8688
8689 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8690 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8691 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8692 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8693 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8694 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8695 DEFINE_COMMAND(calc, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8696 DEFINE_COMMAND(reply, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8697
8698 /* Channel options */
8699 DEFINE_CHANNEL_OPTION(defaulttopic);
8700 DEFINE_CHANNEL_OPTION(topicmask);
8701 DEFINE_CHANNEL_OPTION(greeting);
8702 DEFINE_CHANNEL_OPTION(usergreeting);
8703 DEFINE_CHANNEL_OPTION(modes);
8704 DEFINE_CHANNEL_OPTION(enfops);
8705 DEFINE_CHANNEL_OPTION(enfhalfops);
8706 DEFINE_CHANNEL_OPTION(automode);
8707 DEFINE_CHANNEL_OPTION(protect);
8708 DEFINE_CHANNEL_OPTION(enfmodes);
8709 DEFINE_CHANNEL_OPTION(enftopic);
8710 DEFINE_CHANNEL_OPTION(pubcmd);
8711 DEFINE_CHANNEL_OPTION(userinfo);
8712 DEFINE_CHANNEL_OPTION(dynlimit);
8713 DEFINE_CHANNEL_OPTION(topicsnarf);
8714 DEFINE_CHANNEL_OPTION(nodelete);
8715 DEFINE_CHANNEL_OPTION(toys);
8716 DEFINE_CHANNEL_OPTION(setters);
8717 DEFINE_CHANNEL_OPTION(topicrefresh);
8718 DEFINE_CHANNEL_OPTION(resync);
8719 DEFINE_CHANNEL_OPTION(ctcpreaction);
8720 DEFINE_CHANNEL_OPTION(bantimeout);
8721 DEFINE_CHANNEL_OPTION(inviteme);
8722 if(off_channel > 1)
8723 DEFINE_CHANNEL_OPTION(offchannel);
8724 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8725
8726 /* Alias set topic to set defaulttopic for compatibility. */
8727 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8728
8729 /* User options */
8730 DEFINE_USER_OPTION(autoinvite);
8731 DEFINE_USER_OPTION(autojoin);
8732 DEFINE_USER_OPTION(info);
8733 DEFINE_USER_OPTION(autoop);
8734
8735 /* Alias uset autovoice to uset autoop. */
8736 modcmd_register(chanserv_module, "uset autovoice", user_opt_autoop, 1, 0, NULL);
8737
8738 note_types = dict_new();
8739 dict_set_free_data(note_types, chanserv_deref_note_type);
8740 if(nick)
8741 {
8742 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8743 chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
8744 service_register(chanserv)->trigger = '!';
8745 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8746 }
8747
8748 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8749
8750 if(chanserv_conf.channel_expire_frequency)
8751 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8752
8753 if(chanserv_conf.ban_timeout_frequency)
8754 timeq_add(now + chanserv_conf.ban_timeout_frequency, expire_bans, NULL);
8755
8756 if(chanserv_conf.refresh_period)
8757 {
8758 time_t next_refresh;
8759 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8760 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8761 timeq_add(next_refresh, chanserv_auto_resync, NULL);
8762 }
8763
8764 if (autojoin_channels && chanserv) {
8765 for (i = 0; i < autojoin_channels->used; i++) {
8766 chan = AddChannel(autojoin_channels->list[i], now, "+nt", NULL, NULL);
8767 AddChannelUser(chanserv, chan)->modes |= MODE_CHANOP;
8768 }
8769 }
8770
8771 reg_exit_func(chanserv_db_cleanup);
8772 message_register_table(msgtab);
8773 }