19ece49ba99b118370cfbbf89aa23fc84721ef7d
[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_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
241     { "CSMSG_INVALID_NUMERIC",   "$b%d$b is not a valid choice.  Choose one:" },
242     { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
243     { "CSMSG_SET_TOPICMASK",     "$bTopicMask   $b %s" },
244     { "CSMSG_SET_GREETING",      "$bGreeting    $b %s" },
245     { "CSMSG_SET_USERGREETING",  "$bUserGreeting$b %s" },
246     { "CSMSG_SET_MODES",         "$bModes       $b %s" },
247     { "CSMSG_SET_NODELETE",      "$bNoDelete    $b %s" },
248     { "CSMSG_SET_USERINFO",      "$bUserInfo    $b %s" },
249     { "CSMSG_SET_VOICE",         "$bVoice       $b %s" },
250     { "CSMSG_SET_DYNLIMIT",      "$bDynLimit    $b %s" },
251     { "CSMSG_SET_TOPICSNARF",    "$bTopicSnarf  $b %s" },
252     { "CSMSG_SET_PEONINVITE",    "$bPeonInvite  $b %s" },
253     { "CSMSG_SET_ENFOPS",        "$bEnfOps      $b %d" },
254     { "CSMSG_SET_GIVE_OPS",      "$bGiveOps     $b %d" },
255     { "CSMSG_SET_ENFMODES",      "$bEnfModes    $b %d" },
256     { "CSMSG_SET_ENFTOPIC",      "$bEnfTopic    $b %d" },
257     { "CSMSG_SET_PUBCMD",        "$bPubCmd      $b %d" },
258     { "CSMSG_SET_SETTERS",       "$bSetters     $b %d" },
259     { "CSMSG_SET_CTCPUSERS",     "$bCTCPUsers   $b %d" },
260     { "CSMSG_SET_PROTECT",       "$bProtect     $b %d - %s" },
261     { "CSMSG_SET_TOYS",          "$bToys        $b %d - %s" },
262     { "CSMSG_SET_CTCPREACTION",  "$bCTCPReaction$b %d - %s" },
263     { "CSMSG_SET_TOPICREFRESH",  "$bTopicRefresh$b %d - %s" },
264     { "CSMSG_USET_NOAUTOOP",     "$bNoAutoOp    $b %s" },
265     { "CSMSG_USET_NOAUTOVOICE",  "$bNoAutoVoice $b %s" },
266     { "CSMSG_USET_AUTOINVITE",   "$bAutoInvite  $b %s" },
267     { "CSMSG_USET_INFO",         "$bInfo        $b %s" },
268
269     { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
270     { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
271     { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
272     { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
273     { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
274     { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
275     { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
276     { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
277     { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
278     { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
279     { "CSMSG_PROTECT_NONE", "No users will be protected." },
280     { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
281     { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
282     { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
283     { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
284     { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
285     { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
286     { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
287     { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
288     { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
289     { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
290     { "CSMSG_CTCPREACTION_SHORTBAN",  "Short timed ban on disallowed CTCPs" },
291     { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
292
293     { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
294     { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s%s%s" },
295     { "CSMSG_ALREADY_PRESENT", "%s is $balready in %s$b." },
296     { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
297     { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
298
299     { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
300     { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
301     { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
302
303 /* Channel userlist */
304     { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
305     { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
306     { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
307     { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
308
309 /* Channel note list */
310     { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
311     { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
312     { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
313     { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
314     { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
315     { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
316     { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
317     { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
318     { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
319     { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
320     { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
321     { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
322     { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
323     { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
324     { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
325
326 /* Channel [un]suspension */
327     { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
328     { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
329     { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
330     { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
331     { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
332     { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
333     { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
334
335 /* Access information */
336     { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
337     { "CSMSG_ACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
338     { "CSMSG_SQUAT_ACCESS", "You do not have access to any channels." },
339     { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
340     { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
341     { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
342     { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
343     { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
344     { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
345     { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
346     { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
347
348 /* Seen information */
349     { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
350     { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
351     { "CSMSG_USER_VACATION", "%s is currently on vacation." },
352     { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
353
354 /* Names information */
355     { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
356     { "CSMSG_END_NAMES", "End of names in $b%s$b" },
357
358 /* Channel information */
359     { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
360     { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic:       $b%s" },
361     { "CSMSG_CHANNEL_MODES", "$bMode Lock:           $b%s" },
362     { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
363     { "CSMSG_CHANNEL_MAX", "$bRecord Visitors:     $b%i" },
364     { "CSMSG_CHANNEL_OWNER", "$bOwner:               $b%s" },
365     { "CSMSG_CHANNEL_BANS", "$bBan Count:           $b%i" },
366     { "CSMSG_CHANNEL_USERS", "$bTotal User Count:    $b%i" },
367     { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar:           $b%s" },
368     { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
369     { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
370     { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
371     { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
372     { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
373     { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
374     { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
375     { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
376     { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
377     { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
378     { "CSMSG_CHANNEL_REGISTERED", "$bRegistered:          $b%s ago." },
379     { "CSMSG_CHANNEL_VISITED", "$bVisited:             $b%s ago." },
380
381     { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
382     { "CSMSG_PEEK_TOPIC", "$bTopic:          $b%s" },
383     { "CSMSG_PEEK_MODES", "$bModes:          $b%s" },
384     { "CSMSG_PEEK_USERS", "$bTotal users:    $b%d" },
385     { "CSMSG_PEEK_OPS", "$bOps:$b" },
386     { "CSMSG_PEEK_NO_OPS", "$bOps:            $bNone present" },
387
388 /* Network information */
389     { "CSMSG_NETWORK_INFO", "Network Information:" },
390     { "CSMSG_NETWORK_SERVERS", "$bServers:             $b%i" },
391     { "CSMSG_NETWORK_USERS",   "$bTotal Users:         $b%i" },
392     { "CSMSG_NETWORK_BANS",    "$bTotal Ban Count:     $b%i" },
393     { "CSMSG_NETWORK_OPERS",   "$bIRC Operators:       $b%i" },
394     { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
395     { "CSMSG_SERVICES_UPTIME", "$bServices Uptime:     $b%s" },
396     { "CSMSG_BURST_LENGTH",    "$bLast Burst Length:   $b%s" },
397
398 /* Staff list */
399     { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
400     { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
401     { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
402
403 /* Channel searches */
404     { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
405     { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
406     { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
407     { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
408
409 /* Channel configuration */
410     { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
411     { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
412     { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
413
414 /* User settings */
415     { "CSMSG_USER_OPTIONS", "User Options:" },
416     { "CSMSG_USER_PROTECTED", "That user is protected." },
417
418 /* Toys */
419     { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
420     { "CSMSG_PING_RESPONSE", "Pong!" },
421     { "CSMSG_WUT_RESPONSE", "wut" },
422     { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number.  Please use a number greater than 1 with this command." },
423     { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b.  Please use either a single number or standard 4d6+3 format." },
424     { "CSMSG_BAD_DICE_COUNT", "%d is too many dice.  Please use at most %d." },
425     { "CSMSG_DICE_ROLL", "The total is $b%d$b from rolling %dd%d+%d." },
426     { "CSMSG_DIE_ROLL", "A $b%d$b shows on the %d-sided die." },
427     { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
428     { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
429
430 /* Other things */
431     { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
432     { NULL, NULL }
433 };
434
435 /* eject_user and unban_user flags */
436 #define ACTION_KICK             0x0001
437 #define ACTION_BAN              0x0002
438 #define ACTION_ADD_BAN          0x0004
439 #define ACTION_ADD_TIMED_BAN    0x0008
440 #define ACTION_UNBAN            0x0010
441 #define ACTION_DEL_BAN          0x0020
442
443 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
444 #define MODELEN                 40 + KEYLEN
445 #define PADLEN                  21
446 #define ACCESSLEN               10
447
448 #define CSFUNC_ARGS             user, channel, argc, argv, cmd
449
450 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
451 #define CHANSERV_SYNTAX()       svccmd_send_help(user, chanserv, cmd)
452 #define REQUIRE_PARAMS(N)       if(argc < (N)) {            \
453         reply("MSG_MISSING_PARAMS", argv[0]); \
454         CHANSERV_SYNTAX(); \
455         return 0; }
456
457 DECLARE_LIST(dnrList, struct do_not_register *);
458 DEFINE_LIST(dnrList, struct do_not_register *);
459
460 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
461
462 struct userNode *chanserv;
463 dict_t note_types;
464 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
465 static struct log_type *CS_LOG;
466
467 static struct
468 {
469     struct channelList  support_channels;
470     struct mod_chanmode default_modes;
471
472     unsigned long       db_backup_frequency;
473     unsigned long       channel_expire_frequency;
474
475     long                info_delay;
476     unsigned int        adjust_delay;
477     long                channel_expire_delay;
478     unsigned int        nodelete_level;
479
480     unsigned int        adjust_threshold;
481     int                 join_flood_threshold;
482
483     unsigned int        greeting_length;
484     unsigned int        refresh_period;
485
486     unsigned int        max_owned;
487     unsigned int        max_chan_users;
488     unsigned int        max_chan_bans;
489
490     struct string_list  *set_shows;
491     struct string_list  *eightball;
492     struct string_list  *old_ban_names;
493
494     const char          *ctcp_short_ban_duration;
495     const char          *ctcp_long_ban_duration;
496
497     const char          *irc_operator_epithet;
498     const char          *network_helper_epithet;
499     const char          *support_helper_epithet;
500 } chanserv_conf;
501
502 struct listData
503 {
504     struct userNode *user;
505     struct userNode *bot;
506     struct chanNode *channel;
507     const char      *search;
508     unsigned short  lowest;
509     unsigned short  highest;
510     struct userData **users;
511     struct helpfile_table table;
512 };
513
514 enum note_access_type
515 {
516     NOTE_SET_CHANNEL_ACCESS,
517     NOTE_SET_CHANNEL_SETTER,
518     NOTE_SET_PRIVILEGED
519 };
520
521 enum note_visible_type
522 {
523     NOTE_VIS_ALL,
524     NOTE_VIS_CHANNEL_USERS,
525     NOTE_VIS_PRIVILEGED
526 };
527
528 struct note_type
529 {
530     enum note_access_type set_access_type;
531     union {
532         unsigned int     min_opserv;
533         unsigned short   min_ulevel;
534     } set_access;
535     enum note_visible_type visible_type;
536     unsigned int         max_length;
537     unsigned int         refs;
538     char                 name[1];
539 };
540
541 struct note
542 {
543     struct note_type     *type;
544     char                 setter[NICKSERV_HANDLE_LEN+1];
545     char                 note[1];
546 };
547
548 static unsigned int registered_channels;
549 static unsigned int banCount;
550
551 static const struct {
552     char *name;
553     char *title;
554     unsigned short level;
555     char ch;
556 } accessLevels[] = {
557     { "peon", "Peon", UL_PEON, '+' },
558     { "op", "Op", UL_OP, '@' },
559     { "master", "Master", UL_MASTER, '%' },
560     { "coowner", "Coowner", UL_COOWNER, '*' },
561     { "owner", "Owner", UL_OWNER, '!' },
562     { "helper", "BUG:", UL_HELPER, 'X' }
563 };
564
565 static const struct {
566     char *format_name;
567     char *db_name;
568     unsigned short default_value;
569     unsigned int old_idx;
570 } levelOptions[] = {
571     { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2 },
572     { "CSMSG_SET_ENFOPS", "enfops", 300, 1 },
573     { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3 },
574     { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4 },
575     { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5 },
576     { "CSMSG_SET_SETTERS", "setters", 400, 7 },
577     { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9 }
578 };
579
580 struct charOptionValues {
581     char value;
582     char *format_name;
583 } protectValues[] = {
584     { 'a', "CSMSG_PROTECT_ALL" },
585     { 'e', "CSMSG_PROTECT_EQUAL" },
586     { 'l', "CSMSG_PROTECT_LOWER" },
587     { 'n', "CSMSG_PROTECT_NONE" }
588 }, toysValues[] = {
589     { 'd', "CSMSG_TOYS_DISABLED" },
590     { 'n', "CSMSG_TOYS_PRIVATE" },
591     { 'p', "CSMSG_TOYS_PUBLIC" }
592 }, topicRefreshValues[] = {
593     { 'n', "CSMSG_TOPICREFRESH_NEVER" },
594     { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
595     { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
596     { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
597     { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
598 }, ctcpReactionValues[] = {
599     { 'k', "CSMSG_CTCPREACTION_KICK" },
600     { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
601     { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
602     { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
603 };
604
605 static const struct {
606     char *format_name;
607     char *db_name;
608     char default_value;
609     unsigned int old_idx;
610     unsigned char count;
611     struct charOptionValues *values;
612 } charOptions[] = {
613     { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
614     { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
615     { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
616     { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
617 };
618
619 struct userData *helperList;
620 struct chanData *channelList;
621 static struct module *chanserv_module;
622 static unsigned int userCount;
623
624 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
625 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
626 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
627
628 unsigned short
629 user_level_from_name(const char *name, unsigned short clamp_level)
630 {
631     unsigned int level = 0, ii;
632     if(isdigit(name[0]))
633         level = atoi(name);
634     else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
635         if(!irccasecmp(name, accessLevels[ii].name))
636             level = accessLevels[ii].level;
637     if(level > clamp_level)
638         return 0;
639     return level;
640 }
641
642 int
643 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
644 {
645     char *sep;
646     *minl = strtoul(arg, &sep, 10);
647     if(*sep == '\0')
648     {
649         *maxl = *minl;
650         return 1;
651     }
652     else if(*sep == '-')
653     {
654         *maxl = strtoul(sep+1, &sep, 10);
655         return *sep == '\0';
656     }
657     else
658         return 0;
659 }
660
661 struct userData*
662 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
663 {
664     struct userData *uData, **head;
665
666     if(!channel || !handle)
667         return NULL;
668
669     if(override && HANDLE_FLAGGED(handle, HELPING)
670        && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
671     {
672         for(uData = helperList;
673             uData && uData->handle != handle;
674             uData = uData->next);
675
676         if(!uData)
677         {
678             uData = calloc(1, sizeof(struct userData));
679             uData->handle = handle;
680
681             uData->access = UL_HELPER;
682             uData->seen = 0;
683
684             uData->info = NULL;
685
686             uData->prev = NULL;
687             uData->next = helperList;
688             if(helperList)
689                 helperList->prev = uData;
690             helperList = uData;
691         }
692
693         head = &helperList;
694     }
695     else
696     {
697         for(uData = channel->users; uData; uData = uData->next)
698             if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
699                 break;
700
701         head = &(channel->users);
702     }
703
704     if(uData && (uData != *head))
705     {
706         /* Shuffle the user to the head of whatever list he was in. */
707         if(uData->next)
708             uData->next->prev = uData->prev;
709         if(uData->prev)
710             uData->prev->next = uData->next;
711
712         uData->prev = NULL;
713         uData->next = *head;
714
715         if(*head)
716             (**head).prev = uData;
717         *head = uData;
718     }
719
720     return uData;
721 }
722
723 /* Returns non-zero if user has at least the minimum access.
724  * exempt_owner is set when handling !set, so the owner can set things
725  * to/from >500.
726  */
727 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
728 {
729     struct userData *uData;
730     struct chanData *cData = channel->channel_info;
731     unsigned short minimum = cData->lvlOpts[opt];
732     if(!minimum)
733         return 1;
734     uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
735     if(!uData)
736         return 0;
737     if(minimum <= uData->access)
738         return 1;
739     if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
740         return 1;
741     return 0;
742 }
743
744 /* Scan for other users authenticated to the same handle
745    still in the channel. If so, keep them listed as present.
746
747    user is optional, if not null, it skips checking that userNode
748    (for the handle_part function) */
749 static void
750 scan_handle_presence(struct chanNode *channel, struct handle_info *handle, struct userNode *user)
751 {
752     struct userData *uData;
753
754     if(!channel->channel_info || IsSuspended(channel->channel_info))
755         return;
756
757     uData = GetTrueChannelAccess(channel->channel_info, handle);
758     if(uData)
759     {
760         struct modeNode *mn = find_handle_in_channel(channel, handle, user);
761
762         if(mn)
763         {
764             uData->present = 1;
765             uData->seen = now;
766         }
767         else
768             uData->present = 0;
769     }
770 }
771
772 static void
773 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
774 {
775     unsigned int eflags, argc;
776     char *argv[4];
777     static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
778
779     /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
780     if(!channel->channel_info
781        || IsSuspended(channel->channel_info)
782        || IsService(user)
783        || !ircncasecmp(text, "ACTION ", 7))
784         return;
785     /* Figure out the minimum level needed to CTCP the channel */
786     if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
787         return;
788     /* We need to enforce against them; do so. */
789     eflags = 0;
790     argv[0] = text;
791     argv[1] = user->nick;
792     argc = 2;
793     if(GetUserMode(channel, user))
794         eflags |= ACTION_KICK;
795     switch(channel->channel_info->chOpts[chCTCPReaction]) {
796     default: case 'k': /* just do the kick */ break;
797     case 'b':
798         eflags |= ACTION_BAN;
799         break;
800     case 't':
801         eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
802         argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
803         break;
804     case 'T':
805         eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
806         argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
807         break;
808     }
809     argv[argc++] = bad_ctcp_reason;
810     eject_user(chanserv, channel, argc, argv, NULL, eflags);
811 }
812
813 struct note_type *
814 chanserv_create_note_type(const char *name)
815 {
816     struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
817     strcpy(ntype->name, name);
818     ntype->refs = 1;
819     dict_insert(note_types, ntype->name, ntype);
820     return ntype;
821 }
822
823 static void
824 chanserv_deref_note_type(void *data)
825 {
826     struct note_type *ntype = data;
827
828     if(--ntype->refs > 0)
829         return;
830     free(ntype);
831 }
832
833 static void
834 chanserv_flush_note_type(struct note_type *ntype)
835 {
836     struct chanData *cData;
837     for(cData = channelList; cData; cData = cData->next)
838         dict_remove(cData->notes, ntype->name);
839 }
840
841 static void
842 chanserv_truncate_notes(struct note_type *ntype)
843 {
844     struct chanData *cData;
845     struct note *note;
846     unsigned int size = sizeof(*note) + ntype->max_length;
847     
848     for(cData = channelList; cData; cData = cData->next) {
849         note = dict_find(cData->notes, ntype->name, NULL);
850         if(!note)
851             continue;
852         if(strlen(note->note) <= ntype->max_length)
853             continue;
854         dict_remove2(cData->notes, ntype->name, 1);
855         note = realloc(note, size);
856         note->note[ntype->max_length] = 0;
857         dict_insert(cData->notes, ntype->name, note);
858     }
859 }
860
861 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
862
863 static struct note *
864 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
865 {
866     struct note *note;
867     unsigned int len = strlen(text);
868
869     if(len > type->max_length) len = type->max_length;
870     note = calloc(1, sizeof(*note) + len);
871     note->type = type;
872     strncpy(note->setter, setter, sizeof(note->setter)-1);
873     memcpy(note->note, text, len);
874     note->note[len] = 0;
875     dict_insert(channel->notes, type->name, note);
876     type->refs++;
877     return note;
878 }
879
880 static void
881 chanserv_free_note(void *data)
882 {
883     struct note *note = data;
884
885     chanserv_deref_note_type(note->type);
886     assert(note->type->refs > 0); /* must use delnote to remove the type */
887     free(note);
888 }
889
890 static MODCMD_FUNC(cmd_createnote) {
891     struct note_type *ntype;
892     unsigned int arg = 1, existed = 0, max_length;
893
894     if((ntype = dict_find(note_types, argv[1], NULL)))
895         existed = 1;
896     else
897         ntype = chanserv_create_note_type(argv[arg]);
898     if(!irccasecmp(argv[++arg], "privileged"))
899     {
900         arg++;
901         ntype->set_access_type = NOTE_SET_PRIVILEGED;
902         ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
903     }
904     else if(!irccasecmp(argv[arg], "channel"))
905     {
906         unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
907         if(!ulvl)
908         {
909             reply("CSMSG_INVALID_ACCESS", argv[arg]);
910             goto fail;
911         }
912         ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
913         ntype->set_access.min_ulevel = ulvl;
914     }
915     else if(!irccasecmp(argv[arg], "setter"))
916     {
917         ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
918     }
919     else
920     {
921         reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
922         goto fail;
923     }
924
925     if(!irccasecmp(argv[++arg], "privileged"))
926         ntype->visible_type = NOTE_VIS_PRIVILEGED;
927     else if(!irccasecmp(argv[arg], "channel_users"))
928         ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
929     else if(!irccasecmp(argv[arg], "all"))
930         ntype->visible_type = NOTE_VIS_ALL;
931     else {
932         reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
933         goto fail;
934     }
935
936     if((arg+1) >= argc) {
937         reply("MSG_MISSING_PARAMS", argv[0]);
938         goto fail;
939     }
940     max_length = strtoul(argv[++arg], NULL, 0);
941     if(max_length < 20 || max_length > 450)
942     {
943         reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
944         goto fail;
945     }
946     if(existed && (max_length < ntype->max_length))
947     {
948         ntype->max_length = max_length;
949         chanserv_truncate_notes(ntype);
950     }
951     ntype->max_length = max_length;
952
953     if(existed)
954         reply("CSMSG_NOTE_MODIFIED", ntype->name);    
955     else
956         reply("CSMSG_NOTE_CREATED", ntype->name);
957     return 1;
958
959 fail:
960     if(!existed)
961         dict_remove(note_types, ntype->name);
962     return 0;
963 }
964
965 static MODCMD_FUNC(cmd_removenote) {
966     struct note_type *ntype;
967     int force;
968
969     ntype = dict_find(note_types, argv[1], NULL);
970     force = (argc > 2) && !irccasecmp(argv[2], "force");
971     if(!ntype)
972     {
973         reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
974         return 0;
975     }
976     if(ntype->refs > 1)
977     {
978         if(!force)
979         {
980             reply("CSMSG_NOTE_TYPE_USED", ntype->name);
981             return 0;
982         }
983         chanserv_flush_note_type(ntype);
984     }
985     dict_remove(note_types, argv[1]);
986     reply("CSMSG_NOTE_DELETED", argv[1]);
987     return 1;
988 }
989
990 static int
991 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
992 {
993     if(!orig)
994         return 0;
995     if(orig->modes_set & change->modes_clear)
996         return 1;
997     if(orig->modes_clear & change->modes_set)
998         return 1;
999     if((orig->modes_set & MODE_KEY)
1000        && strcmp(orig->new_key, change->new_key))
1001         return 1;
1002     if((orig->modes_set & MODE_LIMIT)
1003        && (orig->new_limit != change->new_limit))
1004         return 1;
1005     return 0;
1006 }
1007
1008 static char max_length_text[MAXLEN+1][16];
1009
1010 static struct helpfile_expansion
1011 chanserv_expand_variable(const char *variable)
1012 {
1013     struct helpfile_expansion exp;
1014
1015     if(!irccasecmp(variable, "notes"))
1016     {
1017         dict_iterator_t it;
1018         exp.type = HF_TABLE;
1019         exp.value.table.length = 1;
1020         exp.value.table.width = 3;
1021         exp.value.table.flags = 0;
1022         exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1023         exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1024         exp.value.table.contents[0][0] = "Note Type";
1025         exp.value.table.contents[0][1] = "Visibility";
1026         exp.value.table.contents[0][2] = "Max Length";
1027         for(it=dict_first(note_types); it; it=iter_next(it))
1028         {
1029             struct note_type *ntype = iter_data(it);
1030             int row;
1031
1032             if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1033             row = exp.value.table.length++;
1034             exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1035             exp.value.table.contents[row][0] = ntype->name;
1036             exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1037                 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1038                 "unknown";
1039             if(!max_length_text[ntype->max_length][0])
1040                 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1041             exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1042         }
1043         return exp;
1044     }
1045
1046     exp.type = HF_STRING;
1047     exp.value.str = NULL;
1048     return exp;
1049 }
1050
1051 static struct chanData*
1052 register_channel(struct chanNode *cNode, char *registrar)
1053 {
1054     struct chanData *channel;
1055     enum levelOption lvlOpt;
1056     enum charOption chOpt;
1057
1058     channel = calloc(1, sizeof(struct chanData));
1059
1060     channel->notes = dict_new();
1061     dict_set_free_data(channel->notes, chanserv_free_note);
1062
1063     channel->registrar = strdup(registrar);
1064     channel->registered = now;
1065     channel->visited = now;
1066     channel->limitAdjusted = now;
1067     channel->flags = CHANNEL_DEFAULT_FLAGS;
1068     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1069         channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1070     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1071         channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1072
1073     channel->prev = NULL;
1074     channel->next = channelList;
1075
1076     if(channelList)
1077         channelList->prev = channel;
1078     channelList = channel;
1079     registered_channels++;
1080
1081     channel->channel = cNode;
1082     LockChannel(cNode);
1083     cNode->channel_info = channel;
1084
1085     return channel;
1086 }
1087
1088 static struct userData*
1089 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1090 {
1091     struct userData *ud;
1092
1093     if(access > UL_OWNER)
1094         return NULL;
1095
1096     ud = calloc(1, sizeof(*ud));
1097     ud->channel = channel;
1098     ud->handle = handle;
1099     ud->seen = seen;
1100     ud->access = access;
1101     ud->info = info ? strdup(info) : NULL;
1102
1103     ud->prev = NULL;
1104     ud->next = channel->users;
1105     if(channel->users)
1106         channel->users->prev = ud;
1107     channel->users = ud;
1108
1109     channel->userCount++;
1110     userCount++;
1111
1112     ud->u_prev = NULL;
1113     ud->u_next = ud->handle->channels;
1114     if(ud->u_next)
1115         ud->u_next->u_prev = ud;
1116     ud->handle->channels = ud;
1117
1118     return ud;
1119 }
1120
1121 static void unregister_channel(struct chanData *channel, const char *reason);
1122
1123 void
1124 del_channel_user(struct userData *user, int do_gc)
1125 {
1126     struct chanData *channel = user->channel;
1127
1128     channel->userCount--;
1129     userCount--;
1130
1131     if(user->prev)
1132         user->prev->next = user->next;
1133     else
1134         channel->users = user->next;
1135     if(user->next)
1136         user->next->prev = user->prev;
1137
1138     if(user->u_prev)
1139         user->u_prev->u_next = user->u_next;
1140     else
1141         user->handle->channels = user->u_next;
1142     if(user->u_next)
1143         user->u_next->u_prev = user->u_prev;
1144
1145     free(user->info);
1146     free(user);
1147     if(do_gc && !channel->users && !IsProtected(channel))
1148         unregister_channel(channel, "lost all users.");
1149 }
1150
1151 static void expire_ban(void *data);
1152
1153 static struct banData*
1154 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1155 {
1156     struct banData *bd;
1157     unsigned int ii, l1, l2;
1158
1159     if(!mask)
1160         return NULL;
1161
1162     bd = malloc(sizeof(struct banData));
1163
1164     bd->channel = channel;
1165     bd->set = set;
1166     bd->triggered = triggered;
1167     bd->expires = expires;
1168
1169     for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1170     {
1171         extern const char *hidden_host_suffix;
1172         const char *old_name = chanserv_conf.old_ban_names->list[ii];
1173         char *new_mask;
1174
1175         l1 = strlen(mask);
1176         l2 = strlen(old_name);
1177         if(l2+2 > l1)
1178             continue;
1179         if(irccasecmp(mask + l1 - l2, old_name))
1180             continue;
1181         new_mask = alloca(MAXLEN);
1182         sprintf(new_mask, "%.*s%s", l1-l2, mask, hidden_host_suffix);
1183         mask = new_mask;
1184     }
1185     safestrncpy(bd->mask, mask, sizeof(bd->mask));
1186     if(owner)
1187         safestrncpy(bd->owner, owner, sizeof(bd->owner));
1188     bd->reason = reason ? strdup(reason) : NULL;
1189
1190     if(expires)
1191         timeq_add(expires, expire_ban, bd);
1192
1193     bd->prev = NULL;
1194     bd->next = channel->bans;
1195     if(channel->bans)
1196         channel->bans->prev = bd;
1197     channel->bans = bd;
1198     channel->banCount++;
1199     banCount++;
1200
1201     return bd;
1202 }
1203
1204 static void
1205 del_channel_ban(struct banData *ban)
1206 {
1207     ban->channel->banCount--;
1208     banCount--;
1209
1210     if(ban->prev)
1211         ban->prev->next = ban->next;
1212     else
1213         ban->channel->bans = ban->next;
1214
1215     if(ban->next)
1216         ban->next->prev = ban->prev;
1217
1218     if(ban->expires)
1219         timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1220
1221     if(ban->reason)
1222         free(ban->reason);
1223
1224     free(ban);
1225 }
1226
1227 static void
1228 expire_ban(void *data)
1229 {
1230     struct banData *bd = data;
1231     if(!IsSuspended(bd->channel))
1232     {
1233         struct banList bans;
1234         struct mod_chanmode change;
1235         unsigned int ii;
1236         bans = bd->channel->channel->banlist;
1237         change.modes_set = change.modes_clear = 0;
1238         change.argc = 0;
1239         for(ii=0; ii<bans.used; ii++)
1240         {
1241             if(!strcmp(bans.list[ii]->ban, bd->mask))
1242             {
1243                 change.argc = 1;
1244                 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1245                 change.args[0].hostmask = bd->mask;
1246                 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1247                 break;
1248             }
1249         }
1250     }
1251     bd->expires = 0;
1252     del_channel_ban(bd);
1253 }
1254
1255 static void chanserv_expire_suspension(void *data);
1256
1257 static void
1258 unregister_channel(struct chanData *channel, const char *reason)
1259 {
1260     char msgbuf[MAXLEN];
1261
1262     /* After channel unregistration, the following must be cleaned
1263        up:
1264        - Channel information.
1265        - Channel users.
1266        - Channel bans.
1267        - Channel suspension data.
1268        - Timeq entries. (Except timed bans, which are handled elsewhere.)
1269     */
1270
1271     if(!channel)
1272         return;
1273
1274     timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1275
1276     while(channel->users)
1277         del_channel_user(channel->users, 0);
1278
1279     while(channel->bans)
1280         del_channel_ban(channel->bans);
1281
1282     if(channel->topic) free(channel->topic);
1283     if(channel->registrar) free(channel->registrar);
1284     if(channel->greeting) free(channel->greeting);
1285     if(channel->user_greeting) free(channel->user_greeting);
1286     if(channel->topic_mask) free(channel->topic_mask);
1287
1288     if(channel->prev) channel->prev->next = channel->next;
1289     else channelList = channel->next;
1290
1291     if(channel->next) channel->next->prev = channel->prev;
1292
1293     if(channel->suspended)
1294     {
1295         struct chanNode *cNode = channel->channel;
1296         struct suspended *suspended, *next_suspended;
1297
1298         for(suspended = channel->suspended; suspended; suspended = next_suspended)
1299         {
1300             next_suspended = suspended->previous;
1301             free(suspended->suspender);
1302             free(suspended->reason);
1303             if(suspended->expires)
1304                 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1305             free(suspended);
1306         }
1307
1308         if(cNode)
1309             cNode->channel_info = NULL;
1310     }
1311     channel->channel->channel_info = NULL;
1312
1313     if(channel->notes)
1314         dict_delete(channel->notes);
1315     sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1316     if(!IsSuspended(channel))
1317         DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1318     global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1319     UnlockChannel(channel->channel);
1320     free(channel);
1321     registered_channels--;
1322 }
1323
1324 static void
1325 expire_channels(UNUSED_ARG(void *data))
1326 {
1327     struct chanData *channel, *next;
1328     struct userData *user;
1329     char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1330
1331     intervalString(delay, chanserv_conf.channel_expire_delay);
1332     sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1333
1334     for(channel = channelList; channel; channel = next)
1335     {
1336         next = channel->next;
1337
1338         /* See if the channel can be expired. */
1339         if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1340            || IsProtected(channel))
1341             continue;
1342
1343         /* Make sure there are no high-ranking users still in the channel. */
1344         for(user=channel->users; user; user=user->next)
1345             if(user->present && (user->access >= UL_PRESENT))
1346                 break;
1347         if(user)
1348             continue;
1349
1350         /* Unregister the channel */
1351         log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1352         unregister_channel(channel, "registration expired.");
1353     }
1354
1355     if(chanserv_conf.channel_expire_frequency)
1356         timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1357 }
1358
1359 static int
1360 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1361 {
1362     char protect = channel->chOpts[chProtect];
1363     struct userData *cs_victim, *cs_aggressor;
1364
1365     /* Don't protect if no one is to be protected, someone is attacking
1366        himself, or if the aggressor is an IRC Operator. */
1367     if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1368         return 0;
1369
1370     /* Don't protect if the victim isn't authenticated (because they
1371        can't be a channel user), unless we are to protect non-users
1372        also. */
1373     cs_victim = GetChannelAccess(channel, victim->handle_info);
1374     if(protect != 'a' && !cs_victim)
1375         return 0;
1376
1377     /* Protect if the aggressor isn't a user because at this point,
1378        the aggressor can only be less than or equal to the victim. */
1379     cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1380     if(!cs_aggressor)
1381         return 1;
1382
1383     /* If the aggressor was a user, then the victim can't be helped. */
1384     if(!cs_victim)
1385         return 0;
1386
1387     switch(protect)
1388     {
1389     case 'l':
1390         if(cs_victim->access > cs_aggressor->access)
1391             return 1;
1392         break;
1393     case 'a':
1394     case 'e':
1395         if(cs_victim->access >= cs_aggressor->access)
1396             return 1;
1397         break;
1398     }
1399
1400     return 0;
1401 }
1402
1403 static int
1404 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1405 {
1406     struct chanData *cData = channel->channel_info;
1407     struct userData *cs_victim;
1408
1409     if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1410         || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1411        && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1412     {
1413         send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1414         return 0;
1415     }
1416
1417     return 1;
1418 }
1419
1420 static int
1421 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1422 {
1423     if(IsService(victim))
1424     {
1425         send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1426         return 0;
1427     }
1428
1429     if(protect_user(victim, user, channel->channel_info))
1430     {
1431         send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1432         return 0;
1433     }
1434
1435     return 1;
1436 }
1437
1438 static struct do_not_register *
1439 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1440 {
1441     struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1442     safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1443     safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1444     strcpy(dnr->reason, reason);
1445     dnr->set = now;
1446     if(dnr->chan_name[0] == '*')
1447         dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1448     else if(strpbrk(dnr->chan_name, "*?"))
1449         dict_insert(mask_dnrs, dnr->chan_name, dnr);
1450     else
1451         dict_insert(plain_dnrs, dnr->chan_name, dnr);
1452     return dnr;
1453 }
1454
1455 static struct dnrList
1456 chanserv_find_dnrs(const char *chan_name, struct handle_info *handle)
1457 {
1458     struct dnrList list;
1459     dict_iterator_t it;
1460     struct do_not_register *dnr;
1461
1462     dnrList_init(&list);
1463     if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1464         dnrList_append(&list, dnr);
1465     if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1466         dnrList_append(&list, dnr);
1467     if(chan_name)
1468         for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1469             if(match_ircglob(chan_name, iter_key(it)))
1470                 dnrList_append(&list, iter_data(it));
1471     return list;
1472 }
1473
1474 static unsigned int
1475 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, struct handle_info *handle)
1476 {
1477     struct dnrList list;
1478     struct do_not_register *dnr;
1479     unsigned int ii;
1480     char buf[INTERVALLEN];
1481
1482     list = chanserv_find_dnrs(chan_name, handle);
1483     for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1484     {
1485         dnr = list.list[ii];
1486         if(dnr->set)
1487         {
1488             strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1489             reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1490         }
1491         else
1492             reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1493     }
1494     if(ii < list.used)
1495         reply("CSMSG_MORE_DNRS", list.used - ii);
1496     free(list.list);
1497     return ii;
1498 }
1499
1500 struct do_not_register *
1501 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1502 {
1503     struct do_not_register *dnr;
1504     dict_iterator_t it;
1505
1506     if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1507         return dnr;
1508     if(chan_name)
1509     {
1510         if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1511             return dnr;
1512         for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1513             if(match_ircglob(chan_name, iter_key(it)))
1514                 return iter_data(it);
1515     }
1516     return NULL;
1517 }
1518
1519 static CHANSERV_FUNC(cmd_noregister)
1520 {
1521     const char *target;
1522     struct do_not_register *dnr;
1523     char buf[INTERVALLEN];
1524     unsigned int matches;
1525
1526     if(argc < 2)
1527     {
1528         dict_iterator_t it;
1529
1530         reply("CSMSG_DNR_SEARCH_RESULTS");
1531         matches = 0;
1532         for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1533         {
1534             dnr = iter_data(it);
1535             if(dnr->set)
1536                 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1537             else
1538                 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1539             matches++;
1540         }
1541         for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1542         {
1543             dnr = iter_data(it);
1544             if(dnr->set)
1545                 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1546             else
1547                 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1548             matches++;
1549         }
1550         for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1551         {
1552             dnr = iter_data(it);
1553             if(dnr->set)
1554                 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1555             else
1556                 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1557             matches++;
1558         }
1559
1560         if(matches)
1561             reply("MSG_MATCH_COUNT", matches);
1562         else
1563             reply("MSG_NO_MATCHES");
1564         return 0;
1565     }
1566
1567     target = argv[1];
1568
1569     if(!IsChannelName(target) && (*target != '*'))
1570     {
1571         reply("CSMSG_NOT_DNR", target);
1572         return 0;
1573     }
1574
1575     if(argc > 2)
1576     {
1577         const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1578         if((*target == '*') && !get_handle_info(target + 1))
1579         {
1580             reply("MSG_HANDLE_UNKNOWN", target + 1);
1581             return 0;
1582         }
1583         chanserv_add_dnr(target, user->handle_info->handle, reason);
1584         reply("CSMSG_NOREGISTER_CHANNEL", target);
1585         return 1;
1586     }
1587
1588     reply("CSMSG_DNR_SEARCH_RESULTS");
1589     if(*target == '*')
1590         matches = chanserv_show_dnrs(user, cmd, NULL, get_handle_info(target + 1));
1591     else
1592         matches = chanserv_show_dnrs(user, cmd, target, NULL);
1593     if(!matches)
1594         reply("MSG_NO_MATCHES");
1595     return 0;
1596 }
1597
1598 static CHANSERV_FUNC(cmd_allowregister)
1599 {
1600     const char *chan_name = argv[1];
1601
1602     if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1603     {
1604         dict_remove(handle_dnrs, chan_name+1);
1605         reply("CSMSG_DNR_REMOVED", chan_name);
1606     }
1607     else if(dict_find(plain_dnrs, chan_name, NULL))
1608     {
1609         dict_remove(plain_dnrs, chan_name);
1610         reply("CSMSG_DNR_REMOVED", chan_name);
1611     }
1612     else if(dict_find(mask_dnrs, chan_name, NULL))
1613     {
1614         dict_remove(mask_dnrs, chan_name);
1615         reply("CSMSG_DNR_REMOVED", chan_name);
1616     }
1617     else
1618     {
1619         reply("CSMSG_NO_SUCH_DNR", chan_name);
1620         return 0;
1621     }
1622     return 1;
1623 }
1624
1625 unsigned int
1626 chanserv_get_owned_count(struct handle_info *hi)
1627 {
1628     struct userData *cList;
1629     unsigned int owned;
1630
1631     for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1632         if(cList->access == UL_OWNER)
1633             owned++;
1634     return owned;
1635 }
1636
1637 static CHANSERV_FUNC(cmd_register)
1638 {
1639     struct mod_chanmode *change;
1640     struct handle_info *handle;
1641     struct chanData *cData;
1642     struct modeNode *mn;
1643     char reason[MAXLEN];
1644     char *chan_name;
1645     unsigned int new_channel, force=0;
1646     struct do_not_register *dnr;
1647
1648     if(channel)
1649     {
1650         if(channel->channel_info)
1651         {
1652             reply("CSMSG_ALREADY_REGGED", channel->name);
1653             return 0;
1654         }
1655
1656         if(channel->bad_channel)
1657         {
1658             reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1659             return 0;
1660         }
1661
1662         if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1663         {
1664             reply("CSMSG_MUST_BE_OPPED", channel->name);
1665             return 0;
1666         }
1667
1668         new_channel = 0;
1669         chan_name = channel->name;
1670     }
1671     else
1672     {
1673         if((argc < 2) || !IsChannelName(argv[1]))
1674         {
1675             reply("MSG_NOT_CHANNEL_NAME");
1676             return 0;
1677         }
1678
1679         if(opserv_bad_channel(argv[1]))
1680         {
1681             reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1682             return 0;
1683         }
1684
1685         new_channel = 1;
1686         chan_name = argv[1];
1687     }
1688
1689     if(argc >= (new_channel+2))
1690     {
1691         if(!IsHelping(user))
1692         {
1693             reply("CSMSG_PROXY_FORBIDDEN");
1694             return 0;
1695         }
1696
1697         if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1698             return 0;
1699         force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1700         dnr = chanserv_is_dnr(chan_name, handle);
1701     }
1702     else
1703     {
1704         handle = user->handle_info;
1705         dnr = chanserv_is_dnr(chan_name, handle);
1706     }
1707     if(dnr && !force)
1708     {
1709         if(!IsHelping(user))
1710             reply("CSMSG_DNR_CHANNEL", chan_name);
1711         else
1712             chanserv_show_dnrs(user, cmd, chan_name, handle);
1713         return 0;
1714     }
1715
1716     if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1717     {
1718         reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1719         return 0;
1720     }
1721
1722     if(new_channel)
1723         channel = AddChannel(argv[1], now, NULL, NULL);
1724
1725     cData = register_channel(channel, user->handle_info->handle);
1726     add_channel_user(cData, handle, UL_OWNER, 0, NULL);
1727     scan_handle_presence(channel, handle, NULL);
1728     cData->modes = chanserv_conf.default_modes;
1729     change = mod_chanmode_dup(&cData->modes, 1);
1730     change->args[change->argc].mode = MODE_CHANOP;
1731     change->args[change->argc].member = AddChannelUser(chanserv, channel);
1732     change->argc++;
1733     mod_chanmode_announce(chanserv, channel, change);
1734     mod_chanmode_free(change);
1735
1736     /* Initialize the channel's max user record. */
1737     cData->max = channel->members.used;
1738
1739     if(handle != user->handle_info)
1740         reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1741     else
1742         reply("CSMSG_REG_SUCCESS", channel->name);
1743
1744     sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1745     global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1746     return 1;
1747 }
1748
1749 static const char *
1750 make_confirmation_string(struct userData *uData)
1751 {
1752     static char strbuf[16];
1753     char *src;
1754     unsigned int accum;
1755
1756     accum = 0;
1757     for(src = uData->handle->handle; *src; )
1758         accum = accum * 31 + toupper(*src++);
1759     if(uData->channel)
1760         for(src = uData->channel->channel->name; *src; )
1761             accum = accum * 31 + toupper(*src++);
1762     sprintf(strbuf, "%08x", accum);
1763     return strbuf;
1764 }
1765
1766 static CHANSERV_FUNC(cmd_unregister)
1767 {
1768     char *name;
1769     char reason[MAXLEN];
1770     struct chanData *cData;
1771     struct userData *uData;
1772
1773     cData = channel->channel_info;
1774     if(!cData)
1775     {
1776         reply("CSMSG_NOT_REGISTERED", channel->name);
1777         return 0;
1778     }
1779
1780     uData = GetChannelUser(cData, user->handle_info);
1781     if(!uData || (uData->access < UL_OWNER))
1782     {
1783         reply("CSMSG_NO_ACCESS");
1784         return 0;
1785     }
1786
1787     if(IsProtected(cData))
1788     {
1789         reply("CSMSG_UNREG_NODELETE", channel->name);
1790         return 0;
1791     }
1792
1793     if(!IsHelping(user))
1794     {
1795         const char *confirm_string;
1796         if(IsSuspended(cData))
1797         {
1798             reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1799             return 0;
1800         }
1801         confirm_string = make_confirmation_string(uData);
1802         if((argc < 2) || strcmp(argv[1], confirm_string))
1803         {
1804             reply("CSMSG_CONFIRM_UNREG", confirm_string);
1805             return 0;
1806         }
1807     }
1808
1809     sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1810     name = strdup(channel->name);
1811     unregister_channel(cData, reason);
1812     reply("CSMSG_UNREG_SUCCESS", name);
1813     free(name);
1814     return 1;
1815 }
1816
1817 static CHANSERV_FUNC(cmd_move)
1818 {
1819     struct chanNode *target;
1820     struct modeNode *mn;
1821     struct userData *uData;
1822     char reason[MAXLEN];
1823     struct do_not_register *dnr;
1824
1825     REQUIRE_PARAMS(2);
1826
1827     if(IsProtected(channel->channel_info))
1828     {
1829         reply("CSMSG_MOVE_NODELETE", channel->name);
1830         return 0;
1831     }
1832
1833     if(!IsChannelName(argv[1]))
1834     {
1835         reply("MSG_NOT_CHANNEL_NAME");
1836         return 0;
1837     }
1838
1839     if(opserv_bad_channel(argv[1]))
1840     {
1841         reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1842         return 0;
1843     }
1844
1845     if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1846     {
1847         for(uData = channel->channel_info->users; uData; uData = uData->next)
1848         {
1849             if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1850             {
1851                 if(!IsHelping(user))
1852                     reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1853                 else
1854                     chanserv_show_dnrs(user, cmd, argv[1], uData->handle);
1855                 return 0;
1856             }
1857         }
1858     }
1859
1860     if(!(target = GetChannel(argv[1])))
1861     {
1862         target = AddChannel(argv[1], now, NULL, NULL);
1863         LockChannel(target);
1864         if(!IsSuspended(channel->channel_info))
1865             AddChannelUser(chanserv, target);
1866     }
1867     else if(target->channel_info)
1868     {
1869         reply("CSMSG_ALREADY_REGGED", target->name);
1870         return 0;
1871     }
1872     else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1873             && !IsHelping(user))
1874     {
1875         reply("CSMSG_MUST_BE_OPPED", target->name);
1876         return 0;
1877     }
1878     else if(!IsSuspended(channel->channel_info))
1879     {
1880         struct mod_chanmode change;
1881         change.modes_set = change.modes_clear = 0;
1882         change.argc = 1;
1883         change.args[0].mode = MODE_CHANOP;
1884         change.args[0].member = AddChannelUser(chanserv, target);
1885         mod_chanmode_announce(chanserv, target, &change);
1886     }
1887
1888     /* Move the channel_info to the target channel; it
1889        shouldn't be necessary to clear timeq callbacks
1890        for the old channel. */
1891     target->channel_info = channel->channel_info;
1892     target->channel_info->channel = target;
1893     channel->channel_info = NULL;
1894
1895     reply("CSMSG_MOVE_SUCCESS", target->name);
1896
1897     sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1898     if(!IsSuspended(target->channel_info))
1899     {
1900         char reason2[MAXLEN];
1901         sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1902         DelChannelUser(chanserv, channel, reason2, 0);
1903     }
1904     UnlockChannel(channel);
1905     global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1906     return 1;
1907 }
1908
1909 static void
1910 merge_users(struct chanData *source, struct chanData *target)
1911 {
1912     struct userData *suData, *tuData, *next;
1913     dict_iterator_t it;
1914     dict_t merge;
1915
1916     merge = dict_new();
1917
1918     /* Insert the source's users into the scratch area. */
1919     for(suData = source->users; suData; suData = suData->next)
1920         dict_insert(merge, suData->handle->handle, suData);
1921
1922     /* Iterate through the target's users, looking for
1923        users common to both channels. The lower access is
1924        removed from either the scratch area or target user
1925        list. */
1926     for(tuData = target->users; tuData; tuData = next)
1927     {
1928         struct userData *choice;
1929
1930         next = tuData->next;
1931
1932         /* If a source user exists with the same handle as a target
1933            channel's user, resolve the conflict by removing one. */
1934         suData = dict_find(merge, tuData->handle->handle, NULL);
1935         if(!suData)
1936             continue;
1937
1938         /* Pick the data we want to keep. */
1939         /* If the access is the same, use the later seen time. */
1940         if(suData->access == tuData->access)
1941             choice = (suData->seen > tuData->seen) ? suData : tuData;
1942         else /* Otherwise, keep the higher access level. */
1943             choice = (suData->access > tuData->access) ? suData : tuData;
1944
1945         /* Remove the user that wasn't picked. */
1946         if(choice == tuData)
1947         {
1948             dict_remove(merge, suData->handle->handle);
1949             del_channel_user(suData, 0);
1950         }
1951         else
1952             del_channel_user(tuData, 0);
1953     }
1954
1955     /* Move the remaining users to the target channel. */
1956     for(it = dict_first(merge); it; it = iter_next(it))
1957     {
1958         suData = iter_data(it);
1959
1960         /* Insert the user into the target channel's linked list. */
1961         suData->prev = NULL;
1962         suData->next = target->users;
1963         suData->channel = target;
1964
1965         if(target->users)
1966             target->users->prev = suData;
1967         target->users = suData;
1968
1969         /* Update the user counts for the target channel; the
1970            source counts are left alone. */
1971         target->userCount++;
1972     }
1973
1974     /* Possible to assert (source->users == NULL) here. */
1975     source->users = NULL;
1976     dict_delete(merge);
1977 }
1978
1979 static void
1980 merge_bans(struct chanData *source, struct chanData *target)
1981 {
1982     struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
1983
1984     /* Hold on to the original head of the target ban list
1985        to avoid comparing source bans with source bans. */
1986     tFront = target->bans;
1987
1988     /* Perform a totally expensive O(n*m) merge, ick. */
1989     for(sbData = source->bans; sbData; sbData = sNext)
1990     {
1991         /* Flag to track whether the ban's been moved
1992            to the destination yet. */
1993         int moved = 0;
1994
1995         /* Possible to assert (sbData->prev == NULL) here. */
1996         sNext = sbData->next;
1997
1998         for(tbData = tFront; tbData; tbData = tNext)
1999         {
2000             tNext = tbData->next;
2001
2002             /* Perform two comparisons between each source
2003                and target ban, conflicts are resolved by
2004                keeping the broader ban and copying the later
2005                expiration and triggered time. */
2006             if(match_ircglobs(tbData->mask, sbData->mask))
2007             {
2008                 /* There is a broader ban in the target channel that
2009                    overrides one in the source channel; remove the 
2010                    source ban and break. */
2011                 if(sbData->expires > tbData->expires)
2012                     tbData->expires = sbData->expires;
2013                 if(sbData->triggered > tbData->triggered)
2014                     tbData->triggered = sbData->triggered;
2015                 del_channel_ban(sbData);
2016                 break;
2017             }
2018             else if(match_ircglobs(sbData->mask, tbData->mask))
2019             {
2020                 /* There is a broader ban in the source channel that
2021                    overrides one in the target channel; remove the
2022                    target ban, fall through and move the source over. */
2023                 if(tbData->expires > sbData->expires)
2024                     sbData->expires = tbData->expires;
2025                 if(tbData->triggered > sbData->triggered)
2026                     sbData->triggered = tbData->triggered;
2027                 if(tbData == tFront)
2028                     tFront = tNext;
2029                 del_channel_ban(tbData);
2030             }
2031
2032             /* Source bans can override multiple target bans, so
2033                we allow a source to run through this loop multiple
2034                times, but we can only move it once. */
2035             if(moved)
2036                 continue;
2037             moved = 1;
2038
2039             /* Remove the source ban from the source ban list. */
2040             if(sbData->next)
2041                 sbData->next->prev = sbData->prev;
2042
2043             /* Modify the source ban's associated channel. */
2044             sbData->channel = target;
2045
2046             /* Insert the ban into the target channel's linked list. */
2047             sbData->prev = NULL;
2048             sbData->next = target->bans;
2049
2050             if(target->bans)
2051                 target->bans->prev = sbData;
2052             target->bans = sbData;
2053
2054             /* Update the user counts for the target channel. */
2055             target->banCount++;
2056         }
2057     }
2058
2059     /* Possible to assert (source->bans == NULL) here. */
2060     source->bans = NULL;
2061 }
2062
2063 static void
2064 merge_data(struct chanData *source, struct chanData *target)
2065 {
2066     if(source->visited > target->visited)
2067         target->visited = source->visited;
2068 }
2069
2070 static void
2071 merge_channel(struct chanData *source, struct chanData *target)
2072 {
2073     merge_users(source, target);
2074     merge_bans(source, target);
2075     merge_data(source, target);
2076 }
2077
2078 static CHANSERV_FUNC(cmd_merge)
2079 {
2080     struct userData *target_user;
2081     struct chanNode *target;
2082     char reason[MAXLEN];
2083
2084     REQUIRE_PARAMS(2);
2085
2086     /* Make sure the target channel exists and is registered to the user
2087        performing the command. */
2088     if(!(target = GetChannel(argv[1])))
2089     {
2090         reply("MSG_INVALID_CHANNEL");
2091         return 0;
2092     }
2093
2094     if(!target->channel_info)
2095     {
2096         reply("CSMSG_NOT_REGISTERED", target->name);
2097         return 0;
2098     }
2099
2100     if(IsProtected(channel->channel_info))
2101     {
2102         reply("CSMSG_MERGE_NODELETE");
2103         return 0;
2104     }
2105
2106     if(IsSuspended(target->channel_info))
2107     {
2108         reply("CSMSG_MERGE_SUSPENDED");
2109         return 0;
2110     }
2111
2112     if(channel == target)
2113     {
2114         reply("CSMSG_MERGE_SELF");
2115         return 0;
2116     }
2117
2118     target_user = GetChannelUser(target->channel_info, user->handle_info);
2119     if(!target_user || (target_user->access < UL_OWNER))
2120     {
2121         reply("CSMSG_MERGE_NOT_OWNER");
2122         return 0;
2123     }
2124
2125     /* Merge the channel structures and associated data. */
2126     merge_channel(channel->channel_info, target->channel_info);
2127     sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2128     unregister_channel(channel->channel_info, reason);
2129     reply("CSMSG_MERGE_SUCCESS", target->name);
2130     return 1;
2131 }
2132
2133 static CHANSERV_FUNC(cmd_opchan)
2134 {
2135     struct mod_chanmode change;
2136     if(!IsHelping(user) && !channel->channel_info->may_opchan)
2137     {
2138         reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2139         return 0;
2140     }
2141     channel->channel_info->may_opchan = 0;
2142     change.modes_set = change.modes_clear = 0;
2143     change.argc = 1;
2144     change.args[0].mode = MODE_CHANOP;
2145     change.args[0].member = GetUserMode(channel, chanserv);
2146     mod_chanmode_announce(chanserv, channel, &change);
2147     reply("CSMSG_OPCHAN_DONE", channel->name);
2148     return 1;
2149 }
2150
2151 static CHANSERV_FUNC(cmd_adduser)
2152 {
2153     struct userData *actee;
2154     struct userData *actor;
2155     struct handle_info *handle;
2156     unsigned short access;
2157
2158     REQUIRE_PARAMS(3);
2159
2160     if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2161     {
2162         reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2163         return 0;
2164     }
2165
2166     access = user_level_from_name(argv[1], UL_OWNER);
2167     if(!access)
2168     {
2169         reply("CSMSG_INVALID_ACCESS", argv[1]);
2170         return 0;
2171     }
2172
2173     actor = GetChannelUser(channel->channel_info, user->handle_info);
2174     if(actor->access <= access)
2175     {
2176         reply("CSMSG_NO_BUMP_ACCESS");
2177         return 0;
2178     }
2179
2180     if(!(handle = modcmd_get_handle_info(user, argv[2])))
2181         return 0;
2182
2183     if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2184     {
2185         reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2186         return 0;
2187     }
2188
2189     actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2190     scan_handle_presence(channel, handle, NULL);
2191     reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2192     return 1;
2193 }
2194
2195 static CHANSERV_FUNC(cmd_clvl)
2196 {
2197     struct handle_info *handle;
2198     struct userData *victim;
2199     struct userData *actor;
2200     unsigned short new_access;
2201     int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2202
2203     REQUIRE_PARAMS(3);
2204
2205     actor = GetChannelUser(channel->channel_info, user->handle_info);
2206
2207     if(!(handle = modcmd_get_handle_info(user, argv[1])))
2208         return 0;
2209
2210     if(handle == user->handle_info && !privileged)
2211     {
2212         reply("CSMSG_NO_SELF_CLVL");
2213         return 0;
2214     }
2215
2216     if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2217     {
2218         reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2219         return 0;
2220     }
2221
2222     if(actor->access <= victim->access && !privileged)
2223     {
2224         reply("MSG_USER_OUTRANKED", handle->handle);
2225         return 0;
2226     }
2227
2228     new_access = user_level_from_name(argv[2], UL_OWNER);
2229
2230     if(!new_access)
2231     {
2232         reply("CSMSG_INVALID_ACCESS", argv[2]);
2233         return 0;
2234     }
2235
2236     if(new_access >= actor->access && !privileged)
2237     {
2238         reply("CSMSG_NO_BUMP_ACCESS");
2239         return 0;
2240     }
2241
2242     victim->access = new_access;
2243     reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2244     return 1;
2245 }
2246
2247 static CHANSERV_FUNC(cmd_deluser)
2248 {
2249     struct handle_info *handle;
2250     struct userData *victim;
2251     struct userData *actor;
2252     unsigned short access;
2253     char *chan_name;
2254
2255     REQUIRE_PARAMS(2);
2256
2257     actor = GetChannelUser(channel->channel_info, user->handle_info);
2258
2259     if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2260         return 0;
2261
2262     if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2263     {
2264         reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2265         return 0;
2266     }
2267
2268     if(argc > 2)
2269     {
2270         access = user_level_from_name(argv[1], UL_OWNER);
2271         if(!access)
2272         {
2273             reply("CSMSG_INVALID_ACCESS", argv[1]);
2274             return 0;
2275         }
2276         if(access != victim->access)
2277         {
2278             reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2279             return 0;
2280         }
2281     }
2282     else
2283     {
2284         access = victim->access;
2285     }
2286
2287     if((actor->access <= victim->access) && !IsHelping(user))
2288     {
2289         reply("MSG_USER_OUTRANKED", victim->handle->handle);
2290         return 0;
2291     }
2292
2293     chan_name = strdup(channel->name);
2294     del_channel_user(victim, 1);
2295     reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2296     free(chan_name);
2297     return 1;
2298 }
2299
2300 static int
2301 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2302 {
2303     struct userData *actor, *uData, *next;
2304
2305     actor = GetChannelUser(channel->channel_info, user->handle_info);
2306
2307     if(min_access > max_access)
2308     {
2309         reply("CSMSG_BAD_RANGE", min_access, max_access);
2310         return 0;
2311     }
2312
2313     if((actor->access <= max_access) && !IsHelping(user))
2314     {
2315         reply("CSMSG_NO_ACCESS");
2316         return 0;
2317     }
2318
2319     for(uData = channel->channel_info->users; uData; uData = next)
2320     {
2321         next = uData->next;
2322
2323         if((uData->access >= min_access)
2324            && (uData->access <= max_access)
2325            && match_ircglob(uData->handle->handle, mask))
2326             del_channel_user(uData, 1);
2327     }
2328
2329     reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2330     return 1;
2331 }
2332
2333 static CHANSERV_FUNC(cmd_mdelowner)
2334 {
2335     return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2336 }
2337
2338 static CHANSERV_FUNC(cmd_mdelcoowner)
2339 {
2340     return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2341 }
2342
2343 static CHANSERV_FUNC(cmd_mdelmaster)
2344 {
2345     return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2346 }
2347
2348 static CHANSERV_FUNC(cmd_mdelop)
2349 {
2350     return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2351 }
2352
2353 static CHANSERV_FUNC(cmd_mdelpeon)
2354 {
2355     return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2356 }
2357
2358 static int
2359 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2360 {
2361     struct banData *bData, *next;
2362     char interval[INTERVALLEN];
2363     unsigned int count;
2364     time_t limit;
2365
2366     count = 0;
2367     limit = now - duration;
2368     for(bData = channel->channel_info->bans; bData; bData = next)
2369     {
2370         next = bData->next;
2371
2372         if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2373             continue;
2374
2375         del_channel_ban(bData);
2376         count++;
2377     }
2378
2379     intervalString(interval, duration);
2380     send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2381     return 1;
2382 }
2383
2384 static int
2385 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2386 {
2387     struct userData *actor, *uData, *next;
2388     char interval[INTERVALLEN];
2389     unsigned int count;
2390     time_t limit;
2391
2392     actor = GetChannelUser(channel->channel_info, user->handle_info);
2393     if(min_access > max_access)
2394     {
2395         send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2396         return 0;
2397     }
2398
2399     if((actor->access <= max_access) && !IsHelping(user))
2400     {
2401         send_message(user, chanserv, "CSMSG_NO_ACCESS");
2402         return 0;
2403     }
2404
2405     count = 0;
2406     limit = now - duration;
2407     for(uData = channel->channel_info->users; uData; uData = next)
2408     {
2409         next = uData->next;
2410
2411         if((uData->seen > limit) || uData->present)
2412             continue;
2413
2414         if(((uData->access >= min_access) && (uData->access <= max_access))
2415            || (max_access && (uData->access < actor->access)))
2416         {
2417             del_channel_user(uData, 1);
2418             count++;
2419         }
2420     }
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 && !isdigit(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 < value))
4957         {
4958             reply("CSMSG_BAD_SETLEVEL");
4959             return 0;
4960         }
4961         cData->lvlOpts[option] = value;
4962     }
4963     reply(levelOptions[option].format_name, cData->lvlOpts[option]);
4964     return argc > 1;
4965 }
4966
4967 static MODCMD_FUNC(chan_opt_enfops)
4968 {
4969     return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
4970 }
4971
4972 static MODCMD_FUNC(chan_opt_giveops)
4973 {
4974     return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
4975 }
4976
4977 static MODCMD_FUNC(chan_opt_enfmodes)
4978 {
4979     return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
4980 }
4981
4982 static MODCMD_FUNC(chan_opt_enftopic)
4983 {
4984     return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
4985 }
4986
4987 static MODCMD_FUNC(chan_opt_pubcmd)
4988 {
4989     return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
4990 }
4991
4992 static MODCMD_FUNC(chan_opt_setters)
4993 {
4994     return channel_level_option(lvlSetters, CSFUNC_ARGS);
4995 }
4996
4997 static MODCMD_FUNC(chan_opt_ctcpusers)
4998 {
4999     return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5000 }
5001
5002 static int
5003 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5004 {
5005     struct chanData *cData = channel->channel_info;
5006     int count = charOptions[option].count, index;
5007
5008     if(argc > 1)
5009     {
5010         index = atoi(argv[1]);
5011
5012         if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5013         {
5014             reply("CSMSG_INVALID_NUMERIC", index);
5015             /* Show possible values. */
5016             for(index = 0; index < count; index++)
5017                 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5018             return 0;
5019         }
5020
5021         cData->chOpts[option] = charOptions[option].values[index].value;
5022     }
5023     else
5024     {
5025         /* Find current option value. */
5026       find_value:
5027         for(index = 0;
5028             (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5029             index++);
5030         if(index == count)
5031         {
5032             /* Somehow, the option value is corrupt; reset it to the default. */
5033             cData->chOpts[option] = charOptions[option].default_value;
5034             goto find_value;
5035         }
5036     }
5037
5038     reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5039     return 1;
5040 }
5041
5042 static MODCMD_FUNC(chan_opt_protect)
5043 {
5044     return channel_multiple_option(chProtect, CSFUNC_ARGS);
5045 }
5046
5047 static MODCMD_FUNC(chan_opt_toys)
5048 {
5049     return channel_multiple_option(chToys, CSFUNC_ARGS);
5050 }
5051
5052 static MODCMD_FUNC(chan_opt_ctcpreaction)
5053 {
5054     return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5055 }
5056
5057 static MODCMD_FUNC(chan_opt_topicrefresh)
5058 {
5059     return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5060 }
5061
5062 static struct svccmd_list set_shows_list;
5063
5064 static void
5065 handle_svccmd_unbind(struct svccmd *target) {
5066     unsigned int ii;
5067     for(ii=0; ii<set_shows_list.used; ++ii)
5068         if(target == set_shows_list.list[ii])
5069             set_shows_list.used = 0;
5070 }
5071
5072 static CHANSERV_FUNC(cmd_set)
5073 {
5074     struct svccmd *subcmd;
5075     char buf[MAXLEN];
5076     unsigned int ii;
5077
5078     /* Check if we need to (re-)initialize set_shows_list. */
5079     if(!set_shows_list.used)
5080     {
5081         if(!set_shows_list.size)
5082         {
5083             set_shows_list.size = chanserv_conf.set_shows->used;
5084             set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5085         }
5086         for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5087         {
5088             const char *name = chanserv_conf.set_shows->list[ii];
5089             sprintf(buf, "%s %s", argv[0], name);
5090             subcmd = dict_find(cmd->parent->commands, buf, NULL);
5091             if(!subcmd)
5092             {
5093                 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5094                 continue;
5095             }
5096             svccmd_list_append(&set_shows_list, subcmd);
5097         }
5098     }
5099
5100     if(argc < 2)
5101     {
5102         reply("CSMSG_CHANNEL_OPTIONS");
5103         for(ii = 0; ii < set_shows_list.used; ii++)
5104         {
5105             subcmd = set_shows_list.list[ii];
5106             subcmd->command->func(user, channel, 1, argv+1, subcmd);
5107         }
5108         return 1;
5109     }
5110
5111     sprintf(buf, "%s %s", argv[0], argv[1]);
5112     subcmd = dict_find(cmd->parent->commands, buf, NULL);
5113     if(!subcmd)
5114     {
5115         reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5116         return 0;
5117     }
5118     if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5119     {
5120         reply("CSMSG_NO_ACCESS");
5121         return 0;
5122     }
5123
5124     return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5125 }
5126
5127 static int
5128 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5129 {
5130     struct userData *uData;
5131
5132     uData = GetChannelAccess(channel->channel_info, user->handle_info);
5133     if(!uData)
5134     {
5135         reply("CSMSG_NOT_USER", channel->name);
5136         return 0;
5137     }
5138
5139     if(argc < 2)
5140     {
5141         /* Just show current option value. */
5142     }
5143     else if(enabled_string(argv[1]))
5144     {
5145         uData->flags |= mask;
5146     }
5147     else if(disabled_string(argv[1]))
5148     {
5149         uData->flags &= ~mask;
5150     }
5151     else
5152     {
5153         reply("MSG_INVALID_BINARY", argv[1]);
5154         return 0;
5155     }
5156
5157     reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5158     return 1;
5159 }
5160
5161 static MODCMD_FUNC(user_opt_noautoop)
5162 {
5163     struct userData *uData;
5164
5165     uData = GetChannelAccess(channel->channel_info, user->handle_info);
5166     if(!uData)
5167     {
5168         reply("CSMSG_NOT_USER", channel->name);
5169         return 0;
5170     }
5171     if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5172         return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5173     else
5174         return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5175 }
5176
5177 static MODCMD_FUNC(user_opt_autoinvite)
5178 {
5179     return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5180 }
5181
5182 static MODCMD_FUNC(user_opt_info)
5183 {
5184     struct userData *uData;
5185     char *infoline;
5186
5187     uData = GetChannelAccess(channel->channel_info, user->handle_info);
5188
5189     if(!uData)
5190     {
5191         /* If they got past the command restrictions (which require access)
5192          * but fail this test, we have some fool with security override on.
5193          */
5194         reply("CSMSG_NOT_USER", channel->name);
5195         return 0;
5196     }
5197
5198     if(argc > 1)
5199     {
5200         infoline = unsplit_string(argv + 1, argc - 1, NULL);
5201         if(uData->info)
5202             free(uData->info);
5203         if(infoline[0] == '*' && infoline[1] == 0)
5204             uData->info = NULL;
5205         else
5206             uData->info = strdup(infoline);
5207     }
5208     if(uData->info)
5209         reply("CSMSG_USET_INFO", uData->info);
5210     else
5211         reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5212     return 1;
5213 }
5214
5215 struct svccmd_list uset_shows_list;
5216
5217 static CHANSERV_FUNC(cmd_uset)
5218 {
5219     struct svccmd *subcmd;
5220     char buf[MAXLEN];
5221     unsigned int ii;
5222
5223     /* Check if we need to (re-)initialize uset_shows_list. */
5224     if(!uset_shows_list.used)
5225     {
5226         char *options[] =
5227         {
5228             "NoAutoOp", "AutoInvite", "Info"
5229         };
5230
5231         if(!uset_shows_list.size)
5232         {
5233             uset_shows_list.size = ArrayLength(options);
5234             uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5235         }
5236         for(ii = 0; ii < ArrayLength(options); ii++)
5237         {
5238             const char *name = options[ii];
5239             sprintf(buf, "%s %s", argv[0], name);
5240             subcmd = dict_find(cmd->parent->commands, buf, NULL);
5241             if(!subcmd)
5242             {
5243                 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5244                 continue;
5245             }
5246             svccmd_list_append(&uset_shows_list, subcmd);
5247         }
5248     }
5249
5250     if(argc < 2)
5251     {
5252         /* Do this so options are presented in a consistent order. */
5253         reply("CSMSG_USER_OPTIONS");
5254         for(ii = 0; ii < uset_shows_list.used; ii++)
5255             uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5256         return 1;
5257     }
5258
5259     sprintf(buf, "%s %s", argv[0], argv[1]);
5260     subcmd = dict_find(cmd->parent->commands, buf, NULL);
5261     if(!subcmd)
5262     {
5263         reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5264         return 0;
5265     }
5266
5267     return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5268 }
5269
5270 static CHANSERV_FUNC(cmd_giveownership)
5271 {
5272     struct handle_info *new_owner_hi;
5273     struct userData *new_owner, *curr_user;
5274     struct chanData *cData = channel->channel_info;
5275     struct do_not_register *dnr;
5276     unsigned int force;
5277     unsigned short co_access;
5278     char reason[MAXLEN];
5279
5280     REQUIRE_PARAMS(2);
5281     curr_user = GetChannelAccess(cData, user->handle_info);
5282     force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5283     if(!curr_user || (curr_user->access != UL_OWNER))
5284     {
5285         struct userData *owner = NULL;
5286         for(curr_user = channel->channel_info->users;
5287             curr_user;
5288             curr_user = curr_user->next)
5289         {
5290             if(curr_user->access != UL_OWNER)
5291                 continue;
5292             if(owner)
5293             {
5294                 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5295                 return 0;
5296             }
5297             owner = curr_user;
5298         }
5299     }
5300     if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5301         return 0;
5302     if(new_owner_hi == user->handle_info)
5303     {
5304         reply("CSMSG_NO_TRANSFER_SELF");
5305         return 0;
5306     }
5307     new_owner = GetChannelAccess(cData, new_owner_hi);
5308     if(!new_owner)
5309     {
5310         reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5311         return 0;
5312     }
5313     if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5314     {
5315         reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5316         return 0;
5317     }
5318     if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5319         if(!IsHelping(user))
5320             reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5321         else
5322             chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5323         return 0;
5324     }
5325     if(new_owner->access >= UL_COOWNER)
5326         co_access = new_owner->access;
5327     else
5328         co_access = UL_COOWNER;
5329     new_owner->access = UL_OWNER;
5330     if(curr_user)
5331         curr_user->access = co_access;
5332     reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5333     sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5334     global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5335     return 1;
5336 }
5337
5338 static CHANSERV_FUNC(cmd_suspend)
5339 {
5340     struct handle_info *hi;
5341     struct userData *self, *target;
5342
5343     REQUIRE_PARAMS(2);
5344     if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5345     self = GetChannelUser(channel->channel_info, user->handle_info);
5346     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5347     {
5348         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5349         return 0;
5350     }
5351     if(target->access >= self->access)
5352     {
5353         reply("MSG_USER_OUTRANKED", hi->handle);
5354         return 0;
5355     }
5356     if(target->flags & USER_SUSPENDED)
5357     {
5358         reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5359         return 0;
5360     }
5361     if(target->present)
5362     {
5363         target->present = 0;
5364         target->seen = now;
5365     }
5366     target->flags |= USER_SUSPENDED;
5367     reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5368     return 1;
5369 }
5370
5371 static CHANSERV_FUNC(cmd_unsuspend)
5372 {
5373     struct handle_info *hi;
5374     struct userData *self, *target;
5375
5376     REQUIRE_PARAMS(2);
5377     if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5378     self = GetChannelUser(channel->channel_info, user->handle_info);
5379     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5380     {
5381         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5382         return 0;
5383     }
5384     if(target->access >= self->access)
5385     {
5386         reply("MSG_USER_OUTRANKED", hi->handle);
5387         return 0;
5388     }
5389     if(!(target->flags & USER_SUSPENDED))
5390     {
5391         reply("CSMSG_NOT_SUSPENDED", hi->handle);
5392         return 0;
5393     }
5394     target->flags &= ~USER_SUSPENDED;
5395     reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5396     return 1;
5397 }
5398
5399 static MODCMD_FUNC(cmd_deleteme)
5400 {
5401     struct handle_info *hi;
5402     struct userData *target;
5403     const char *confirm_string;
5404     unsigned short access;
5405     char *channel_name;
5406
5407     hi = user->handle_info;
5408     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5409     {
5410         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5411         return 0;
5412     }
5413     if(target->access == UL_OWNER)
5414     {
5415         reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5416         return 0;
5417     }
5418     confirm_string = make_confirmation_string(target);
5419     if((argc < 2) || strcmp(argv[1], confirm_string))
5420     {
5421         reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5422         return 0;
5423     }
5424     access = target->access;
5425     channel_name = strdup(channel->name);
5426     del_channel_user(target, 1);
5427     reply("CSMSG_DELETED_YOU", access, channel_name);
5428     free(channel_name);
5429     return 1;
5430 }
5431
5432 static void
5433 chanserv_refresh_topics(UNUSED_ARG(void *data))
5434 {
5435     unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5436     struct chanData *cData;
5437     char opt;
5438
5439     for(cData = channelList; cData; cData = cData->next)
5440     {
5441         if(IsSuspended(cData))
5442             continue;
5443         opt = cData->chOpts[chTopicRefresh];
5444         if(opt == 'n')
5445             continue;
5446         if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5447             continue;
5448         if(cData->topic)
5449             SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5450         cData->last_refresh = refresh_num;
5451     }
5452     timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5453 }
5454
5455 static CHANSERV_FUNC(cmd_unf)
5456 {
5457     if(channel)
5458     {
5459         char response[MAXLEN];
5460         const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5461         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
5462         irc_privmsg(cmd->parent->bot, channel->name, response);
5463     }
5464     else
5465         reply("CSMSG_UNF_RESPONSE");
5466     return 1;
5467 }
5468
5469 static CHANSERV_FUNC(cmd_ping)
5470 {
5471     if(channel)
5472     {
5473         char response[MAXLEN];
5474         const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5475         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
5476         irc_privmsg(cmd->parent->bot, channel->name, response);
5477     }
5478     else
5479         reply("CSMSG_PING_RESPONSE");
5480     return 1;
5481 }
5482
5483 static CHANSERV_FUNC(cmd_wut)
5484 {
5485     if(channel)
5486     {
5487         char response[MAXLEN];
5488         const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5489         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
5490         irc_privmsg(cmd->parent->bot, channel->name, response);
5491     }
5492     else
5493         reply("CSMSG_WUT_RESPONSE");
5494     return 1;
5495 }
5496
5497 static CHANSERV_FUNC(cmd_8ball)
5498 {
5499     unsigned int i, j, accum;
5500     const char *resp;
5501
5502     REQUIRE_PARAMS(2);
5503     accum = 0;
5504     for(i=1; i<argc; i++)
5505         for(j=0; argv[i][j]; j++)
5506             accum = (accum << 5) - accum + toupper(argv[i][j]);
5507     resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5508     if(channel)
5509     {
5510         char response[MAXLEN];
5511         sprintf(response, "\ 2%s\ 2: %s", user->nick, resp);
5512         irc_privmsg(cmd->parent->bot, channel->name, response);
5513     }
5514     else
5515         send_message_type(4, user, cmd->parent->bot, "%s", resp);
5516     return 1;
5517 }
5518
5519 static CHANSERV_FUNC(cmd_d)
5520 {
5521     unsigned long sides, count, modifier, ii, total;
5522     char response[MAXLEN], *sep;
5523     const char *fmt;
5524
5525     REQUIRE_PARAMS(2);
5526     if((count = strtoul(argv[1], &sep, 10)) <= 1)
5527         goto no_dice;
5528     if(sep[0] == 0)
5529     {
5530         sides = count;
5531         count = 1;
5532         modifier = 0;
5533     }
5534     else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5535             && (sides = strtoul(sep+1, &sep, 10)) > 1)
5536     {
5537         if(sep[0] == 0)
5538             modifier = 0;
5539         else if((sep[0] == '-') && isdigit(sep[1]))
5540             modifier = strtoul(sep, NULL, 10);
5541         else if((sep[0] == '+') && isdigit(sep[1]))
5542             modifier = strtoul(sep+1, NULL, 10);
5543         else
5544             goto no_dice;
5545     }
5546     else
5547     {
5548       no_dice:
5549         reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5550         return 0;
5551     }
5552     if(count > 10)
5553     {
5554         reply("CSMSG_BAD_DICE_COUNT", count, 10);
5555         return 0;
5556     }
5557     for(total = ii = 0; ii < count; ++ii)
5558         total += (rand() % sides) + 1;
5559     total += modifier;
5560
5561     if((count > 1) || modifier)
5562     {
5563         fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5564         sprintf(response, fmt, total, count, sides, modifier);
5565     }
5566     else
5567     {
5568         fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5569         sprintf(response, fmt, total, sides);
5570     }
5571     if(channel)
5572         send_target_message(5, channel->name, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5573     else
5574         send_message_type(4, user, cmd->parent->bot, "%s", response);
5575     return 1;
5576 }
5577
5578 static CHANSERV_FUNC(cmd_huggle)
5579 {
5580     char response[MAXLEN];
5581     const char *fmt;
5582     /* CTCP must be via PRIVMSG, never notice */
5583     if(channel)
5584     {
5585         fmt = user_find_message(user, "CSMSG_HUGGLES_HIM");
5586         sprintf(response, fmt, user->nick);
5587         irc_privmsg(cmd->parent->bot, channel->name, response);
5588     }
5589     else
5590     {
5591         fmt = user_find_message(user, "CSMSG_HUGGLES_YOU");
5592         irc_privmsg(cmd->parent->bot, user->nick, fmt);
5593     }
5594     return 1;
5595 }
5596
5597 static void
5598 chanserv_adjust_limit(void *data)
5599 {
5600     struct mod_chanmode change;
5601     struct chanData *cData = data;
5602     struct chanNode *channel = cData->channel;
5603     unsigned int limit;
5604
5605     if(IsSuspended(cData))
5606         return;
5607
5608     cData->limitAdjusted = now;
5609     limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5610     if(cData->modes.modes_set & MODE_LIMIT)
5611     {
5612         if(limit > cData->modes.new_limit)
5613             limit = cData->modes.new_limit;
5614         else if(limit == cData->modes.new_limit)
5615             return;
5616     }
5617
5618     change.modes_set = MODE_LIMIT;
5619     change.modes_clear = 0;
5620     change.new_limit = limit;
5621     change.argc = 0;
5622     mod_chanmode_announce(chanserv, channel, &change);
5623 }
5624
5625 static void
5626 handle_new_channel(struct chanNode *channel)
5627 {
5628     struct chanData *cData;
5629
5630     if(!(cData = channel->channel_info))
5631         return;
5632
5633     if(cData->modes.modes_set || cData->modes.modes_clear)
5634         mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5635
5636     if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5637         SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5638 }
5639
5640 /* Welcome to my worst nightmare. Warning: Read (or modify)
5641    the code below at your own risk. */
5642 static int
5643 handle_join(struct modeNode *mNode)
5644 {
5645     struct mod_chanmode change;
5646     struct userNode *user = mNode->user;
5647     struct chanNode *channel = mNode->channel;
5648     struct chanData *cData;
5649     struct userData *uData = NULL;
5650     struct banData *bData;
5651     struct handle_info *handle;
5652     unsigned int modes = 0, info = 0;
5653     char *greeting;
5654
5655     if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5656         return 0;
5657
5658     cData = channel->channel_info;
5659     if(channel->members.used > cData->max)
5660         cData->max = channel->members.used;
5661
5662     /* Check for bans.  If they're joining through a ban, one of two
5663      * cases applies:
5664      *   1: Join during a netburst, by riding the break.  Kick them
5665      *      unless they have ops or voice in the channel.
5666      *   2: They're allowed to join through the ban (an invite in
5667      *   ircu2.10, or a +e on Hybrid, or something).
5668      * If they're not joining through a ban, and the banlist is not
5669      * full, see if they're on the banlist for the channel.  If so,
5670      * kickban them.
5671      */
5672     if(user->uplink->burst && !mNode->modes)
5673     {
5674         unsigned int ii;
5675         for(ii = 0; ii < channel->banlist.used; ii++)
5676         {
5677             if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5678             {
5679                 /* Riding a netburst.  Naughty. */
5680                 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5681                 return 1;
5682             }
5683         }
5684     }
5685
5686     change.modes_set = change.modes_clear = 0;
5687     change.argc = 1;
5688     if(channel->banlist.used < MAXBANS)
5689     {
5690         /* Not joining through a ban. */
5691         for(bData = cData->bans;
5692                 bData && !user_matches_glob(user, bData->mask, 1);
5693                 bData = bData->next);
5694
5695         if(bData)
5696         {
5697             char kick_reason[MAXLEN];
5698             sprintf(kick_reason, "%s (%s)", bData->reason, bData->owner);
5699
5700             bData->triggered = now;
5701             if(bData != cData->bans)
5702             {
5703                 /* Shuffle the ban to the head of the list. */
5704                 if(bData->next) bData->next->prev = bData->prev;
5705                 if(bData->prev) bData->prev->next = bData->next;
5706
5707                 bData->prev = NULL;
5708                 bData->next = cData->bans;
5709
5710                 if(cData->bans)
5711                     cData->bans->prev = bData;
5712                 cData->bans = bData;
5713             }
5714
5715             change.args[0].mode = MODE_BAN;
5716             change.args[0].hostmask = bData->mask;
5717             mod_chanmode_announce(chanserv, channel, &change);
5718             KickChannelUser(user, channel, chanserv, kick_reason);
5719             return 1;
5720         }
5721     }
5722
5723     /* ChanServ will not modify the limits in join-flooded channels.
5724        It will also skip DynLimit processing when the user (or srvx)
5725        is bursting in, because there are likely more incoming. */
5726     if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5727        && !user->uplink->burst
5728        && !channel->join_flooded
5729        && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5730     {
5731         /* The user count has begun "bumping" into the channel limit,
5732            so set a timer to raise the limit a bit. Any previous
5733            timers are removed so three incoming users within the delay
5734            results in one limit change, not three. */
5735
5736         timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5737         timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5738     }
5739
5740     if(cData->lvlOpts[lvlGiveOps] == 0)
5741         modes |= MODE_CHANOP;
5742     else if((cData->flags & CHANNEL_VOICE_ALL) && !channel->join_flooded)
5743         modes |= MODE_VOICE;
5744
5745     greeting = cData->greeting;
5746     if(user->handle_info)
5747     {
5748         handle = user->handle_info;
5749
5750         if(IsHelper(user) && !IsHelping(user))
5751         {
5752             unsigned int ii;
5753             for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5754             {
5755                 if(channel == chanserv_conf.support_channels.list[ii])
5756                 {
5757                     HANDLE_SET_FLAG(user->handle_info, HELPING);
5758                     break;
5759                 }
5760             }
5761         }
5762
5763         uData = GetTrueChannelAccess(cData, handle);
5764         if(uData && !IsUserSuspended(uData))
5765         {
5766             /* Ops and above were handled by the above case. */
5767             if(IsUserAutoOp(uData))
5768             {
5769                 if(uData->access < cData->lvlOpts[lvlGiveOps])
5770                     modes |= MODE_VOICE;
5771                 else
5772                     modes |= MODE_CHANOP;
5773             }
5774             if(uData->access >= UL_PRESENT)
5775                 cData->visited = now;
5776
5777             uData->seen = now;
5778             uData->present = 1;
5779
5780             if(cData->user_greeting)
5781                 greeting = cData->user_greeting;
5782             if(uData->info
5783                && (cData->flags & CHANNEL_INFO_LINES)
5784                && ((now - uData->seen) >= chanserv_conf.info_delay)
5785                && !uData->present)
5786                 info = 1;
5787         }
5788     }
5789     if(!user->uplink->burst)
5790     {
5791         if(modes)
5792         {
5793             change.args[0].mode = modes;
5794             change.args[0].member = mNode;
5795             mod_chanmode_announce(chanserv, channel, &change);
5796         }
5797         if(greeting && !user->uplink->burst)
5798             send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5799         if(uData && info)
5800             send_target_message(4, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5801     }
5802     return 0;
5803 }
5804
5805 static void
5806 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5807 {
5808     struct mod_chanmode change;
5809     struct userData *channel;
5810     unsigned int ii, jj;
5811
5812     if(!user->handle_info)
5813         return;
5814
5815     change.modes_set = change.modes_clear = 0;
5816     change.argc = 1;
5817     for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5818     {
5819         struct chanNode *cn;
5820         struct modeNode *mn;
5821         if(IsSuspended(channel->channel) || !(cn = channel->channel->channel))
5822             continue;
5823
5824         mn = GetUserMode(cn, user);
5825         if(!mn)
5826         {
5827             if(!IsUserSuspended(channel)
5828                && IsUserAutoInvite(channel)
5829                && (cn->modes & (MODE_KEY | MODE_INVITEONLY))
5830                && !self->burst)
5831                 irc_invite(chanserv, user, cn);
5832             continue;
5833         }
5834
5835         if(channel->access >= UL_PRESENT)
5836             channel->channel->visited = now;
5837
5838         if(IsUserAutoOp(channel))
5839         {
5840             if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5841                 change.args[0].mode = MODE_CHANOP;
5842             else
5843                 change.args[0].mode = MODE_VOICE;
5844             change.args[0].member = mn;
5845             mod_chanmode_announce(chanserv, cn, &change);
5846         }
5847
5848         channel->seen = now;
5849         channel->present = 1;
5850     }
5851
5852     for(ii = 0; ii < user->channels.used; ++ii)
5853     {
5854         struct chanNode *channel = user->channels.list[ii]->channel;
5855         struct banData *ban;
5856
5857         if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
5858            || !channel->channel_info)
5859             continue;
5860         for(jj = 0; jj < channel->banlist.used; ++jj)
5861             if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
5862                 break;
5863         if(jj < channel->banlist.used)
5864             continue;
5865         for(ban = channel->channel_info->bans; ban; ban = ban->next)
5866         {
5867             char kick_reason[MAXLEN];
5868             if(!user_matches_glob(user, ban->mask, 1))
5869                 continue;
5870             change.args[0].mode = MODE_BAN;
5871             change.args[0].hostmask = ban->mask;
5872             mod_chanmode_announce(chanserv, channel, &change);
5873             sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
5874             KickChannelUser(user, channel, chanserv, kick_reason);
5875             ban->triggered = now;
5876             break;
5877         }
5878     }
5879
5880     if(IsSupportHelper(user))
5881     {
5882         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5883         {
5884             if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
5885             {
5886                 HANDLE_SET_FLAG(user->handle_info, HELPING);
5887                 break;
5888             }
5889         }
5890     }
5891 }
5892
5893 static void
5894 handle_part(struct userNode *user, struct chanNode *channel, UNUSED_ARG(const char *reason))
5895 {
5896     struct chanData *cData;
5897     struct userData *uData;
5898     struct handle_info *handle;
5899
5900     cData = channel->channel_info;
5901     if(!cData || IsSuspended(cData) || IsLocal(user)) return;
5902
5903     if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !channel->join_flooded)
5904     {
5905         /* Allow for a bit of padding so that the limit doesn't
5906            track the user count exactly, which could get annoying. */
5907         if((channel->limit - channel->members.used) > chanserv_conf.adjust_threshold + 5)
5908         {
5909             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5910             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5911         }
5912     }
5913
5914     if((handle = user->handle_info) && (uData = GetTrueChannelAccess(cData, handle)))
5915     {
5916         uData->seen = now;
5917         scan_handle_presence(channel, handle, user);
5918     }
5919
5920     if(IsHelping(user) && IsSupportHelper(user))
5921     {
5922         unsigned int ii, jj;
5923         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5924         {
5925             for(jj = 0; jj < user->channels.used; ++jj)
5926                 if(user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
5927                     break;
5928             if(jj < user->channels.used)
5929                 break;
5930         }
5931         if(ii == chanserv_conf.support_channels.used)
5932             HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
5933     }
5934 }
5935
5936 static void
5937 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
5938 {
5939     char *reason = "CSMSG_USER_PROTECTED";
5940
5941     if(!channel->channel_info || !kicker || IsService(kicker)
5942        || (kicker == victim) || IsSuspended(channel->channel_info)
5943        || (kicker->handle_info && kicker->handle_info == victim->handle_info))
5944         return;
5945
5946     if(protect_user(victim, kicker, channel->channel_info))
5947         KickChannelUser(kicker, channel, chanserv, reason);
5948 }
5949
5950 static int
5951 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
5952 {
5953     struct chanData *cData;
5954
5955     if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user)) return 0;
5956
5957     cData = channel->channel_info;
5958     if(bad_topic(channel, user, channel->topic))
5959     {
5960         send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
5961         if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
5962             SetChannelTopic(channel, chanserv, old_topic, 1);
5963         else if(cData->topic)
5964             SetChannelTopic(channel, chanserv, cData->topic, 1);
5965         return 1;
5966     }
5967     /* With topicsnarf, grab the topic and save it as the default topic. */
5968     if(cData->flags & CHANNEL_TOPIC_SNARF)
5969     {
5970         free(cData->topic);
5971         cData->topic = strdup(channel->topic);
5972     }
5973     return 0;
5974 }
5975
5976 static void
5977 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
5978 {
5979     struct mod_chanmode *bounce = NULL;
5980     unsigned int bnc, ii;
5981     char deopped = 0;
5982
5983     if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
5984         return;
5985
5986     if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
5987        && mode_lock_violated(&channel->channel_info->modes, change))
5988     {
5989         char correct[MAXLEN];
5990         bounce = mod_chanmode_alloc(change->argc + 1);
5991         *bounce = channel->channel_info->modes;
5992         mod_chanmode_format(&channel->channel_info->modes, correct);
5993         send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
5994     }
5995     for(ii = bnc = 0; ii < change->argc; ++ii)
5996     {
5997         if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
5998         {
5999             const struct userNode *victim = change->args[ii].member->user;
6000             if(!protect_user(victim, user, channel->channel_info))
6001                 continue;
6002             if(!bounce)
6003                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6004             if(!deopped)
6005             {
6006                 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6007                 bounce->args[bnc].member = GetUserMode(channel, user);
6008                 if(bounce->args[bnc].member)
6009                     bnc++;
6010             }
6011             bounce->args[bnc].mode = MODE_CHANOP;
6012             bounce->args[bnc].member = change->args[ii].member;
6013             bnc++;
6014             send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6015         }
6016         else if(change->args[ii].mode & MODE_CHANOP)
6017         {
6018             const struct userNode *victim = change->args[ii].member->user;
6019             if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6020                 continue;
6021             if(!bounce)
6022                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6023             bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6024             bounce->args[bnc].member = change->args[ii].member;
6025             bnc++;
6026         }
6027         else if(change->args[ii].mode & MODE_BAN)
6028         {
6029             const char *ban = change->args[ii].hostmask;
6030             if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6031                 continue;
6032             if(!bounce)
6033                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6034             bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6035             bounce->args[bnc].hostmask = ban;
6036             bnc++;
6037             send_message(user, chanserv, "CSMSG_MASK_PROTECTED", remove);
6038         }
6039     }
6040     if(bounce)
6041     {
6042         if((bounce->argc = bnc))
6043             mod_chanmode_announce(chanserv, channel, bounce);
6044         mod_chanmode_free(bounce);
6045     }
6046 }
6047
6048 static void
6049 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6050 {
6051     struct chanNode *channel;
6052     struct banData *bData;
6053     struct mod_chanmode change;
6054     unsigned int ii, jj;
6055     char kick_reason[MAXLEN];
6056
6057     change.modes_set = change.modes_clear = 0;
6058     change.argc = 1;
6059     change.args[0].mode = MODE_BAN;
6060     for(ii = 0; ii < user->channels.used; ++ii)
6061     {
6062         channel = user->channels.list[ii]->channel;
6063         /* Need not check for bans if they're opped or voiced. */
6064         if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6065             continue;
6066         /* Need not check for bans unless channel registration is active. */
6067         if(!channel->channel_info || IsSuspended(channel->channel_info))
6068             continue;
6069         /* Look for a matching ban already on the channel. */
6070         for(jj = 0; jj < channel->banlist.used; ++jj)
6071             if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6072                 break;
6073         /* Need not act if we found one. */
6074         if(jj < channel->banlist.used)
6075             continue;
6076         /* Look for a matching ban in this channel. */
6077         for(bData = channel->channel_info->bans; bData; bData = bData->next)
6078         {
6079             if(!user_matches_glob(user, bData->mask, 1))
6080                 continue;
6081             change.args[0].hostmask = bData->mask;
6082             mod_chanmode_announce(chanserv, channel, &change);
6083             sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6084             KickChannelUser(user, channel, chanserv, kick_reason);
6085             bData->triggered = now;
6086             break; /* we don't need to check any more bans in the channel */
6087         }
6088     }
6089 }
6090
6091 static void handle_rename(struct handle_info *handle, const char *old_handle)
6092 {
6093     struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6094
6095     if(dnr)
6096     {
6097         dict_remove2(handle_dnrs, old_handle, 1);
6098         safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6099         dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6100     }
6101 }
6102
6103 static void
6104 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6105 {
6106     struct userNode *h_user;
6107
6108     if(handle->channels)
6109     {
6110         for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6111             send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6112
6113         while(handle->channels)
6114             del_channel_user(handle->channels, 1);
6115     }
6116 }
6117
6118 static void
6119 handle_server_link(UNUSED_ARG(struct server *server))
6120 {
6121     struct chanData *cData;
6122
6123     for(cData = channelList; cData; cData = cData->next)
6124     {
6125         if(!IsSuspended(cData))
6126             cData->may_opchan = 1;
6127         if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6128            && !cData->channel->join_flooded
6129            && ((cData->channel->limit - cData->channel->members.used)
6130                < chanserv_conf.adjust_threshold))
6131         {
6132             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6133             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6134         }
6135     }
6136 }
6137
6138 static void
6139 chanserv_conf_read(void)
6140 {
6141     dict_t conf_node;
6142     const char *str;
6143     char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6144     struct mod_chanmode *change;
6145     struct string_list *strlist;
6146     struct chanNode *chan;
6147     unsigned int ii;
6148
6149     if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6150     {
6151         log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6152         return;
6153     }
6154     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6155         UnlockChannel(chanserv_conf.support_channels.list[ii]);
6156     chanserv_conf.support_channels.used = 0;
6157     if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6158     {
6159         for(ii = 0; ii < strlist->used; ++ii)
6160         {
6161             const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6162             if(!str2)
6163                 str2 = "+nt";
6164             chan = AddChannel(strlist->list[ii], now, str2, NULL);
6165             LockChannel(chan);
6166             channelList_append(&chanserv_conf.support_channels, chan);
6167         }
6168     }
6169     else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6170     {
6171         const char *str2;
6172         str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6173         if(!str2)
6174             str2 = "+nt";
6175         chan = AddChannel(str, now, str2, NULL);
6176         LockChannel(chan);
6177         channelList_append(&chanserv_conf.support_channels, chan);
6178     }
6179     str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6180     chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6181     str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6182     chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6183     str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6184     chanserv_conf.greeting_length = str ? atoi(str) : 120;
6185     str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6186     chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6187     str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6188     chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6189     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6190     chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6191     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6192     chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6193     str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6194     chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6195     str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6196     chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6197     str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6198     chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6199     str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6200     if(chanserv && str)
6201         NickChange(chanserv, str, 0);
6202     str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6203     chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6204     str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6205     chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6206     str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6207     chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6208     str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6209     chanserv_conf.max_owned = str ? atoi(str) : 5;
6210     str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6211     chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6212     str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6213     chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6214     str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6215     chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6216     str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6217     if(!str)
6218         str = "+nt";
6219     safestrncpy(mode_line, str, sizeof(mode_line));
6220     ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6221     if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6222     {
6223         chanserv_conf.default_modes = *change;
6224         mod_chanmode_free(change);
6225     }
6226     free_string_list(chanserv_conf.set_shows);
6227     strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6228     if(strlist)
6229         strlist = string_list_copy(strlist);
6230     else
6231     {
6232         static const char *list[] = {
6233             /* multiple choice options */
6234             "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6235             "PubCmd", "GiveOps", "EnfOps", "EnfModes", "EnfTopic", "Protect",
6236             "Toys", "Setters", "TopicRefresh", "CtcpUsers", "CtcpReaction",
6237             /* binary options */
6238             "Voice", "UserInfo", "DynLimit", "TopicSnarf", "PeonInvite", "NoDelete",
6239             /* delimiter */
6240             NULL };
6241         unsigned int ii;
6242         strlist = alloc_string_list(ArrayLength(list)-1);
6243         for(ii=0; list[ii]; ii++)
6244             string_list_append(strlist, strdup(list[ii]));
6245     }
6246     chanserv_conf.set_shows = strlist;
6247     /* We don't look things up now, in case the list refers to options
6248      * defined by modules initialized after this point.  Just mark the
6249      * function list as invalid, so it will be initialized.
6250      */
6251     set_shows_list.used = 0;
6252     free_string_list(chanserv_conf.eightball);
6253     strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6254     if(strlist)
6255     {
6256         strlist = string_list_copy(strlist);
6257     }
6258     else
6259     {
6260         strlist = alloc_string_list(4);
6261         string_list_append(strlist, strdup("Yes."));
6262         string_list_append(strlist, strdup("No."));
6263         string_list_append(strlist, strdup("Maybe so."));
6264     }
6265     chanserv_conf.eightball = strlist;
6266     free_string_list(chanserv_conf.old_ban_names);
6267     strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6268     if (strlist)
6269         strlist = string_list_copy(strlist);
6270     else
6271         strlist = alloc_string_list(2);
6272     chanserv_conf.old_ban_names = strlist;
6273 }
6274
6275 static void
6276 chanserv_note_type_read(const char *key, struct record_data *rd)
6277 {
6278     dict_t obj;
6279     struct note_type *ntype;
6280     const char *str;
6281
6282     if(!(obj = GET_RECORD_OBJECT(rd)))
6283     {
6284         log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6285         return;
6286     }
6287     if(!(ntype = chanserv_create_note_type(key)))
6288     {
6289         log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6290         return;
6291     }
6292
6293     /* Figure out set access */
6294     if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6295     {
6296         ntype->set_access_type = NOTE_SET_PRIVILEGED;
6297         ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6298     }
6299     else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6300     {
6301         ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6302         ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6303     }
6304     else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6305     {
6306         ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6307     }
6308     else
6309     {
6310         log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6311         ntype->set_access_type = NOTE_SET_PRIVILEGED;
6312         ntype->set_access.min_opserv = 0;
6313     }
6314
6315     /* Figure out visibility */
6316     if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6317         ntype->visible_type = NOTE_VIS_PRIVILEGED;
6318     else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6319         ntype->visible_type = NOTE_VIS_PRIVILEGED;
6320     else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6321         ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6322     else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6323         ntype->visible_type = NOTE_VIS_ALL;
6324     else
6325         ntype->visible_type = NOTE_VIS_PRIVILEGED;
6326
6327     str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6328     ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6329 }
6330
6331 static void
6332 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6333 {
6334     struct handle_info *handle;
6335     struct userData *uData;
6336     char *seen, *inf, *flags;
6337     time_t last_seen;
6338     unsigned short access;
6339
6340     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6341     {
6342         log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6343         return;
6344     }
6345
6346     access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6347     if(access > UL_OWNER)
6348     {
6349         log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6350         return;
6351     }
6352
6353     inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6354     seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6355     last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6356     flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6357     handle = get_handle_info(key);
6358     if(!handle)
6359     {
6360         log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6361         return;
6362     }
6363
6364     uData = add_channel_user(chan, handle, access, last_seen, inf);
6365     uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6366 }
6367
6368 static void
6369 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6370 {
6371     struct banData *bData;
6372     char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6373     time_t set_time, triggered_time, expires_time;
6374
6375     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6376     {
6377         log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6378         return;
6379     }
6380
6381     set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6382     triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6383     s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6384     s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6385     owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6386     reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6387
6388     set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6389     triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6390     if(s_expires)
6391         expires_time = (time_t)strtoul(s_expires, NULL, 0);
6392     else if(s_duration)
6393         expires_time = set_time + atoi(s_duration);
6394     else
6395         expires_time = 0;
6396
6397     if(expires_time && (expires_time < now))
6398         return;
6399
6400     bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6401 }
6402
6403 static struct suspended *
6404 chanserv_read_suspended(dict_t obj)
6405 {
6406     struct suspended *suspended = calloc(1, sizeof(*suspended));
6407     char *str;
6408     dict_t previous;
6409
6410     str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6411     suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6412     str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6413     suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6414     str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6415     suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6416     suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6417     suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6418     previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6419     suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6420     return suspended;
6421 }
6422
6423 static int
6424 chanserv_channel_read(const char *key, struct record_data *hir)
6425 {
6426     struct suspended *suspended;
6427     struct mod_chanmode *modes;
6428     struct chanNode *cNode;
6429     struct chanData *cData;
6430     struct dict *channel, *obj;
6431     char *str, *argv[10];
6432     dict_iterator_t it;
6433     unsigned int argc;
6434
6435     channel = hir->d.object;
6436
6437     str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6438     if(!str)
6439         str = "<unknown>";
6440     cNode = AddChannel(key, now, NULL, NULL);
6441     if(!cNode)
6442     {
6443         log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6444         return 0;
6445     }
6446     cData = register_channel(cNode, str);
6447     if(!cData)
6448     {
6449         log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6450         return 0;
6451     }
6452
6453     if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6454     {
6455         enum levelOption lvlOpt;
6456         enum charOption chOpt;
6457         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6458         {
6459             if(!(str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING)))
6460                 continue;
6461             cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6462         }
6463         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6464         {
6465             if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6466                 continue;
6467             cData->chOpts[chOpt] = str[0];
6468         }
6469         if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6470             cData->flags = atoi(str);
6471     }
6472     else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6473     {
6474         enum levelOption lvlOpt;
6475         enum charOption chOpt;
6476         unsigned int count;
6477
6478         cData->flags = base64toint(str, 5);
6479         count = strlen(str += 5);
6480         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6481         {
6482             unsigned short lvl;
6483             switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6484             {
6485             case 'c': lvl = UL_COOWNER; break;
6486             case 'm': lvl = UL_MASTER; break;
6487             case 'n': lvl = UL_OWNER+1; break;
6488             case 'o': lvl = UL_OP; break;
6489             case 'p': lvl = UL_PEON; break;
6490             case 'w': lvl = UL_OWNER; break;
6491             default: lvl = 0; break;
6492             }
6493             cData->lvlOpts[lvlOpt] = lvl;
6494         }
6495         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6496             cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6497     }
6498    
6499     if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6500     {
6501         suspended = chanserv_read_suspended(obj);
6502         cData->suspended = suspended;
6503         suspended->cData = cData;
6504         /* We could use suspended->expires and suspended->revoked to
6505          * set the CHANNEL_SUSPENDED flag, but we don't. */
6506     }
6507     else if(cData->flags & CHANNEL_SUSPENDED)
6508     {
6509         suspended = calloc(1, sizeof(*suspended));
6510         suspended->issued = 0;
6511         suspended->revoked = 0;
6512         str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6513         suspended->expires = str ? atoi(str) : 0;
6514         suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6515         str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6516         suspended->reason = strdup(str ? str : "No reason");
6517         suspended->previous = NULL;
6518         cData->suspended = suspended;
6519         suspended->cData = cData;
6520     }
6521     else
6522         suspended = NULL;
6523
6524     if((cData->flags & CHANNEL_SUSPENDED)
6525        && suspended->expires
6526        && (suspended->expires <= now))
6527     {
6528         cData->flags &= ~CHANNEL_SUSPENDED;
6529     }
6530
6531     if(!(cData->flags & CHANNEL_SUSPENDED))
6532     {
6533         struct mod_chanmode change;
6534         change.modes_set = change.modes_clear = 0;
6535         change.argc = 1;
6536         change.args[0].mode = MODE_CHANOP;
6537         change.args[0].member = AddChannelUser(chanserv, cNode);
6538         mod_chanmode_announce(chanserv, cNode, &change);
6539     }
6540     else if(suspended->expires > now)
6541     {
6542         timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6543     }
6544
6545     str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6546     cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6547     str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6548     cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6549     str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6550     cData->max = str ? atoi(str) : 0;
6551     str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6552     cData->greeting = str ? strdup(str) : NULL;
6553     str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6554     cData->user_greeting = str ? strdup(str) : NULL;
6555     str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6556     cData->topic_mask = str ? strdup(str) : NULL;
6557     str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6558     cData->topic = str ? strdup(str) : NULL;
6559
6560     if((str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6561        && (argc = split_line(str, 0, ArrayLength(argv), argv))
6562        && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6563         cData->modes = *modes;
6564         if(cData->modes.argc > 1)
6565             cData->modes.argc = 1;
6566         if(!IsSuspended(cData))
6567             mod_chanmode_announce(chanserv, cNode, &cData->modes);
6568         mod_chanmode_free(modes);
6569     }
6570
6571     obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6572     for(it = dict_first(obj); it; it = iter_next(it))
6573         user_read_helper(iter_key(it), iter_data(it), cData);
6574
6575     if(!cData->users && !IsProtected(cData))
6576     {
6577         log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6578         unregister_channel(cData, "has empty user list.");
6579         return 0;
6580     }
6581
6582     obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6583     for(it = dict_first(obj); it; it = iter_next(it))
6584         ban_read_helper(iter_key(it), iter_data(it), cData);
6585
6586     obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6587     for(it = dict_first(obj); it; it = iter_next(it))
6588     {
6589         struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6590         struct record_data *rd = iter_data(it);
6591         const char *note, *setter;
6592
6593         if(rd->type != RECDB_OBJECT)
6594         {
6595             log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6596         }
6597         else if(!ntype)
6598         {
6599             log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6600         }
6601         else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6602         {
6603             log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6604         }
6605         else
6606         {
6607             setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6608             if(!setter) setter = "<unknown>";
6609             chanserv_add_channel_note(cData, ntype, setter, note);
6610         }
6611     }
6612
6613     return 0;
6614 }
6615
6616 static void
6617 chanserv_dnr_read(const char *key, struct record_data *hir)
6618 {
6619     const char *setter, *reason, *str;
6620     struct do_not_register *dnr;
6621
6622     setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6623     if(!setter)
6624     {
6625         log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6626         return;
6627     }
6628     reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6629     if(!reason)
6630     {
6631         log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6632         return;
6633     }
6634     dnr = chanserv_add_dnr(key, setter, reason);
6635     if(!dnr)
6636         return;
6637     str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6638     if(str)
6639         dnr->set = atoi(str);
6640     else
6641         dnr->set = 0;
6642 }
6643
6644 static int
6645 chanserv_saxdb_read(struct dict *database)
6646 {
6647     struct dict *section;
6648     dict_iterator_t it;
6649
6650     if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6651         for(it = dict_first(section); it; it = iter_next(it))
6652             chanserv_note_type_read(iter_key(it), iter_data(it));
6653
6654     if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6655         for(it = dict_first(section); it; it = iter_next(it))
6656             chanserv_channel_read(iter_key(it), iter_data(it));
6657
6658     if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6659         for(it = dict_first(section); it; it = iter_next(it))
6660             chanserv_dnr_read(iter_key(it), iter_data(it));
6661
6662     return 0;
6663 }
6664
6665 static int
6666 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6667 {
6668     int high_present = 0;
6669     saxdb_start_record(ctx, KEY_USERS, 1);
6670     for(; uData; uData = uData->next)
6671     {
6672         if((uData->access >= UL_PRESENT) && uData->present)
6673             high_present = 1;
6674         saxdb_start_record(ctx, uData->handle->handle, 0);
6675         saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6676         saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6677         if(uData->flags)
6678             saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6679         if(uData->info)
6680             saxdb_write_string(ctx, KEY_INFO, uData->info);
6681         saxdb_end_record(ctx);
6682     }
6683     saxdb_end_record(ctx);
6684     return high_present;
6685 }
6686
6687 static void
6688 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6689 {
6690     if(!bData)
6691         return;
6692     saxdb_start_record(ctx, KEY_BANS, 1);
6693     for(; bData; bData = bData->next)
6694     {
6695         saxdb_start_record(ctx, bData->mask, 0);
6696         saxdb_write_int(ctx, KEY_SET, bData->set);
6697         if(bData->triggered)
6698             saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6699         if(bData->expires)
6700             saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6701         if(bData->owner[0])
6702             saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6703         if(bData->reason)
6704             saxdb_write_string(ctx, KEY_REASON, bData->reason);
6705         saxdb_end_record(ctx);
6706     }
6707     saxdb_end_record(ctx);
6708 }
6709
6710 static void
6711 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6712 {
6713     saxdb_start_record(ctx, name, 0);
6714     saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6715     saxdb_write_string(ctx, KEY_REASON, susp->reason);
6716     if(susp->issued)
6717         saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6718     if(susp->expires)
6719         saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6720     if(susp->revoked)
6721         saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6722     if(susp->previous)
6723         chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6724     saxdb_end_record(ctx);
6725 }
6726
6727 static void
6728 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6729 {
6730     char buf[MAXLEN];
6731     int high_present;
6732     enum levelOption lvlOpt;
6733     enum charOption chOpt;
6734
6735     saxdb_start_record(ctx, channel->channel->name, 1);
6736
6737     saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6738     saxdb_write_int(ctx, KEY_MAX, channel->max);
6739     if(channel->topic)
6740         saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6741     if(channel->registrar)
6742         saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6743     if(channel->greeting)
6744         saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6745     if(channel->user_greeting)
6746         saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6747     if(channel->topic_mask)
6748         saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6749     if(channel->suspended)
6750         chanserv_write_suspended(ctx, "suspended", channel->suspended);
6751
6752     saxdb_start_record(ctx, KEY_OPTIONS, 0);
6753     saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6754     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6755         saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6756     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6757     {
6758         buf[0] = channel->chOpts[chOpt];
6759         buf[1] = '\0';
6760         saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6761     }
6762     saxdb_end_record(ctx);
6763
6764     if(channel->modes.modes_set || channel->modes.modes_clear)
6765     {
6766         mod_chanmode_format(&channel->modes, buf);
6767         saxdb_write_string(ctx, KEY_MODES, buf);
6768     }
6769
6770     high_present = chanserv_write_users(ctx, channel->users);
6771     chanserv_write_bans(ctx, channel->bans);
6772
6773     if(dict_size(channel->notes))
6774     {
6775         dict_iterator_t it;
6776
6777         saxdb_start_record(ctx, KEY_NOTES, 1);
6778         for(it = dict_first(channel->notes); it; it = iter_next(it))
6779         {
6780             struct note *note = iter_data(it);
6781             saxdb_start_record(ctx, iter_key(it), 0);
6782             saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6783             saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6784             saxdb_end_record(ctx);
6785         }
6786         saxdb_end_record(ctx);
6787     }
6788
6789     saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6790     saxdb_end_record(ctx);
6791 }
6792
6793 static void
6794 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6795 {
6796     const char *str;
6797
6798     saxdb_start_record(ctx, ntype->name, 0);
6799     switch(ntype->set_access_type)
6800     {
6801     case NOTE_SET_CHANNEL_ACCESS:
6802         saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6803         break;
6804     case NOTE_SET_CHANNEL_SETTER:
6805         saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6806         break;
6807     case NOTE_SET_PRIVILEGED: default:
6808         saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6809         break;
6810     }
6811     switch(ntype->visible_type)
6812     {
6813     case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6814     case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6815     case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6816     }
6817     saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6818     saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6819     saxdb_end_record(ctx);
6820 }
6821
6822 static void
6823 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
6824 {
6825     struct do_not_register *dnr;
6826     dict_iterator_t it;
6827
6828     for(it = dict_first(dnrs); it; it = iter_next(it))
6829     {
6830         dnr = iter_data(it);
6831         saxdb_start_record(ctx, dnr->chan_name, 0);
6832         if(dnr->set)
6833             saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
6834         saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
6835         saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
6836         saxdb_end_record(ctx);
6837     }
6838 }
6839
6840 static int
6841 chanserv_saxdb_write(struct saxdb_context *ctx)
6842 {
6843     dict_iterator_t it;
6844     struct chanData *channel;
6845
6846     /* Notes */
6847     saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
6848     for(it = dict_first(note_types); it; it = iter_next(it))
6849         chanserv_write_note_type(ctx, iter_data(it));
6850     saxdb_end_record(ctx);
6851
6852     /* DNRs */
6853     saxdb_start_record(ctx, KEY_DNR, 1);
6854     write_dnrs_helper(ctx, handle_dnrs);
6855     write_dnrs_helper(ctx, plain_dnrs);
6856     write_dnrs_helper(ctx, mask_dnrs);
6857     saxdb_end_record(ctx);
6858
6859     /* Channels */
6860     saxdb_start_record(ctx, KEY_CHANNELS, 1);
6861     for(channel = channelList; channel; channel = channel->next)
6862         chanserv_write_channel(ctx, channel);
6863     saxdb_end_record(ctx);
6864
6865     return 0;
6866 }
6867
6868 static void
6869 chanserv_db_cleanup(void) {
6870     unsigned int ii;
6871     unreg_part_func(handle_part);
6872     while(channelList)
6873         unregister_channel(channelList, "terminating.");
6874     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6875         UnlockChannel(chanserv_conf.support_channels.list[ii]);
6876     free(chanserv_conf.support_channels.list);
6877     dict_delete(handle_dnrs);
6878     dict_delete(plain_dnrs);
6879     dict_delete(mask_dnrs);
6880     dict_delete(note_types);
6881     free_string_list(chanserv_conf.eightball);
6882     free_string_list(chanserv_conf.old_ban_names);
6883     free_string_list(chanserv_conf.set_shows);
6884     free(set_shows_list.list);
6885     free(uset_shows_list.list);
6886     while(helperList)
6887     {
6888         struct userData *helper = helperList;
6889         helperList = helperList->next;
6890         free(helper);
6891     }
6892 }
6893
6894 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
6895 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
6896 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
6897
6898 void
6899 init_chanserv(const char *nick)
6900 {
6901     CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
6902     conf_register_reload(chanserv_conf_read);
6903
6904     reg_server_link_func(handle_server_link);
6905
6906     reg_new_channel_func(handle_new_channel);
6907     reg_join_func(handle_join);
6908     reg_part_func(handle_part);
6909     reg_kick_func(handle_kick);
6910     reg_topic_func(handle_topic);
6911     reg_mode_change_func(handle_mode);
6912     reg_nick_change_func(handle_nick_change);
6913
6914     reg_auth_func(handle_auth);
6915     reg_handle_rename_func(handle_rename);
6916     reg_unreg_func(handle_unreg);
6917
6918     handle_dnrs = dict_new();
6919     dict_set_free_data(handle_dnrs, free);
6920     plain_dnrs = dict_new();
6921     dict_set_free_data(plain_dnrs, free);
6922     mask_dnrs = dict_new();
6923     dict_set_free_data(mask_dnrs, free);
6924
6925     reg_svccmd_unbind_func(handle_svccmd_unbind);
6926     chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
6927     DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
6928     DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
6929     DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
6930     DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
6931     DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
6932     DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
6933     DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
6934     DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
6935
6936     DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
6937     DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
6938
6939     DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6940     DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6941     DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6942     DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6943     DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
6944
6945     DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
6946     DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
6947     DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
6948     DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6949     DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6950
6951     DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6952     DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN, "access", "peon", NULL);
6953     DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6954     DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
6955
6956     DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
6957     DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
6958     DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
6959     DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
6960     DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
6961     DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
6962     DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
6963     DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
6964
6965     DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6966     DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6967     DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6968     DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
6969     DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
6970     DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
6971     DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
6972     DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
6973     DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6974     DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "peon", NULL);
6975     DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
6976     DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
6977     DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6978     DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6979
6980     DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
6981     DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
6982     DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
6983     DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
6984     DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "peon", NULL);
6985
6986     DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "peon", "flags", "+nolog", NULL);
6987     DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
6988
6989     DEFINE_COMMAND(access, 1, 0, "flags", "+nolog,+acceptchan", NULL);
6990     DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6991     DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6992     DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6993     DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6994     DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6995     DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6996     DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6997     DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6998     DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6999
7000     DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7001     DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7002
7003     DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7004     DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7005     DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7006     DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7007
7008     DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7009     DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7010     DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7011     DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7012     DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7013
7014     DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7015     DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7016     DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7017     DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7018     DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7019     DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7020
7021     /* Channel options */
7022     DEFINE_CHANNEL_OPTION(defaulttopic);
7023     DEFINE_CHANNEL_OPTION(topicmask);
7024     DEFINE_CHANNEL_OPTION(greeting);
7025     DEFINE_CHANNEL_OPTION(usergreeting);
7026     DEFINE_CHANNEL_OPTION(modes);
7027     DEFINE_CHANNEL_OPTION(enfops);
7028     DEFINE_CHANNEL_OPTION(giveops);
7029     DEFINE_CHANNEL_OPTION(protect);
7030     DEFINE_CHANNEL_OPTION(enfmodes);
7031     DEFINE_CHANNEL_OPTION(enftopic);
7032     DEFINE_CHANNEL_OPTION(pubcmd);
7033     DEFINE_CHANNEL_OPTION(voice);
7034     DEFINE_CHANNEL_OPTION(userinfo);
7035     DEFINE_CHANNEL_OPTION(dynlimit);
7036     DEFINE_CHANNEL_OPTION(topicsnarf);
7037     DEFINE_CHANNEL_OPTION(nodelete);
7038     DEFINE_CHANNEL_OPTION(toys);
7039     DEFINE_CHANNEL_OPTION(setters);
7040     DEFINE_CHANNEL_OPTION(topicrefresh);
7041     DEFINE_CHANNEL_OPTION(ctcpusers);
7042     DEFINE_CHANNEL_OPTION(ctcpreaction);
7043     DEFINE_CHANNEL_OPTION(peoninvite);
7044     modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7045
7046     /* Alias set topic to set defaulttopic for compatibility. */
7047     modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7048
7049     /* User options */
7050     DEFINE_USER_OPTION(noautoop);
7051     DEFINE_USER_OPTION(autoinvite);
7052     DEFINE_USER_OPTION(info);
7053
7054     /* Alias uset autovoice to uset autoop. */
7055     modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7056
7057     note_types = dict_new();
7058     dict_set_free_data(note_types, chanserv_deref_note_type);
7059     if(nick)
7060     {
7061         chanserv = AddService(nick, "Channel Services");
7062         service_register(chanserv, '!');
7063         reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7064     }
7065     saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7066
7067     if(chanserv_conf.channel_expire_frequency)
7068         timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7069
7070     if(chanserv_conf.refresh_period)
7071     {
7072         time_t next_refresh;
7073         next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7074         timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7075     }
7076     
7077     reg_exit_func(chanserv_db_cleanup);
7078     message_register_table(msgtab);
7079 }