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