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