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