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