Initial import (again)
[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;
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     for(start = curr = 1; curr < list->table.length; )
3318     {
3319         uData = list->users[curr-1];
3320         list->table.contents[curr++][0] = " ";
3321         if((curr == list->table.length) || (list->users[curr-1]->access != uData->access))
3322         {
3323             if(list->search)
3324                 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3325             else
3326                 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3327             temp = list->table.contents[--start];
3328             list->table.contents[start] = list->table.contents[0];
3329             tmp_table.contents = list->table.contents + start;
3330             tmp_table.length = curr - start;
3331             table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3332             list->table.contents[start] = temp;
3333             start = curr;
3334         }
3335     }
3336 }
3337
3338 static void
3339 def_list(struct listData *list)
3340 {
3341     const char *msg;
3342     if(list->search)
3343         send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3344     else
3345         send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3346     table_send(list->bot, list->user->nick, 0, NULL, list->table);
3347     if(list->table.length == 1)
3348     {
3349         msg = user_find_message(list->user, "MSG_NONE");
3350         send_message_type(4, list->user, list->bot, "  %s", msg);
3351     }
3352 }
3353
3354 static int
3355 userData_access_comp(const void *arg_a, const void *arg_b)
3356 {
3357     const struct userData *a = *(struct userData**)arg_a;
3358     const struct userData *b = *(struct userData**)arg_b;
3359     int res;
3360     if(a->access != b->access)
3361         res = b->access - a->access;
3362     else
3363         res = irccasecmp(a->handle->handle, b->handle->handle);
3364     return res;
3365 }
3366
3367 static int
3368 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3369 {
3370     void (*send_list)(struct listData *);
3371     struct userData *uData;
3372     struct listData lData;
3373     unsigned int matches;
3374     const char **ary;
3375
3376     lData.user = user;
3377     lData.bot = cmd->parent->bot;
3378     lData.channel = channel;
3379     lData.lowest = lowest;
3380     lData.highest = highest;
3381     lData.search = (argc > 1) ? argv[1] : NULL;
3382     send_list = zoot_list;
3383
3384     if(user->handle_info)
3385     {
3386         switch(user->handle_info->userlist_style)
3387         {
3388         case HI_STYLE_DEF: send_list = def_list; break;
3389         case HI_STYLE_ZOOT: send_list = zoot_list; break;
3390         }
3391     }
3392
3393     lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3394     matches = 0;
3395     for(uData = channel->channel_info->users; uData; uData = uData->next)
3396     {
3397         if((uData->access < lowest)
3398            || (uData->access > highest)
3399            || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3400             continue;
3401         lData.users[matches++] = uData;
3402     }
3403     qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3404
3405     lData.table.length = matches+1;
3406     lData.table.width = 4;
3407     lData.table.flags = TABLE_NO_FREE;
3408     lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3409     ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3410     lData.table.contents[0] = ary;
3411     ary[0] = "Access";
3412     ary[1] = "Account";
3413     ary[2] = "Last Seen";
3414     ary[3] = "Status";
3415     for(matches = 1; matches < lData.table.length; ++matches)
3416     {
3417         struct userData *uData = lData.users[matches-1];
3418         char seen[INTERVALLEN];
3419
3420         ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3421         lData.table.contents[matches] = ary;
3422         ary[0] = strtab(uData->access);
3423         ary[1] = uData->handle->handle;
3424         if(uData->present)
3425             ary[2] = "Here";
3426         else if(!uData->seen)
3427             ary[2] = "Never";
3428         else
3429             ary[2] = intervalString(seen, now - uData->seen);
3430         ary[2] = strdup(ary[2]);
3431         if(IsUserSuspended(uData))
3432             ary[3] = "Suspended";
3433         else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3434             ary[3] = "Vacation";
3435         else
3436             ary[3] = "Normal";
3437     }
3438     send_list(&lData);
3439     for(matches = 1; matches < lData.table.length; ++matches)
3440     {
3441         free((char*)lData.table.contents[matches][2]);
3442         free(lData.table.contents[matches]);
3443     }
3444     free(lData.table.contents[0]);
3445     free(lData.table.contents);
3446     return 1;
3447 }
3448
3449 static CHANSERV_FUNC(cmd_users)
3450 {
3451     return cmd_list_users(CSFUNC_ARGS, 0, UL_OWNER);
3452 }
3453
3454 static CHANSERV_FUNC(cmd_wlist)
3455 {
3456     return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3457 }
3458
3459 static CHANSERV_FUNC(cmd_clist)
3460 {
3461     return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3462 }
3463 static CHANSERV_FUNC(cmd_mlist)
3464 {
3465     return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3466 }
3467 static CHANSERV_FUNC(cmd_olist)
3468 {
3469     return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3470 }
3471 static CHANSERV_FUNC(cmd_plist)
3472 {
3473     return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3474 }
3475
3476 static CHANSERV_FUNC(cmd_bans)
3477 {
3478     struct helpfile_table tbl;
3479     unsigned int matches = 0, timed = 0, ii;
3480     char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3481     const char *msg_never, *triggered, *expires;
3482     struct banData *ban, **bans;
3483
3484     if(argc > 1)
3485         search = argv[1];
3486     else
3487         search = NULL;
3488
3489     bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3490
3491     for(ban = channel->channel_info->bans; ban; ban = ban->next)
3492     {
3493         if(search && !match_ircglobs(search, ban->mask))
3494             continue;
3495         bans[matches++] = ban;
3496         if(ban->expires)
3497             timed = 1;
3498     }
3499
3500     tbl.length = matches + 1;
3501     tbl.width = 4 + timed;
3502     tbl.flags = 0;
3503     tbl.flags = TABLE_NO_FREE;
3504     tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3505     tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3506     tbl.contents[0][0] = "Mask";
3507     tbl.contents[0][1] = "Set By";
3508     tbl.contents[0][2] = "Triggered";
3509     if(timed)
3510     {
3511         tbl.contents[0][3] = "Expires";
3512         tbl.contents[0][4] = "Reason";
3513     }
3514     else
3515         tbl.contents[0][3] = "Reason";
3516     if(!matches)
3517     {
3518         table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3519         reply("MSG_NONE");
3520         return 0;
3521     }
3522
3523     msg_never = user_find_message(user, "MSG_NEVER");
3524     for(ii = 0; ii < matches; )
3525     {
3526         ban = bans[ii];
3527
3528         if(!timed)
3529             expires = "";
3530         else if(ban->expires)
3531             expires = intervalString(e_buffer, ban->expires - now);
3532         else
3533             expires = msg_never;
3534
3535         if(ban->triggered)
3536             triggered = intervalString(t_buffer, now - ban->triggered);
3537         else
3538             triggered = msg_never;
3539
3540         tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3541         tbl.contents[ii][0] = ban->mask;
3542         tbl.contents[ii][1] = ban->owner;
3543         tbl.contents[ii][2] = strdup(triggered);
3544         if(timed)
3545         {
3546             tbl.contents[ii][3] = strdup(expires);
3547             tbl.contents[ii][4] = ban->reason;
3548         }
3549         else
3550             tbl.contents[ii][3] = ban->reason;
3551     }
3552     table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3553     reply("MSG_MATCH_COUNT", matches);
3554     for(ii = 1; ii < tbl.length; ++ii)
3555     {
3556         free((char*)tbl.contents[ii][2]);
3557         if(timed)
3558             free((char*)tbl.contents[ii][3]);
3559         free(tbl.contents[ii]);
3560     }
3561     free(tbl.contents[0]);
3562     free(tbl.contents);
3563     return 1;
3564 }
3565
3566 static int
3567 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3568 {
3569     struct chanData *cData = channel->channel_info;
3570     if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3571         return 0;
3572     if(cData->topic_mask)
3573         return !match_ircglob(new_topic, cData->topic_mask);
3574     else if(cData->topic)
3575         return irccasecmp(new_topic, cData->topic);
3576     else
3577         return 0;
3578 }
3579
3580 static CHANSERV_FUNC(cmd_topic)
3581 {
3582     struct chanData *cData;
3583     char *topic;
3584
3585     cData = channel->channel_info;
3586     if(argc < 2)
3587     {
3588         if(cData->topic)
3589         {
3590             SetChannelTopic(channel, chanserv, cData->topic, 1);
3591             reply("CSMSG_TOPIC_SET", cData->topic);
3592             return 1;
3593         }
3594
3595         reply("CSMSG_NO_TOPIC", channel->name);
3596         return 0;
3597     }
3598
3599     topic = unsplit_string(argv + 1, argc - 1, NULL);
3600     /* If they say "!topic *", use an empty topic. */
3601     if((topic[0] == '*') && (topic[1] == 0))
3602         topic[0] = 0;
3603     if(bad_topic(channel, user, topic))
3604     {
3605         char *topic_mask = cData->topic_mask;
3606         if(topic_mask)
3607         {
3608             char new_topic[TOPICLEN+1], tchar;
3609             int pos=0, starpos=-1, dpos=0, len;
3610
3611             while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3612             {
3613                 switch(tchar)
3614                 {
3615                 case '*':
3616                     if(starpos != -1)
3617                         goto bad_mask;
3618                     len = strlen(topic);
3619                     if((dpos + len) > TOPICLEN)
3620                         len = TOPICLEN + 1 - dpos;
3621                     memcpy(new_topic+dpos, topic, len);
3622                     dpos += len;
3623                     starpos = pos;
3624                     break;
3625                 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3626                 default: new_topic[dpos++] = tchar; break;
3627                 }
3628             }
3629             if((dpos > TOPICLEN) || tchar)
3630             {
3631             bad_mask:
3632                 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3633                 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3634                 return 0;
3635             }
3636             new_topic[dpos] = 0;
3637             SetChannelTopic(channel, chanserv, new_topic, 1);
3638         } else {
3639             reply("CSMSG_TOPIC_LOCKED", channel->name);
3640             return 0;
3641         }
3642     }
3643     else
3644         SetChannelTopic(channel, chanserv, topic, 1);
3645
3646     if(cData->flags & CHANNEL_TOPIC_SNARF)
3647     {
3648         /* Grab the topic and save it as the default topic. */
3649         free(cData->topic);
3650         cData->topic = strdup(channel->topic);
3651     }
3652
3653     return 1;
3654 }
3655
3656 static CHANSERV_FUNC(cmd_mode)
3657 {
3658     struct mod_chanmode *change;
3659     
3660     if(argc < 2)
3661     {
3662         change = &channel->channel_info->modes;
3663         if(change->modes_set || change->modes_clear) {
3664             modcmd_chanmode_announce(change);
3665             reply("CSMSG_DEFAULTED_MODES", channel->name);
3666         } else
3667             reply("CSMSG_NO_MODES", channel->name);
3668         return 1;
3669     }
3670
3671     change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE);
3672     if(!change)
3673     {
3674         reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3675         return 0;
3676     }
3677
3678     if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3679        && mode_lock_violated(&channel->channel_info->modes, change))
3680     {
3681         char modes[MAXLEN];
3682         mod_chanmode_format(&channel->channel_info->modes, modes);
3683         reply("CSMSG_MODE_LOCKED", modes, channel->name);
3684         return 0;
3685     }
3686
3687     modcmd_chanmode_announce(change);
3688     mod_chanmode_free(change);
3689     reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3690     return 1;
3691 }
3692
3693 static CHANSERV_FUNC(cmd_invite)
3694 {
3695     struct userData *uData;
3696     struct userNode *invite;
3697
3698     uData = GetChannelUser(channel->channel_info, user->handle_info);
3699
3700     if(argc > 1)
3701     {
3702         if(!(invite = GetUserH(argv[1])))
3703         {
3704             reply("MSG_NICK_UNKNOWN", argv[1]);
3705             return 0;
3706         }
3707     }
3708     else
3709         invite = user;
3710
3711     if(GetUserMode(channel, invite))
3712     {
3713         reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3714         return 0;
3715     }
3716
3717     if(user != invite)
3718     {
3719         char *reason = (argc > 2) ? unsplit_string(argv + 2, argc - 2, NULL) : "";
3720         send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name, (argc > 2) ? ": " : ".", reason);
3721     }
3722     irc_invite(chanserv, invite, channel);
3723     if(argc > 1)
3724         reply("CSMSG_INVITED_USER", argv[1], channel->name);
3725
3726     return 1;
3727 }
3728
3729 static CHANSERV_FUNC(cmd_inviteme)
3730 {
3731     struct userData *uData;
3732
3733     if(GetUserMode(channel, user))
3734     {
3735         reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3736         return 0;
3737     }
3738     if(channel->channel_info
3739        && !(channel->channel_info->flags & CHANNEL_PEON_INVITE)
3740        && (uData = GetChannelUser(channel->channel_info, user->handle_info))
3741        && (uData->access < channel->channel_info->lvlOpts[lvlGiveOps]))
3742     {
3743         reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3744         return 0;
3745     }
3746     irc_invite(cmd->parent->bot, user, channel);
3747     return 1;
3748 }
3749
3750 static void
3751 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3752 {
3753     unsigned int combo;
3754     char buf1[INTERVALLEN], buf2[INTERVALLEN];
3755
3756     /* We display things based on two dimensions:
3757      * - Issue time: present or absent
3758      * - Expiration: revoked, expired, expires in future, or indefinite expiration
3759      * (in order of precedence, so something both expired and revoked
3760      * only counts as revoked)
3761      */
3762     combo = (suspended->issued ? 4 : 0)
3763         + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3764     switch(combo) {
3765     case 0: /* no issue time, indefinite expiration */
3766         reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3767         break;
3768     case 1: /* no issue time, expires in future */
3769         intervalString(buf1, suspended->expires-now);
3770         reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3771         break;
3772     case 2: /* no issue time, expired */
3773         intervalString(buf1, now-suspended->expires);
3774         reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3775         break;
3776     case 3: /* no issue time, revoked */
3777         intervalString(buf1, now-suspended->revoked);
3778         reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3779         break;
3780     case 4: /* issue time set, indefinite expiration */
3781         intervalString(buf1, now-suspended->issued);
3782         reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3783         break;
3784     case 5: /* issue time set, expires in future */
3785         intervalString(buf1, now-suspended->issued);
3786         intervalString(buf2, suspended->expires-now);
3787         reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3788         break;
3789     case 6: /* issue time set, expired */
3790         intervalString(buf1, now-suspended->issued);
3791         intervalString(buf2, now-suspended->expires);
3792         reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3793         break;
3794     case 7: /* issue time set, revoked */
3795         intervalString(buf1, now-suspended->issued);
3796         intervalString(buf2, now-suspended->revoked);
3797         reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3798         break;
3799     default:
3800         log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3801         return;
3802     }
3803 }
3804
3805 static CHANSERV_FUNC(cmd_info)
3806 {
3807     char modes[MAXLEN], buffer[INTERVALLEN];
3808     struct userData *uData, *owner;
3809     struct chanData *cData;
3810     struct do_not_register *dnr;
3811     struct note *note;
3812     dict_iterator_t it;
3813     int privileged;
3814
3815     cData = channel->channel_info;
3816     reply("CSMSG_CHANNEL_INFO", channel->name);
3817
3818     uData = GetChannelUser(cData, user->handle_info);
3819     if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3820     {
3821         mod_chanmode_format(&cData->modes, modes);
3822         reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3823         reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3824     }
3825
3826     for(it = dict_first(cData->notes); it; it = iter_next(it))
3827     {
3828         int padding;
3829
3830         note = iter_data(it);
3831         if(!note_type_visible_to_user(cData, note->type, user))
3832             continue;
3833
3834         padding = PADLEN - 1 - strlen(iter_key(it));
3835         reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3836     }
3837
3838     reply("CSMSG_CHANNEL_MAX", cData->max);
3839     for(owner = cData->users; owner; owner = owner->next)
3840         if(owner->access == UL_OWNER)
3841             reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3842     reply("CSMSG_CHANNEL_USERS", cData->userCount);
3843     reply("CSMSG_CHANNEL_BANS", cData->banCount);
3844     reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited));
3845     reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered));
3846
3847     privileged = IsStaff(user);
3848     if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3849         reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3850
3851     if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3852         chanserv_show_dnrs(user, cmd, channel->name, NULL);
3853
3854     if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3855     {
3856         struct suspended *suspended;
3857         reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3858         for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3859             show_suspension_info(cmd, user, suspended);
3860     }
3861     else if(IsSuspended(cData))
3862     {
3863         reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3864         show_suspension_info(cmd, user, cData->suspended);
3865     }
3866     return 1;
3867 }
3868
3869 static CHANSERV_FUNC(cmd_netinfo)
3870 {
3871     extern time_t boot_time;
3872     extern unsigned long burst_length;
3873     char interval[INTERVALLEN];
3874
3875     reply("CSMSG_NETWORK_INFO");
3876     reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3877     reply("CSMSG_NETWORK_USERS", dict_size(clients));
3878     reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3879     reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3880     reply("CSMSG_NETWORK_BANS", banCount);
3881     reply("CSMSG_CHANNEL_USERS", userCount);
3882     reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time));
3883     reply("CSMSG_BURST_LENGTH",intervalString(interval, burst_length));
3884     return 1;
3885 }
3886
3887 static void
3888 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3889 {
3890     struct helpfile_table table;
3891     unsigned int nn;
3892     struct userNode *user;
3893     char *nick;
3894
3895     table.length = 0;
3896     table.width = 1;
3897     table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3898     table.contents = alloca(list->used*sizeof(*table.contents));
3899     for(nn=0; nn<list->used; nn++)
3900     {
3901         user = list->list[nn];
3902         if(user->modes & skip_flags)
3903             continue;
3904         if(IsBot(user))
3905             continue;
3906         table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3907         if(IsAway(user))
3908         {
3909             nick = alloca(strlen(user->nick)+3);
3910             sprintf(nick, "(%s)", user->nick);
3911         }
3912         else
3913             nick = user->nick;
3914         table.contents[table.length][0] = nick;
3915         table.length++;
3916     }
3917     table_send(chanserv, to->nick, 0, NULL, table);
3918 }
3919
3920 static CHANSERV_FUNC(cmd_ircops)
3921 {
3922     reply("CSMSG_STAFF_OPERS");
3923     send_staff_list(user, &curr_opers, FLAGS_SERVICE);
3924     return 1;
3925 }
3926
3927 static CHANSERV_FUNC(cmd_helpers)
3928 {
3929     reply("CSMSG_STAFF_HELPERS");
3930     send_staff_list(user, &curr_helpers, FLAGS_OPER);
3931     return 1;
3932 }
3933
3934 static CHANSERV_FUNC(cmd_staff)
3935 {
3936     reply("CSMSG_NETWORK_STAFF");
3937     cmd_ircops(CSFUNC_ARGS);
3938     cmd_helpers(CSFUNC_ARGS);
3939     return 1;
3940 }
3941
3942 static CHANSERV_FUNC(cmd_peek)
3943 {
3944     struct modeNode *mn;
3945     char modes[MODELEN];
3946     unsigned int n;
3947     struct helpfile_table table;
3948
3949     irc_make_chanmode(channel, modes);
3950
3951     reply("CSMSG_PEEK_INFO", channel->name);
3952     reply("CSMSG_PEEK_TOPIC", channel->topic);
3953     reply("CSMSG_PEEK_MODES", modes);
3954     reply("CSMSG_PEEK_USERS", channel->members.used);
3955
3956     table.length = 0;
3957     table.width = 1;
3958     table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3959     table.contents = alloca(channel->members.used*sizeof(*table.contents));
3960     for(n = 0; n < channel->members.used; n++)
3961     {
3962         mn = channel->members.list[n];
3963         if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
3964             continue;
3965         table.contents[table.length] = alloca(sizeof(**table.contents));
3966         table.contents[table.length][0] = mn->user->nick;
3967         table.length++;
3968     }
3969     if(table.length)
3970     {
3971         reply("CSMSG_PEEK_OPS");
3972         table_send(chanserv, user->nick, 0, NULL, table);
3973     }
3974     else
3975         reply("CSMSG_PEEK_NO_OPS");
3976     return 1;
3977 }
3978
3979 static MODCMD_FUNC(cmd_wipeinfo)
3980 {
3981     struct handle_info *victim;
3982     struct userData *ud, *actor;
3983
3984     REQUIRE_PARAMS(2);
3985     actor = GetChannelUser(channel->channel_info, user->handle_info);
3986     if(!(victim = modcmd_get_handle_info(user, argv[1])))
3987         return 0;
3988     if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
3989     {
3990         reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
3991         return 0;
3992     }
3993     if((ud->access >= actor->access) && (ud != actor))
3994     {
3995         reply("MSG_USER_OUTRANKED", victim->handle);
3996         return 0;
3997     }
3998     if(ud->info)
3999         free(ud->info);
4000     ud->info = NULL;
4001     reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4002     return 1;
4003 }
4004
4005 static CHANSERV_FUNC(cmd_resync)
4006 {
4007     struct mod_chanmode *changes;
4008     struct chanData *cData = channel->channel_info;
4009     unsigned int ii, used;
4010
4011     changes = mod_chanmode_alloc(channel->members.used * 2);
4012     for(ii = used = 0; ii < channel->members.used; ++ii)
4013     {
4014         struct modeNode *mn = channel->members.list[ii];
4015         struct userData *uData;
4016         if(IsService(mn->user))
4017         {
4018             /* must not change modes for this user */
4019         }
4020         else if(!(uData = GetChannelAccess(cData, mn->user->handle_info)))
4021         {
4022             if(mn->modes)
4023             {
4024                 changes->args[used].mode = MODE_REMOVE | mn->modes;
4025                 changes->args[used++].member = mn;
4026             }
4027         }
4028         else if(uData->access < cData->lvlOpts[lvlGiveOps])
4029         {
4030             if(mn->modes & MODE_CHANOP)
4031             {
4032                 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4033                 changes->args[used++].member = mn;
4034             }
4035             if(!(mn->modes & MODE_VOICE))
4036             {
4037                 changes->args[used].mode = MODE_VOICE;
4038                 changes->args[used++].member = mn;
4039             }
4040         }
4041         else
4042         {
4043             if(!(mn->modes & MODE_CHANOP))
4044             {
4045                 changes->args[used].mode = MODE_CHANOP;
4046                 changes->args[used++].member = mn;
4047             }
4048         }
4049     }
4050     changes->argc = used;
4051     modcmd_chanmode_announce(changes);
4052     mod_chanmode_free(changes);
4053     reply("CSMSG_RESYNCED_USERS", channel->name);
4054     return 1;
4055 }
4056
4057 static CHANSERV_FUNC(cmd_seen)
4058 {
4059     struct userData *uData;
4060     struct handle_info *handle;
4061     char seen[INTERVALLEN];
4062
4063     REQUIRE_PARAMS(2);
4064
4065     if(!irccasecmp(argv[1], chanserv->nick))
4066     {
4067         reply("CSMSG_IS_CHANSERV");
4068         return 1;
4069     }
4070
4071     if(!(handle = get_handle_info(argv[1])))
4072     {
4073         reply("MSG_HANDLE_UNKNOWN", argv[1]);
4074         return 0;
4075     }
4076
4077     if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4078     {
4079         reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4080         return 0;
4081     }
4082
4083     if(uData->present)
4084         reply("CSMSG_USER_PRESENT", handle->handle);
4085     else if(uData->seen)
4086         reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen));
4087     else
4088         reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4089
4090     if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4091         reply("CSMSG_USER_VACATION", handle->handle);
4092
4093     return 1;
4094 }
4095
4096 static MODCMD_FUNC(cmd_names)
4097 {
4098     struct userNode *targ;
4099     struct userData *targData;
4100     unsigned int ii, pos;
4101     char buf[400];
4102
4103     for(ii=pos=0; ii<channel->members.used; ++ii)
4104     {
4105         targ = channel->members.list[ii]->user;
4106         targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4107         if(!targData)
4108             continue;
4109         if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 6 > sizeof(buf))
4110         {
4111             buf[pos] = 0;
4112             reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4113             pos = 0;
4114         }
4115         buf[pos++] = ' ';
4116         if(IsUserSuspended(targData))
4117             buf[pos++] = 's';
4118         pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4119     }
4120     buf[pos] = 0;
4121     reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4122     reply("CSMSG_END_NAMES", channel->name);
4123     return 1;
4124 }
4125
4126 static int
4127 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4128 {
4129     switch(ntype->visible_type)
4130     {
4131     case NOTE_VIS_ALL: return 1;
4132     case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4133     case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4134     }
4135 }
4136
4137 static int
4138 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4139 {
4140     struct userData *uData;
4141
4142     switch(ntype->set_access_type)
4143     {
4144     case NOTE_SET_CHANNEL_ACCESS:
4145         if(!user->handle_info)
4146             return 0;
4147         if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4148             return 0;
4149         return uData->access >= ntype->set_access.min_ulevel;
4150     case NOTE_SET_CHANNEL_SETTER:
4151         return check_user_level(channel, user, lvlSetters, 1, 0);
4152     case NOTE_SET_PRIVILEGED: default:
4153         return IsHelping(user);
4154     }
4155 }
4156
4157 static CHANSERV_FUNC(cmd_note)
4158 {
4159     struct chanData *cData;
4160     struct note *note;
4161     struct note_type *ntype;
4162
4163     cData = channel->channel_info;
4164     if(!cData)
4165     {
4166         reply("CSMSG_NOT_REGISTERED", channel->name);
4167         return 0;
4168     }
4169
4170     /* If no arguments, show all visible notes for the channel. */
4171     if(argc < 2)
4172     {
4173         dict_iterator_t it;
4174         unsigned int count;
4175
4176         for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4177         {
4178             note = iter_data(it);
4179             if(!note_type_visible_to_user(cData, note->type, user))
4180                 continue;
4181             if(!count++)
4182                 reply("CSMSG_NOTELIST_HEADER", channel->name);
4183             reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4184         }
4185         if(count)
4186             reply("CSMSG_NOTELIST_END", channel->name);
4187         else
4188             reply("CSMSG_NOTELIST_EMPTY", channel->name);
4189     }
4190     /* If one argument, show the named note. */
4191     else if(argc == 2)
4192     {
4193         if((note = dict_find(cData->notes, argv[1], NULL))
4194            && note_type_visible_to_user(cData, note->type, user))
4195         {
4196             reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4197         }
4198         else if((ntype = dict_find(note_types, argv[1], NULL))
4199                 && note_type_visible_to_user(NULL, ntype, user))
4200         {
4201             reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4202             return 0;
4203         }
4204         else
4205         {
4206             reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4207             return 0;
4208         }
4209     }
4210     /* Assume they're trying to set a note. */
4211     else
4212     {
4213         char *note_text;
4214         ntype = dict_find(note_types, argv[1], NULL);
4215         if(!ntype)
4216         {
4217             reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4218             return 0;
4219         }
4220         else if(note_type_settable_by_user(channel, ntype, user))
4221         {
4222             note_text = unsplit_string(argv+2, argc-2, NULL);
4223             if((note = dict_find(cData->notes, argv[1], NULL)))
4224                 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4225             chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4226             reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4227
4228             if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4229             {
4230                 /* The note is viewable to staff only, so return 0
4231                    to keep the invocation from getting logged (or
4232                    regular users can see it in !events). */
4233                 return 0;
4234             }
4235         }
4236         else
4237         {
4238             reply("CSMSG_NO_ACCESS");
4239             return 0;
4240         }
4241     }
4242     return 1;
4243 }
4244
4245 static CHANSERV_FUNC(cmd_delnote)
4246 {
4247     struct note *note;
4248
4249     REQUIRE_PARAMS(2);
4250     if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4251        || !note_type_settable_by_user(channel, note->type, user))
4252     {
4253         reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4254         return 0;
4255     }
4256     dict_remove(channel->channel_info->notes, note->type->name);
4257     reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4258     return 1;
4259 }
4260
4261 static CHANSERV_FUNC(cmd_events)
4262 {
4263     struct logSearch discrim;
4264     struct logReport report;
4265     unsigned int matches, limit;
4266
4267     limit = (argc > 1) ? atoi(argv[1]) : 10;
4268     if(limit < 1 || limit > 200) limit = 10;
4269
4270     memset(&discrim, 0, sizeof(discrim));
4271     discrim.masks.bot = chanserv;
4272     discrim.masks.channel_name = channel->name;
4273     if(argc > 2) discrim.masks.command = argv[2];
4274     discrim.limit = limit;
4275     discrim.max_time = INT_MAX;
4276     discrim.severities = 1 << LOG_COMMAND;
4277     report.reporter = chanserv;
4278     report.user = user;
4279     reply("CSMSG_EVENT_SEARCH_RESULTS");
4280     matches = log_entry_search(&discrim, log_report_entry, &report);
4281     if(matches)
4282         reply("MSG_MATCH_COUNT", matches);
4283     else
4284         reply("MSG_NO_MATCHES");
4285     return 1;
4286 }
4287
4288 static CHANSERV_FUNC(cmd_say)
4289 {
4290     char *msg;
4291     if(channel)
4292     {
4293         REQUIRE_PARAMS(2);
4294         msg = unsplit_string(argv + 1, argc - 1, NULL);
4295         send_channel_message(channel, cmd->parent->bot, "%s", msg);
4296     }
4297     else if(GetUserH(argv[1]))
4298     {
4299         REQUIRE_PARAMS(3);
4300         msg = unsplit_string(argv + 2, argc - 2, NULL);
4301         send_target_message(1, argv[1], cmd->parent->bot, "%s", msg);
4302     }
4303     else
4304     {
4305         reply("You must specify the name of a channel or user.");
4306         return 0;
4307     }
4308     return 1;
4309 }
4310
4311 static CHANSERV_FUNC(cmd_emote)
4312 {
4313     char *msg;
4314     assert(argc >= 2);
4315     if(channel)
4316     {
4317         /* CTCP is so annoying. */
4318         msg = unsplit_string(argv + 1, argc - 1, NULL);
4319         send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4320     }
4321     else if(GetUserH(argv[1]))
4322     {
4323         msg = unsplit_string(argv + 2, argc - 2, NULL);
4324         send_target_message(1, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4325     }
4326     else
4327     {
4328         reply("You must specify the name of a channel or user.");
4329         return 0;
4330     }
4331     return 1;
4332 }
4333
4334 struct channelList *
4335 chanserv_support_channels(void)
4336 {
4337     return &chanserv_conf.support_channels;
4338 }
4339
4340 static CHANSERV_FUNC(cmd_expire)
4341 {
4342     int channel_count = registered_channels;
4343     expire_channels(NULL);
4344     reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4345     return 1;
4346 }
4347
4348 static void
4349 chanserv_expire_suspension(void *data)
4350 {
4351     struct suspended *suspended = data;
4352     struct chanNode *channel;
4353     struct mod_chanmode change;
4354
4355     if(!suspended->expires || (now < suspended->expires))
4356         suspended->revoked = now;
4357     channel = suspended->cData->channel;
4358     suspended->cData->channel = channel;
4359     suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4360     change.modes_set = change.modes_clear = 0;
4361     change.argc = 1;
4362     change.args[0].mode = MODE_CHANOP;
4363     change.args[0].member = AddChannelUser(chanserv, channel);
4364     mod_chanmode_announce(chanserv, channel, &change);
4365 }
4366
4367 static CHANSERV_FUNC(cmd_csuspend)
4368 {
4369     struct suspended *suspended;
4370     char reason[MAXLEN];
4371     time_t expiry, duration;
4372     struct userData *uData;
4373
4374     REQUIRE_PARAMS(3);
4375
4376     if(IsProtected(channel->channel_info))
4377     {
4378         reply("CSMSG_SUSPEND_NODELETE", channel->name);
4379         return 0;
4380     }
4381
4382     if(argv[1][0] == '!')
4383         argv[1]++;
4384     else if(IsSuspended(channel->channel_info))
4385     {
4386         reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4387         show_suspension_info(cmd, user, channel->channel_info->suspended);
4388         return 0;
4389     }
4390
4391     if(!strcmp(argv[1], "0"))
4392         expiry = 0;
4393     else if((duration = ParseInterval(argv[1])))
4394         expiry = now + duration;
4395     else
4396     {
4397         reply("MSG_INVALID_DURATION", argv[1]);
4398         return 0;
4399     }
4400
4401     unsplit_string(argv + 2, argc - 2, reason);
4402
4403     suspended = calloc(1, sizeof(*suspended));
4404     suspended->revoked = 0;
4405     suspended->issued = now;
4406     suspended->suspender = strdup(user->handle_info->handle);
4407     suspended->expires = expiry;
4408     suspended->reason = strdup(reason);
4409     suspended->cData = channel->channel_info;
4410     suspended->previous = suspended->cData->suspended;
4411     suspended->cData->suspended = suspended;
4412
4413     if(suspended->expires)
4414         timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4415
4416     if(IsSuspended(channel->channel_info))
4417     {
4418         suspended->previous->revoked = now;
4419         if(suspended->previous->expires)
4420             timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4421         sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4422         global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4423     }
4424     else
4425     {
4426         /* Mark all users in channel as absent. */
4427         for(uData = channel->channel_info->users; uData; uData = uData->next)
4428         {
4429             if(uData->present)
4430             {
4431                 uData->seen = now;
4432                 uData->present = 0;
4433             }
4434         }
4435
4436         /* Mark the channel as suspended, then part. */
4437         channel->channel_info->flags |= CHANNEL_SUSPENDED;
4438         DelChannelUser(chanserv, channel, suspended->reason, 0);
4439         reply("CSMSG_SUSPENDED", channel->name);
4440         sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4441         global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4442     }
4443     return 1;
4444 }
4445
4446 static CHANSERV_FUNC(cmd_cunsuspend)
4447 {
4448     struct suspended *suspended;
4449     char message[MAXLEN];
4450
4451     if(!IsSuspended(channel->channel_info))
4452     {
4453         reply("CSMSG_NOT_SUSPENDED", channel->name);
4454         return 0;
4455     }
4456
4457     suspended = channel->channel_info->suspended;
4458
4459     /* Expire the suspension and join ChanServ to the channel. */
4460     timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4461     chanserv_expire_suspension(suspended);
4462     reply("CSMSG_UNSUSPENDED", channel->name);
4463     sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4464     global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4465     return 1;
4466 }
4467
4468 typedef struct chanservSearch
4469 {
4470     char *name;
4471     char *registrar;
4472
4473     time_t unvisited;
4474     time_t registered;
4475
4476     unsigned long flags;
4477     unsigned int limit;
4478 } *search_t;
4479
4480 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4481
4482 static search_t
4483 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4484 {
4485     search_t search;
4486     unsigned int i;
4487
4488     search = malloc(sizeof(struct chanservSearch));
4489     memset(search, 0, sizeof(*search));
4490     search->limit = 25;
4491
4492     for(i = 0; i < argc; i++)
4493     {
4494         /* Assume all criteria require arguments. */
4495         if(i == (argc - 1))
4496         {
4497             send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4498             goto fail;
4499         }
4500
4501         if(!irccasecmp(argv[i], "name"))
4502             search->name = argv[++i];
4503         else if(!irccasecmp(argv[i], "registrar"))
4504             search->registrar = argv[++i];
4505         else if(!irccasecmp(argv[i], "unvisited"))
4506             search->unvisited = ParseInterval(argv[++i]);
4507         else if(!irccasecmp(argv[i], "registered"))
4508             search->registered = ParseInterval(argv[++i]);
4509         else if(!irccasecmp(argv[i], "flags"))
4510         {
4511             i++;
4512             if(!irccasecmp(argv[i], "nodelete"))
4513                 search->flags |= CHANNEL_NODELETE;
4514             else if(!irccasecmp(argv[i], "suspended"))
4515                 search->flags |= CHANNEL_SUSPENDED;
4516             else
4517             {
4518                 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4519                 goto fail;
4520             }
4521         }
4522         else if(!irccasecmp(argv[i], "limit"))
4523             search->limit = strtoul(argv[++i], NULL, 10);
4524         else
4525         {
4526             send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4527             goto fail;
4528         }
4529     }
4530
4531     if(search->name && !strcmp(search->name, "*"))
4532         search->name = 0;
4533     if(search->registrar && !strcmp(search->registrar, "*"))
4534         search->registrar = 0;
4535
4536     return search;
4537   fail:
4538     free(search);
4539     return NULL;
4540 }
4541
4542 static int
4543 chanserv_channel_match(struct chanData *channel, search_t search)
4544 {
4545     const char *name = channel->channel->name;
4546     if((search->name && !match_ircglob(name, search->name)) ||
4547        (search->registrar && !channel->registrar) ||
4548        (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4549        (search->unvisited && (now - channel->visited) < search->unvisited) ||
4550        (search->registered && (now - channel->registered) > search->registered) ||
4551        (search->flags && ((search->flags & channel->flags) != search->flags)))
4552         return 0;
4553
4554     return 1;
4555 }
4556
4557 static unsigned int
4558 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4559 {
4560     struct chanData *channel;
4561     unsigned int matches = 0;
4562
4563     for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4564     {
4565         if(!chanserv_channel_match(channel, search))
4566             continue;
4567         matches++;
4568         smf(channel, data);
4569     }
4570
4571     return matches;
4572 }
4573
4574 static void
4575 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4576 {
4577 }
4578
4579 static void
4580 search_print(struct chanData *channel, void *data)
4581 {
4582     send_message_type(4, data, chanserv, "%s", channel->channel->name);
4583 }
4584
4585 static CHANSERV_FUNC(cmd_search)
4586 {
4587     search_t search;
4588     unsigned int matches;
4589     channel_search_func action;
4590
4591     REQUIRE_PARAMS(3);
4592
4593     if(!irccasecmp(argv[1], "count"))
4594         action = search_count;
4595     else if(!irccasecmp(argv[1], "print"))
4596         action = search_print;
4597     else
4598     {
4599         reply("CSMSG_ACTION_INVALID", argv[1]);
4600         return 0;
4601     }
4602
4603     search = chanserv_search_create(user, argc - 2, argv + 2);
4604     if(!search)
4605         return 0;
4606
4607     if(action == search_count)
4608         search->limit = INT_MAX;
4609
4610     if(action == search_print)
4611         reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4612
4613     matches = chanserv_channel_search(search, action, user);
4614
4615     if(matches)
4616         reply("MSG_MATCH_COUNT", matches);
4617     else
4618         reply("MSG_NO_MATCHES");
4619
4620     free(search);
4621     return 1;
4622 }
4623
4624 static CHANSERV_FUNC(cmd_unvisited)
4625 {
4626     struct chanData *cData;
4627     time_t interval = chanserv_conf.channel_expire_delay;
4628     char buffer[INTERVALLEN];
4629     unsigned int limit = 25, matches = 0;
4630
4631     if(argc > 1)
4632     {
4633         interval = ParseInterval(argv[1]);
4634         if(argc > 2)
4635             limit = atoi(argv[2]);
4636     }
4637
4638     intervalString(buffer, interval);
4639     reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4640
4641     for(cData = channelList; cData && matches < limit; cData = cData->next)
4642     {
4643         if((now - cData->visited) < interval)
4644             continue;
4645
4646         intervalString(buffer, now - cData->visited);
4647         reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4648         matches++;
4649     }
4650
4651     return 1;
4652 }
4653
4654 static MODCMD_FUNC(chan_opt_defaulttopic)
4655 {
4656     if(argc > 1)
4657     {
4658         char *topic;
4659
4660         if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4661         {
4662             reply("CSMSG_TOPIC_LOCKED", channel->name);
4663             return 0;
4664         }
4665
4666         topic = unsplit_string(argv+1, argc-1, NULL);
4667
4668         free(channel->channel_info->topic);
4669         if(topic[0] == '*' && topic[1] == 0)
4670         {
4671             topic = channel->channel_info->topic = NULL;
4672         }
4673         else
4674         {
4675             topic = channel->channel_info->topic = strdup(topic);
4676             if(channel->channel_info->topic_mask
4677                && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4678                 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4679         }
4680         SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4681     }
4682
4683     if(channel->channel_info->topic)
4684         reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4685     else
4686         reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4687     return 1;
4688 }
4689
4690 static MODCMD_FUNC(chan_opt_topicmask)
4691 {
4692     if(argc > 1)
4693     {
4694         struct chanData *cData = channel->channel_info;
4695         char *mask;
4696
4697         if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4698         {
4699             reply("CSMSG_TOPIC_LOCKED", channel->name);
4700             return 0;
4701         }
4702
4703         mask = unsplit_string(argv+1, argc-1, NULL);
4704
4705         if(cData->topic_mask)
4706             free(cData->topic_mask);
4707         if(mask[0] == '*' && mask[1] == 0)
4708         {
4709             cData->topic_mask = 0;
4710         }
4711         else
4712         {
4713             cData->topic_mask = strdup(mask);
4714             if(!cData->topic)
4715                 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4716             else if(!match_ircglob(cData->topic, cData->topic_mask))
4717                 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4718         }
4719     }
4720
4721     if(channel->channel_info->topic_mask)
4722         reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4723     else
4724         reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4725     return 1;
4726 }
4727
4728 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4729 {
4730     if(argc > 1)
4731     {
4732         char *greeting = unsplit_string(argv+1, argc-1, NULL);
4733         char *previous;
4734
4735         previous = *data;
4736         if(greeting[0] == '*' && greeting[1] == 0)
4737             *data = NULL;
4738         else
4739         {
4740             unsigned int length = strlen(greeting);
4741             if(length > chanserv_conf.greeting_length)
4742             {
4743                 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4744                 return 0;
4745             }
4746             *data = strdup(greeting);
4747         }
4748         if(previous)
4749             free(previous);
4750     }
4751
4752     if(*data)
4753         reply(name, *data);
4754     else
4755         reply(name, user_find_message(user, "MSG_NONE"));
4756     return 1;
4757 }
4758
4759 static MODCMD_FUNC(chan_opt_greeting)
4760 {
4761     return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4762 }
4763
4764 static MODCMD_FUNC(chan_opt_usergreeting)
4765 {
4766     return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4767 }
4768
4769 static MODCMD_FUNC(chan_opt_modes)
4770 {
4771     struct mod_chanmode *new_modes;
4772     char modes[MODELEN];
4773
4774     if(argc > 1)
4775     {
4776         if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4777         {
4778             reply("CSMSG_NO_ACCESS");
4779             return 0;
4780         }
4781         if(argv[1][0] == '*' && argv[1][1] == 0)
4782         {
4783             memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4784         }
4785         else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE)))
4786         {
4787             reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4788             return 0;
4789         }
4790         else if(new_modes->argc > 1)
4791         {
4792             reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4793             mod_chanmode_free(new_modes);
4794             return 0;
4795         }
4796         else
4797         {
4798             channel->channel_info->modes = *new_modes;
4799             modcmd_chanmode_announce(new_modes);
4800             mod_chanmode_free(new_modes);
4801         }
4802     }
4803
4804     mod_chanmode_format(&channel->channel_info->modes, modes);
4805     if(modes[0])
4806         reply("CSMSG_SET_MODES", modes);
4807     else
4808         reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4809     return 1;
4810 }
4811
4812 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4813 static int
4814 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4815 {
4816     struct chanData *cData = channel->channel_info;
4817     int value;
4818
4819     if(argc > 1)
4820     {
4821         /* Set flag according to value. */
4822         if(enabled_string(argv[1]))
4823         {
4824             cData->flags |= mask;
4825             value = 1;
4826         }
4827         else if(disabled_string(argv[1]))
4828         {
4829             cData->flags &= ~mask;
4830             value = 0;
4831         }
4832         else
4833         {
4834             reply("MSG_INVALID_BINARY", argv[1]);
4835             return 0;
4836         }
4837     }
4838     else
4839     {
4840         /* Find current option value. */
4841         value = (cData->flags & mask) ? 1 : 0;
4842     }
4843
4844     if(value)
4845         reply(name, user_find_message(user, "MSG_ON"));
4846     else
4847         reply(name, user_find_message(user, "MSG_OFF"));
4848     return 1;
4849 }
4850
4851 static MODCMD_FUNC(chan_opt_nodelete)
4852 {
4853     if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4854     {
4855         reply("MSG_SETTING_PRIVILEGED", argv[0]);
4856         return 0;
4857     }
4858
4859     CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4860 }
4861
4862 static MODCMD_FUNC(chan_opt_userinfo)
4863 {
4864     CHANNEL_BINARY_OPTION("CSMSG_SET_USERINFO", CHANNEL_INFO_LINES);
4865 }
4866
4867 static MODCMD_FUNC(chan_opt_voice)
4868 {
4869     CHANNEL_BINARY_OPTION("CSMSG_SET_VOICE", CHANNEL_VOICE_ALL);
4870 }
4871
4872 static MODCMD_FUNC(chan_opt_dynlimit)
4873 {
4874     CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4875 }
4876
4877 static MODCMD_FUNC(chan_opt_topicsnarf)
4878 {
4879     if((argc > 0) && !check_user_level(channel, user, lvlEnfTopic, 1, 0))
4880     {
4881         reply("CSMSG_TOPIC_LOCKED", channel->name);
4882         return 0;
4883     }
4884     CHANNEL_BINARY_OPTION("CSMSG_SET_TOPICSNARF", CHANNEL_TOPIC_SNARF);
4885 }
4886
4887 static MODCMD_FUNC(chan_opt_peoninvite)
4888 {
4889     CHANNEL_BINARY_OPTION("CSMSG_SET_PEONINVITE", CHANNEL_PEON_INVITE);
4890 }
4891
4892 static MODCMD_FUNC(chan_opt_defaults)
4893 {
4894     struct userData *uData;
4895     struct chanData *cData;
4896     const char *confirm;
4897     enum levelOption lvlOpt;
4898     enum charOption chOpt;
4899
4900     cData = channel->channel_info;
4901     uData = GetChannelUser(cData, user->handle_info);
4902     if(!uData || (uData->access < UL_OWNER))
4903     {
4904         reply("CSMSG_OWNER_DEFAULTS", channel->name);
4905         return 0;
4906     }
4907     confirm = make_confirmation_string(uData);
4908     if((argc < 2) || strcmp(argv[1], confirm))
4909     {
4910         reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
4911         return 0;
4912     }
4913     cData->flags = CHANNEL_DEFAULT_FLAGS;
4914     cData->modes = chanserv_conf.default_modes;
4915     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
4916         cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
4917     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
4918         cData->chOpts[chOpt] = charOptions[chOpt].default_value;
4919     reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
4920     return 1;
4921 }
4922
4923 static int
4924 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4925 {
4926     struct chanData *cData = channel->channel_info;
4927     struct userData *uData;
4928     unsigned short value;
4929
4930     if(argc > 1)
4931     {
4932         if(!check_user_level(channel, user, option, 1, 1))
4933         {
4934             reply("CSMSG_CANNOT_SET");
4935             return 0;
4936         }
4937         value = user_level_from_name(argv[1], UL_OWNER+1);
4938         if(!value && !isdigit(argv[1][0]))
4939         {
4940             reply("CSMSG_INVALID_ACCESS", index);
4941             return 0;
4942         }
4943         uData = GetChannelUser(cData, user->handle_info);
4944         if(!uData || (uData->access < value))
4945         {
4946             reply("CSMSG_BAD_SETLEVEL");
4947             return 0;
4948         }
4949         cData->lvlOpts[option] = value;
4950     }
4951     reply(levelOptions[option].format_name, cData->lvlOpts[option]);
4952     return argc > 1;
4953 }
4954
4955 static MODCMD_FUNC(chan_opt_enfops)
4956 {
4957     return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
4958 }
4959
4960 static MODCMD_FUNC(chan_opt_giveops)
4961 {
4962     return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
4963 }
4964
4965 static MODCMD_FUNC(chan_opt_enfmodes)
4966 {
4967     return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
4968 }
4969
4970 static MODCMD_FUNC(chan_opt_enftopic)
4971 {
4972     return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
4973 }
4974
4975 static MODCMD_FUNC(chan_opt_pubcmd)
4976 {
4977     return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
4978 }
4979
4980 static MODCMD_FUNC(chan_opt_setters)
4981 {
4982     return channel_level_option(lvlSetters, CSFUNC_ARGS);
4983 }
4984
4985 static MODCMD_FUNC(chan_opt_ctcpusers)
4986 {
4987     return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
4988 }
4989
4990 static int
4991 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4992 {
4993     struct chanData *cData = channel->channel_info;
4994     int count = charOptions[option].count, index;
4995
4996     if(argc > 1)
4997     {
4998         index = atoi(argv[1]);
4999
5000         if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5001         {
5002             reply("CSMSG_INVALID_NUMERIC", index);
5003             /* Show possible values. */
5004             for(index = 0; index < count; index++)
5005                 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5006             return 0;
5007         }
5008
5009         cData->chOpts[option] = charOptions[option].values[index].value;
5010     }
5011     else
5012     {
5013         /* Find current option value. */
5014       find_value:
5015         for(index = 0;
5016             (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5017             index++);
5018         if(index == count)
5019         {
5020             /* Somehow, the option value is corrupt; reset it to the default. */
5021             cData->chOpts[option] = charOptions[option].default_value;
5022             goto find_value;
5023         }
5024     }
5025
5026     reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5027     return 1;
5028 }
5029
5030 static MODCMD_FUNC(chan_opt_protect)
5031 {
5032     return channel_multiple_option(chProtect, CSFUNC_ARGS);
5033 }
5034
5035 static MODCMD_FUNC(chan_opt_toys)
5036 {
5037     return channel_multiple_option(chToys, CSFUNC_ARGS);
5038 }
5039
5040 static MODCMD_FUNC(chan_opt_ctcpreaction)
5041 {
5042     return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5043 }
5044
5045 static MODCMD_FUNC(chan_opt_topicrefresh)
5046 {
5047     return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5048 }
5049
5050 static struct svccmd_list set_shows_list;
5051
5052 static void
5053 handle_svccmd_unbind(struct svccmd *target) {
5054     unsigned int ii;
5055     for(ii=0; ii<set_shows_list.used; ++ii)
5056         if(target == set_shows_list.list[ii])
5057             set_shows_list.used = 0;
5058 }
5059
5060 static CHANSERV_FUNC(cmd_set)
5061 {
5062     struct svccmd *subcmd;
5063     char buf[MAXLEN];
5064     unsigned int ii;
5065
5066     /* Check if we need to (re-)initialize set_shows_list. */
5067     if(!set_shows_list.used)
5068     {
5069         if(!set_shows_list.size)
5070         {
5071             set_shows_list.size = chanserv_conf.set_shows->used;
5072             set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5073         }
5074         for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5075         {
5076             const char *name = chanserv_conf.set_shows->list[ii];
5077             sprintf(buf, "%s %s", argv[0], name);
5078             subcmd = dict_find(cmd->parent->commands, buf, NULL);
5079             if(!subcmd)
5080             {
5081                 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5082                 continue;
5083             }
5084             svccmd_list_append(&set_shows_list, subcmd);
5085         }
5086     }
5087
5088     if(argc < 2)
5089     {
5090         reply("CSMSG_CHANNEL_OPTIONS");
5091         for(ii = 0; ii < set_shows_list.used; ii++)
5092         {
5093             subcmd = set_shows_list.list[ii];
5094             subcmd->command->func(user, channel, 1, argv+1, subcmd);
5095         }
5096         return 1;
5097     }
5098
5099     sprintf(buf, "%s %s", argv[0], argv[1]);
5100     subcmd = dict_find(cmd->parent->commands, buf, NULL);
5101     if(!subcmd)
5102     {
5103         reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5104         return 0;
5105     }
5106     if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5107     {
5108         reply("CSMSG_NO_ACCESS");
5109         return 0;
5110     }
5111
5112     return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5113 }
5114
5115 static int
5116 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5117 {
5118     struct userData *uData;
5119
5120     uData = GetChannelAccess(channel->channel_info, user->handle_info);
5121     if(!uData)
5122     {
5123         reply("CSMSG_NOT_USER", channel->name);
5124         return 0;
5125     }
5126
5127     if(argc < 2)
5128     {
5129         /* Just show current option value. */
5130     }
5131     else if(enabled_string(argv[1]))
5132     {
5133         uData->flags |= mask;
5134     }
5135     else if(disabled_string(argv[1]))
5136     {
5137         uData->flags &= ~mask;
5138     }
5139     else
5140     {
5141         reply("MSG_INVALID_BINARY", argv[1]);
5142         return 0;
5143     }
5144
5145     reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5146     return 1;
5147 }
5148
5149 static MODCMD_FUNC(user_opt_noautoop)
5150 {
5151     struct userData *uData;
5152
5153     uData = GetChannelAccess(channel->channel_info, user->handle_info);
5154     if(!uData)
5155     {
5156         reply("CSMSG_NOT_USER", channel->name);
5157         return 0;
5158     }
5159     if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5160         return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5161     else
5162         return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5163 }
5164
5165 static MODCMD_FUNC(user_opt_autoinvite)
5166 {
5167     return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5168 }
5169
5170 static MODCMD_FUNC(user_opt_info)
5171 {
5172     struct userData *uData;
5173     char *infoline;
5174
5175     uData = GetChannelAccess(channel->channel_info, user->handle_info);
5176
5177     if(!uData)
5178     {
5179         /* If they got past the command restrictions (which require access)
5180          * but fail this test, we have some fool with security override on.
5181          */
5182         reply("CSMSG_NOT_USER", channel->name);
5183         return 0;
5184     }
5185
5186     if(argc > 1)
5187     {
5188         infoline = unsplit_string(argv + 1, argc - 1, NULL);
5189         if(uData->info)
5190             free(uData->info);
5191         if(infoline[0] == '*' && infoline[1] == 0)
5192             uData->info = NULL;
5193         else
5194             uData->info = strdup(infoline);
5195     }
5196     if(uData->info)
5197         reply("CSMSG_USET_INFO", uData->info);
5198     else
5199         reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5200     return 1;
5201 }
5202
5203 struct svccmd_list uset_shows_list;
5204
5205 static CHANSERV_FUNC(cmd_uset)
5206 {
5207     struct svccmd *subcmd;
5208     char buf[MAXLEN];
5209     unsigned int ii;
5210
5211     /* Check if we need to (re-)initialize uset_shows_list. */
5212     if(!uset_shows_list.used)
5213     {
5214         char *options[] =
5215         {
5216             "NoAutoOp", "AutoInvite", "Info"
5217         };
5218
5219         if(!uset_shows_list.size)
5220         {
5221             uset_shows_list.size = ArrayLength(options);
5222             uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5223         }
5224         for(ii = 0; ii < ArrayLength(options); ii++)
5225         {
5226             const char *name = options[ii];
5227             sprintf(buf, "%s %s", argv[0], name);
5228             subcmd = dict_find(cmd->parent->commands, buf, NULL);
5229             if(!subcmd)
5230             {
5231                 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5232                 continue;
5233             }
5234             svccmd_list_append(&uset_shows_list, subcmd);
5235         }
5236     }
5237
5238     if(argc < 2)
5239     {
5240         /* Do this so options are presented in a consistent order. */
5241         reply("CSMSG_USER_OPTIONS");
5242         for(ii = 0; ii < uset_shows_list.used; ii++)
5243             uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5244         return 1;
5245     }
5246
5247     sprintf(buf, "%s %s", argv[0], argv[1]);
5248     subcmd = dict_find(cmd->parent->commands, buf, NULL);
5249     if(!subcmd)
5250     {
5251         reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5252         return 0;
5253     }
5254
5255     return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5256 }
5257
5258 static CHANSERV_FUNC(cmd_giveownership)
5259 {
5260     struct handle_info *new_owner_hi;
5261     struct userData *new_owner, *curr_user;
5262     struct chanData *cData = channel->channel_info;
5263     struct do_not_register *dnr;
5264     unsigned int force;
5265     unsigned short co_access;
5266     char reason[MAXLEN];
5267
5268     REQUIRE_PARAMS(2);
5269     curr_user = GetChannelAccess(cData, user->handle_info);
5270     force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5271     if(!curr_user || (curr_user->access != UL_OWNER))
5272     {
5273         struct userData *owner = NULL;
5274         for(curr_user = channel->channel_info->users;
5275             curr_user;
5276             curr_user = curr_user->next)
5277         {
5278             if(curr_user->access != UL_OWNER)
5279                 continue;
5280             if(owner)
5281             {
5282                 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5283                 return 0;
5284             }
5285             owner = curr_user;
5286         }
5287     }
5288     if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5289         return 0;
5290     if(new_owner_hi == user->handle_info)
5291     {
5292         reply("CSMSG_NO_TRANSFER_SELF");
5293         return 0;
5294     }
5295     new_owner = GetChannelAccess(cData, new_owner_hi);
5296     if(!new_owner)
5297     {
5298         reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5299         return 0;
5300     }
5301     if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5302     {
5303         reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5304         return 0;
5305     }
5306     if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5307         if(!IsHelping(user))
5308             reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5309         else
5310             chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5311         return 0;
5312     }
5313     if(new_owner->access >= UL_COOWNER)
5314         co_access = new_owner->access;
5315     else
5316         co_access = UL_COOWNER;
5317     new_owner->access = UL_OWNER;
5318     if(curr_user)
5319         curr_user->access = co_access;
5320     reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5321     sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5322     global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5323     return 1;
5324 }
5325
5326 static CHANSERV_FUNC(cmd_suspend)
5327 {
5328     struct handle_info *hi;
5329     struct userData *self, *target;
5330
5331     REQUIRE_PARAMS(2);
5332     if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5333     self = GetChannelUser(channel->channel_info, user->handle_info);
5334     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5335     {
5336         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5337         return 0;
5338     }
5339     if(target->access >= self->access)
5340     {
5341         reply("MSG_USER_OUTRANKED", hi->handle);
5342         return 0;
5343     }
5344     if(target->flags & USER_SUSPENDED)
5345     {
5346         reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5347         return 0;
5348     }
5349     if(target->present)
5350     {
5351         target->present = 0;
5352         target->seen = now;
5353     }
5354     target->flags |= USER_SUSPENDED;
5355     reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5356     return 1;
5357 }
5358
5359 static CHANSERV_FUNC(cmd_unsuspend)
5360 {
5361     struct handle_info *hi;
5362     struct userData *self, *target;
5363
5364     REQUIRE_PARAMS(2);
5365     if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5366     self = GetChannelUser(channel->channel_info, user->handle_info);
5367     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5368     {
5369         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5370         return 0;
5371     }
5372     if(target->access >= self->access)
5373     {
5374         reply("MSG_USER_OUTRANKED", hi->handle);
5375         return 0;
5376     }
5377     if(!(target->flags & USER_SUSPENDED))
5378     {
5379         reply("CSMSG_NOT_SUSPENDED", hi->handle);
5380         return 0;
5381     }
5382     target->flags &= ~USER_SUSPENDED;
5383     reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5384     return 1;
5385 }
5386
5387 static MODCMD_FUNC(cmd_deleteme)
5388 {
5389     struct handle_info *hi;
5390     struct userData *target;
5391     const char *confirm_string;
5392     unsigned short access;
5393     char *channel_name;
5394
5395     hi = user->handle_info;
5396     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5397     {
5398         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5399         return 0;
5400     }
5401     if(target->access == UL_OWNER)
5402     {
5403         reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5404         return 0;
5405     }
5406     confirm_string = make_confirmation_string(target);
5407     if((argc < 2) || strcmp(argv[1], confirm_string))
5408     {
5409         reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5410         return 0;
5411     }
5412     access = target->access;
5413     channel_name = strdup(channel->name);
5414     del_channel_user(target, 1);
5415     reply("CSMSG_DELETED_YOU", access, channel_name);
5416     free(channel_name);
5417     return 1;
5418 }
5419
5420 static void
5421 chanserv_refresh_topics(UNUSED_ARG(void *data))
5422 {
5423     unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5424     struct chanData *cData;
5425     char opt;
5426
5427     for(cData = channelList; cData; cData = cData->next)
5428     {
5429         if(IsSuspended(cData))
5430             continue;
5431         opt = cData->chOpts[chTopicRefresh];
5432         if(opt == 'n')
5433             continue;
5434         if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5435             continue;
5436         if(cData->topic)
5437             SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5438         cData->last_refresh = refresh_num;
5439     }
5440     timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5441 }
5442
5443 static CHANSERV_FUNC(cmd_unf)
5444 {
5445     if(channel)
5446     {
5447         char response[MAXLEN];
5448         const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5449         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
5450         irc_privmsg(cmd->parent->bot, channel->name, response);
5451     }
5452     else
5453         reply("CSMSG_UNF_RESPONSE");
5454     return 1;
5455 }
5456
5457 static CHANSERV_FUNC(cmd_ping)
5458 {
5459     if(channel)
5460     {
5461         char response[MAXLEN];
5462         const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5463         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
5464         irc_privmsg(cmd->parent->bot, channel->name, response);
5465     }
5466     else
5467         reply("CSMSG_PING_RESPONSE");
5468     return 1;
5469 }
5470
5471 static CHANSERV_FUNC(cmd_wut)
5472 {
5473     if(channel)
5474     {
5475         char response[MAXLEN];
5476         const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5477         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
5478         irc_privmsg(cmd->parent->bot, channel->name, response);
5479     }
5480     else
5481         reply("CSMSG_WUT_RESPONSE");
5482     return 1;
5483 }
5484
5485 static CHANSERV_FUNC(cmd_8ball)
5486 {
5487     unsigned int i, j, accum;
5488     const char *resp;
5489
5490     REQUIRE_PARAMS(2);
5491     accum = 0;
5492     for(i=1; i<argc; i++)
5493         for(j=0; argv[i][j]; j++)
5494             accum = (accum << 5) - accum + toupper(argv[i][j]);
5495     resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5496     if(channel)
5497     {
5498         char response[MAXLEN];
5499         sprintf(response, "\ 2%s\ 2: %s", user->nick, resp);
5500         irc_privmsg(cmd->parent->bot, channel->name, response);
5501     }
5502     else
5503         send_message_type(4, user, cmd->parent->bot, "%s", resp);
5504     return 1;
5505 }
5506
5507 static CHANSERV_FUNC(cmd_d)
5508 {
5509     unsigned long sides, count, modifier, ii, total;
5510     char response[MAXLEN], *sep;
5511     const char *fmt;
5512
5513     REQUIRE_PARAMS(2);
5514     if((count = strtoul(argv[1], &sep, 10)) <= 1)
5515         goto no_dice;
5516     if(sep[0] == 0)
5517     {
5518         sides = count;
5519         count = 1;
5520         modifier = 0;
5521     }
5522     else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5523             && (sides = strtoul(sep+1, &sep, 10)) > 1)
5524     {
5525         if(sep[0] == 0)
5526             modifier = 0;
5527         else if((sep[0] == '-') && isdigit(sep[1]))
5528             modifier = strtoul(sep, NULL, 10);
5529         else if((sep[0] == '+') && isdigit(sep[1]))
5530             modifier = strtoul(sep+1, NULL, 10);
5531         else
5532             goto no_dice;
5533     }
5534     else
5535     {
5536       no_dice:
5537         reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5538         return 0;
5539     }
5540     if(count > 10)
5541     {
5542         reply("CSMSG_BAD_DICE_COUNT", count, 10);
5543         return 0;
5544     }
5545     for(total = ii = 0; ii < count; ++ii)
5546         total += (rand() % sides) + 1;
5547     total += modifier;
5548
5549     if((count > 1) || modifier)
5550     {
5551         fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5552         sprintf(response, fmt, total, count, sides, modifier);
5553     }
5554     else
5555     {
5556         fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5557         sprintf(response, fmt, total, sides);
5558     }
5559     if(channel)
5560         send_target_message(5, channel->name, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5561     else
5562         send_message_type(4, user, cmd->parent->bot, "%s", response);
5563     return 1;
5564 }
5565
5566 static CHANSERV_FUNC(cmd_huggle)
5567 {
5568     char response[MAXLEN];
5569     const char *fmt;
5570     /* CTCP must be via PRIVMSG, never notice */
5571     if(channel)
5572     {
5573         fmt = user_find_message(user, "CSMSG_HUGGLES_HIM");
5574         sprintf(response, fmt, user->nick);
5575         irc_privmsg(cmd->parent->bot, channel->name, response);
5576     }
5577     else
5578     {
5579         fmt = user_find_message(user, "CSMSG_HUGGLES_YOU");
5580         irc_privmsg(cmd->parent->bot, user->nick, fmt);
5581     }
5582     return 1;
5583 }
5584
5585 static void
5586 chanserv_adjust_limit(void *data)
5587 {
5588     struct mod_chanmode change;
5589     struct chanData *cData = data;
5590     struct chanNode *channel = cData->channel;
5591     unsigned int limit;
5592
5593     if(IsSuspended(cData))
5594         return;
5595
5596     cData->limitAdjusted = now;
5597     limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5598     if(cData->modes.modes_set & MODE_LIMIT)
5599     {
5600         if(limit > cData->modes.new_limit)
5601             limit = cData->modes.new_limit;
5602         else if(limit == cData->modes.new_limit)
5603             return;
5604     }
5605
5606     change.modes_set = MODE_LIMIT;
5607     change.modes_clear = 0;
5608     change.new_limit = limit;
5609     change.argc = 0;
5610     mod_chanmode_announce(chanserv, channel, &change);
5611 }
5612
5613 static void
5614 handle_new_channel(struct chanNode *channel)
5615 {
5616     struct chanData *cData;
5617
5618     if(!(cData = channel->channel_info))
5619         return;
5620
5621     if(cData->modes.modes_set || cData->modes.modes_clear)
5622         mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5623
5624     if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5625         SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5626 }
5627
5628 /* Welcome to my worst nightmare. Warning: Read (or modify)
5629    the code below at your own risk. */
5630 static int
5631 handle_join(struct modeNode *mNode)
5632 {
5633     struct mod_chanmode change;
5634     struct userNode *user = mNode->user;
5635     struct chanNode *channel = mNode->channel;
5636     struct chanData *cData;
5637     struct userData *uData = NULL;
5638     struct banData *bData;
5639     struct handle_info *handle;
5640     unsigned int modes = 0, info = 0;
5641     char *greeting;
5642
5643     if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5644         return 0;
5645
5646     cData = channel->channel_info;
5647     if(channel->members.used > cData->max)
5648         cData->max = channel->members.used;
5649
5650     /* Check for bans.  If they're joining through a ban, one of two
5651      * cases applies:
5652      *   1: Join during a netburst, by riding the break.  Kick them
5653      *      unless they have ops or voice in the channel.
5654      *   2: They're allowed to join through the ban (an invite in
5655      *   ircu2.10, or a +e on Hybrid, or something).
5656      * If they're not joining through a ban, and the banlist is not
5657      * full, see if they're on the banlist for the channel.  If so,
5658      * kickban them.
5659      */
5660     if(user->uplink->burst && !mNode->modes)
5661     {
5662         unsigned int ii;
5663         for(ii = 0; ii < channel->banlist.used; ii++)
5664         {
5665             if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5666             {
5667                 /* Riding a netburst.  Naughty. */
5668                 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5669                 return 1;
5670             }
5671         }
5672     }
5673
5674     change.modes_set = change.modes_clear = 0;
5675     change.argc = 1;
5676     if(channel->banlist.used < MAXBANS)
5677     {
5678         /* Not joining through a ban. */
5679         for(bData = cData->bans;
5680                 bData && !user_matches_glob(user, bData->mask, 1);
5681                 bData = bData->next);
5682
5683         if(bData)
5684         {
5685             char kick_reason[MAXLEN];
5686             sprintf(kick_reason, "%s (%s)", bData->reason, bData->owner);
5687
5688             bData->triggered = now;
5689             if(bData != cData->bans)
5690             {
5691                 /* Shuffle the ban to the head of the list. */
5692                 if(bData->next) bData->next->prev = bData->prev;
5693                 if(bData->prev) bData->prev->next = bData->next;
5694
5695                 bData->prev = NULL;
5696                 bData->next = cData->bans;
5697
5698                 if(cData->bans)
5699                     cData->bans->prev = bData;
5700                 cData->bans = bData;
5701             }
5702
5703             change.args[0].mode = MODE_BAN;
5704             change.args[0].hostmask = bData->mask;
5705             mod_chanmode_announce(chanserv, channel, &change);
5706             KickChannelUser(user, channel, chanserv, kick_reason);
5707             return 1;
5708         }
5709     }
5710
5711     /* ChanServ will not modify the limits in join-flooded channels.
5712        It will also skip DynLimit processing when the user (or srvx)
5713        is bursting in, because there are likely more incoming. */
5714     if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5715        && !user->uplink->burst
5716        && !channel->join_flooded
5717        && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5718     {
5719         /* The user count has begun "bumping" into the channel limit,
5720            so set a timer to raise the limit a bit. Any previous
5721            timers are removed so three incoming users within the delay
5722            results in one limit change, not three. */
5723
5724         timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5725         timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5726     }
5727
5728     if(cData->lvlOpts[lvlGiveOps] == 0)
5729         modes |= MODE_CHANOP;
5730     else if((cData->flags & CHANNEL_VOICE_ALL) && !channel->join_flooded)
5731         modes |= MODE_VOICE;
5732
5733     greeting = cData->greeting;
5734     if(user->handle_info)
5735     {
5736         handle = user->handle_info;
5737
5738         if(IsHelper(user) && !IsHelping(user))
5739         {
5740             unsigned int ii;
5741             for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5742             {
5743                 if(channel == chanserv_conf.support_channels.list[ii])
5744                 {
5745                     HANDLE_SET_FLAG(user->handle_info, HELPING);
5746                     break;
5747                 }
5748             }
5749         }
5750
5751         uData = GetTrueChannelAccess(cData, handle);
5752         if(uData && !IsUserSuspended(uData))
5753         {
5754             /* Ops and above were handled by the above case. */
5755             if(IsUserAutoOp(uData))
5756             {
5757                 if(uData->access < cData->lvlOpts[lvlGiveOps])
5758                     modes |= MODE_VOICE;
5759                 else
5760                     modes |= MODE_CHANOP;
5761             }
5762             if(uData->access >= UL_PRESENT)
5763                 cData->visited = now;
5764
5765             uData->seen = now;
5766             uData->present = 1;
5767
5768             if(cData->user_greeting)
5769                 greeting = cData->user_greeting;
5770             if(uData->info
5771                && (cData->flags & CHANNEL_INFO_LINES)
5772                && ((now - uData->seen) >= chanserv_conf.info_delay)
5773                && !uData->present)
5774                 info = 1;
5775         }
5776     }
5777     if(!user->uplink->burst)
5778     {
5779         if(modes)
5780         {
5781             change.args[0].mode = modes;
5782             change.args[0].member = mNode;
5783             mod_chanmode_announce(chanserv, channel, &change);
5784         }
5785         if(greeting && !user->uplink->burst)
5786             send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5787         if(uData && info)
5788             send_target_message(4, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5789     }
5790     return 0;
5791 }
5792
5793 static void
5794 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5795 {
5796     struct mod_chanmode change;
5797     struct userData *channel;
5798     unsigned int ii, jj;
5799
5800     if(!user->handle_info)
5801         return;
5802
5803     change.modes_set = change.modes_clear = 0;
5804     change.argc = 1;
5805     for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5806     {
5807         struct chanNode *cn;
5808         struct modeNode *mn;
5809         if(IsSuspended(channel->channel) || !(cn = channel->channel->channel))
5810             continue;
5811
5812         mn = GetUserMode(cn, user);
5813         if(!mn)
5814         {
5815             if(!IsUserSuspended(channel)
5816                && IsUserAutoInvite(channel)
5817                && (cn->modes & (MODE_KEY | MODE_INVITEONLY))
5818                && !self->burst)
5819                 irc_invite(chanserv, user, cn);
5820             continue;
5821         }
5822
5823         if(channel->access >= UL_PRESENT)
5824             channel->channel->visited = now;
5825
5826         if(IsUserAutoOp(channel))
5827         {
5828             if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5829                 change.args[0].mode = MODE_CHANOP;
5830             else
5831                 change.args[0].mode = MODE_VOICE;
5832             change.args[0].member = mn;
5833             mod_chanmode_announce(chanserv, cn, &change);
5834         }
5835
5836         channel->seen = now;
5837         channel->present = 1;
5838     }
5839
5840     for(ii = 0; ii < user->channels.used; ++ii)
5841     {
5842         struct chanNode *channel = user->channels.list[ii]->channel;
5843         struct banData *ban;
5844
5845         if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
5846            || !channel->channel_info)
5847             continue;
5848         for(jj = 0; jj < channel->banlist.used; ++jj)
5849             if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
5850                 break;
5851         if(jj < channel->banlist.used)
5852             continue;
5853         for(ban = channel->channel_info->bans; ban; ban = ban->next)
5854         {
5855             char kick_reason[MAXLEN];
5856             if(!user_matches_glob(user, ban->mask, 1))
5857                 continue;
5858             change.args[0].mode = MODE_BAN;
5859             change.args[0].hostmask = ban->mask;
5860             mod_chanmode_announce(chanserv, channel, &change);
5861             sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
5862             KickChannelUser(user, channel, chanserv, kick_reason);
5863             ban->triggered = now;
5864             break;
5865         }
5866     }
5867
5868     if(IsSupportHelper(user))
5869     {
5870         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5871         {
5872             if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
5873             {
5874                 HANDLE_SET_FLAG(user->handle_info, HELPING);
5875                 break;
5876             }
5877         }
5878     }
5879 }
5880
5881 static void
5882 handle_part(struct userNode *user, struct chanNode *channel, UNUSED_ARG(const char *reason))
5883 {
5884     struct chanData *cData;
5885     struct userData *uData;
5886     struct handle_info *handle;
5887
5888     cData = channel->channel_info;
5889     if(!cData || IsSuspended(cData) || IsLocal(user)) return;
5890
5891     if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !channel->join_flooded)
5892     {
5893         /* Allow for a bit of padding so that the limit doesn't
5894            track the user count exactly, which could get annoying. */
5895         if((channel->limit - channel->members.used) > chanserv_conf.adjust_threshold + 5)
5896         {
5897             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5898             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5899         }
5900     }
5901
5902     if((handle = user->handle_info) && (uData = GetTrueChannelAccess(cData, handle)))
5903     {
5904         uData->seen = now;
5905         scan_handle_presence(channel, handle, user);
5906     }
5907
5908     if(IsHelping(user) && IsSupportHelper(user))
5909     {
5910         unsigned int ii, jj;
5911         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5912         {
5913             for(jj = 0; jj < user->channels.used; ++jj)
5914                 if(user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
5915                     break;
5916             if(jj < user->channels.used)
5917                 break;
5918         }
5919         if(ii == chanserv_conf.support_channels.used)
5920             HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
5921     }
5922 }
5923
5924 static void
5925 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
5926 {
5927     char *reason = "CSMSG_USER_PROTECTED";
5928
5929     if(!channel->channel_info || !kicker || IsService(kicker)
5930        || (kicker == victim) || IsSuspended(channel->channel_info)
5931        || (kicker->handle_info && kicker->handle_info == victim->handle_info))
5932         return;
5933
5934     if(protect_user(victim, kicker, channel->channel_info))
5935         KickChannelUser(kicker, channel, chanserv, reason);
5936 }
5937
5938 static int
5939 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
5940 {
5941     struct chanData *cData;
5942
5943     if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user)) return 0;
5944
5945     cData = channel->channel_info;
5946     if(bad_topic(channel, user, channel->topic))
5947     {
5948         send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
5949         if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
5950             SetChannelTopic(channel, chanserv, old_topic, 1);
5951         else if(cData->topic)
5952             SetChannelTopic(channel, chanserv, cData->topic, 1);
5953         return 1;
5954     }
5955     /* With topicsnarf, grab the topic and save it as the default topic. */
5956     if(cData->flags & CHANNEL_TOPIC_SNARF)
5957     {
5958         free(cData->topic);
5959         cData->topic = strdup(channel->topic);
5960     }
5961     return 0;
5962 }
5963
5964 static void
5965 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
5966 {
5967     struct mod_chanmode *bounce = NULL;
5968     unsigned int bnc, ii;
5969     char deopped = 0;
5970
5971     if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
5972         return;
5973
5974     if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
5975        && mode_lock_violated(&channel->channel_info->modes, change))
5976     {
5977         char correct[MAXLEN];
5978         bounce = mod_chanmode_alloc(change->argc + 1);
5979         *bounce = channel->channel_info->modes;
5980         mod_chanmode_format(&channel->channel_info->modes, correct);
5981         send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
5982     }
5983     for(ii = bnc = 0; ii < change->argc; ++ii)
5984     {
5985         if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
5986         {
5987             const struct userNode *victim = change->args[ii].member->user;
5988             if(!protect_user(victim, user, channel->channel_info))
5989                 continue;
5990             if(!bounce)
5991                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
5992             if(!deopped)
5993             {
5994                 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
5995                 bounce->args[bnc].member = GetUserMode(channel, user);
5996                 if(bounce->args[bnc].member)
5997                     bnc++;
5998             }
5999             bounce->args[bnc].mode = MODE_CHANOP;
6000             bounce->args[bnc].member = change->args[ii].member;
6001             bnc++;
6002             send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6003         }
6004         else if(change->args[ii].mode & MODE_CHANOP)
6005         {
6006             const struct userNode *victim = change->args[ii].member->user;
6007             if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6008                 continue;
6009             if(!bounce)
6010                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6011             bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6012             bounce->args[bnc].member = change->args[ii].member;
6013             bnc++;
6014         }
6015         else if(change->args[ii].mode & MODE_BAN)
6016         {
6017             const char *ban = change->args[ii].hostmask;
6018             if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6019                 continue;
6020             if(!bounce)
6021                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6022             bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6023             bounce->args[bnc].hostmask = ban;
6024             bnc++;
6025             send_message(user, chanserv, "CSMSG_MASK_PROTECTED", remove);
6026         }
6027     }
6028     if(bounce)
6029     {
6030         if((bounce->argc = bnc))
6031             mod_chanmode_announce(chanserv, channel, bounce);
6032         mod_chanmode_free(bounce);
6033     }
6034 }
6035
6036 static void
6037 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6038 {
6039     struct chanNode *channel;
6040     struct banData *bData;
6041     struct mod_chanmode change;
6042     unsigned int ii, jj;
6043     char kick_reason[MAXLEN];
6044
6045     change.modes_set = change.modes_clear = 0;
6046     change.argc = 1;
6047     change.args[0].mode = MODE_BAN;
6048     for(ii = 0; ii < user->channels.used; ++ii)
6049     {
6050         channel = user->channels.list[ii]->channel;
6051         /* Need not check for bans if they're opped or voiced. */
6052         if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6053             continue;
6054         /* Need not check for bans unless channel registration is active. */
6055         if(!channel->channel_info || IsSuspended(channel->channel_info))
6056             continue;
6057         /* Look for a matching ban already on the channel. */
6058         for(jj = 0; jj < channel->banlist.used; ++jj)
6059             if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6060                 break;
6061         /* Need not act if we found one. */
6062         if(jj < channel->banlist.used)
6063             continue;
6064         /* Look for a matching ban in this channel. */
6065         for(bData = channel->channel_info->bans; bData; bData = bData->next)
6066         {
6067             if(!user_matches_glob(user, bData->mask, 1))
6068                 continue;
6069             change.args[0].hostmask = bData->mask;
6070             mod_chanmode_announce(chanserv, channel, &change);
6071             sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6072             KickChannelUser(user, channel, chanserv, kick_reason);
6073             bData->triggered = now;
6074             break; /* we don't need to check any more bans in the channel */
6075         }
6076     }
6077 }
6078
6079 static void handle_rename(struct handle_info *handle, const char *old_handle)
6080 {
6081     struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6082
6083     if(dnr)
6084     {
6085         dict_remove2(handle_dnrs, old_handle, 1);
6086         safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6087         dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6088     }
6089 }
6090
6091 static void
6092 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6093 {
6094     struct userNode *h_user;
6095
6096     if(handle->channels)
6097     {
6098         for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6099             send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6100
6101         while(handle->channels)
6102             del_channel_user(handle->channels, 1);
6103     }
6104 }
6105
6106 static void
6107 handle_server_link(UNUSED_ARG(struct server *server))
6108 {
6109     struct chanData *cData;
6110
6111     for(cData = channelList; cData; cData = cData->next)
6112     {
6113         if(!IsSuspended(cData))
6114             cData->may_opchan = 1;
6115         if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6116            && !cData->channel->join_flooded
6117            && ((cData->channel->limit - cData->channel->members.used)
6118                < chanserv_conf.adjust_threshold))
6119         {
6120             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6121             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6122         }
6123     }
6124 }
6125
6126 static void
6127 chanserv_conf_read(void)
6128 {
6129     dict_t conf_node;
6130     const char *str;
6131     char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6132     struct mod_chanmode *change;
6133     struct string_list *strlist;
6134     struct chanNode *chan;
6135     unsigned int ii;
6136
6137     if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6138     {
6139         log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6140         return;
6141     }
6142     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6143         UnlockChannel(chanserv_conf.support_channels.list[ii]);
6144     chanserv_conf.support_channels.used = 0;
6145     if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6146     {
6147         for(ii = 0; ii < strlist->used; ++ii)
6148         {
6149             const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6150             if(!str2)
6151                 str2 = "+nt";
6152             chan = AddChannel(strlist->list[ii], now, str2, NULL);
6153             LockChannel(chan);
6154             channelList_append(&chanserv_conf.support_channels, chan);
6155         }
6156     }
6157     else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6158     {
6159         const char *str2;
6160         str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6161         if(!str2)
6162             str2 = "+nt";
6163         chan = AddChannel(str, now, str2, NULL);
6164         LockChannel(chan);
6165         channelList_append(&chanserv_conf.support_channels, chan);
6166     }
6167     str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6168     chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6169     str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6170     chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6171     str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6172     chanserv_conf.greeting_length = str ? atoi(str) : 120;
6173     str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6174     chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6175     str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6176     chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6177     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6178     chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6179     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6180     chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6181     str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6182     chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6183     str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6184     chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6185     str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6186     chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6187     str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6188     if(str) NickChange(chanserv, str, 0);
6189     str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6190     chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6191     str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6192     chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6193     str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6194     chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6195     str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6196     chanserv_conf.max_owned = str ? atoi(str) : 5;
6197     str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6198     chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6199     str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6200     chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6201     str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6202     chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6203     str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6204     if(!str)
6205         str = "+nt";
6206     safestrncpy(mode_line, str, sizeof(mode_line));
6207     ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6208     if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6209     {
6210         chanserv_conf.default_modes = *change;
6211         mod_chanmode_free(change);
6212     }
6213     free_string_list(chanserv_conf.set_shows);
6214     strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6215     if(strlist)
6216         strlist = string_list_copy(strlist);
6217     else
6218     {
6219         static const char *list[] = {
6220             /* multiple choice options */
6221             "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6222             "PubCmd", "GiveOps", "EnfOps", "EnfModes", "EnfTopic", "Protect",
6223             "Toys", "Setters", "TopicRefresh", "CtcpUsers", "CtcpReaction",
6224             /* binary options */
6225             "Voice", "UserInfo", "DynLimit", "TopicSnarf", "PeonInvite", "NoDelete",
6226             /* delimiter */
6227             NULL };
6228         unsigned int ii;
6229         strlist = alloc_string_list(ArrayLength(list)-1);
6230         for(ii=0; list[ii]; ii++)
6231             string_list_append(strlist, strdup(list[ii]));
6232     }
6233     chanserv_conf.set_shows = strlist;
6234     /* We don't look things up now, in case the list refers to options
6235      * defined by modules initialized after this point.  Just mark the
6236      * function list as invalid, so it will be initialized.
6237      */
6238     set_shows_list.used = 0;
6239     free_string_list(chanserv_conf.eightball);
6240     strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6241     if(strlist)
6242     {
6243         strlist = string_list_copy(strlist);
6244     }
6245     else
6246     {
6247         strlist = alloc_string_list(4);
6248         string_list_append(strlist, strdup("Yes."));
6249         string_list_append(strlist, strdup("No."));
6250         string_list_append(strlist, strdup("Maybe so."));
6251     }
6252     chanserv_conf.eightball = strlist;
6253     free_string_list(chanserv_conf.old_ban_names);
6254     strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6255     if (strlist)
6256         strlist = string_list_copy(strlist);
6257     else
6258         strlist = alloc_string_list(2);
6259     chanserv_conf.old_ban_names = strlist;
6260 }
6261
6262 static void
6263 chanserv_note_type_read(const char *key, struct record_data *rd)
6264 {
6265     dict_t obj;
6266     struct note_type *ntype;
6267     const char *str;
6268
6269     if(!(obj = GET_RECORD_OBJECT(rd)))
6270     {
6271         log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6272         return;
6273     }
6274     if(!(ntype = chanserv_create_note_type(key)))
6275     {
6276         log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6277         return;
6278     }
6279
6280     /* Figure out set access */
6281     if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6282     {
6283         ntype->set_access_type = NOTE_SET_PRIVILEGED;
6284         ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6285     }
6286     else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6287     {
6288         ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6289         ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6290     }
6291     else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6292     {
6293         ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6294     }
6295     else
6296     {
6297         log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6298         ntype->set_access_type = NOTE_SET_PRIVILEGED;
6299         ntype->set_access.min_opserv = 0;
6300     }
6301
6302     /* Figure out visibility */
6303     if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6304         ntype->visible_type = NOTE_VIS_PRIVILEGED;
6305     else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6306         ntype->visible_type = NOTE_VIS_PRIVILEGED;
6307     else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6308         ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6309     else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6310         ntype->visible_type = NOTE_VIS_ALL;
6311     else
6312         ntype->visible_type = NOTE_VIS_PRIVILEGED;
6313
6314     str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6315     ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6316 }
6317
6318 static void
6319 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6320 {
6321     struct handle_info *handle;
6322     struct userData *uData;
6323     char *seen, *inf, *flags;
6324     time_t last_seen;
6325     unsigned short access;
6326
6327     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6328     {
6329         log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6330         return;
6331     }
6332
6333     access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6334     if(access > UL_OWNER)
6335     {
6336         log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6337         return;
6338     }
6339
6340     inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6341     seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6342     last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6343     flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6344     handle = get_handle_info(key);
6345     if(!handle)
6346     {
6347         log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6348         return;
6349     }
6350
6351     uData = add_channel_user(chan, handle, access, last_seen, inf);
6352     uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6353 }
6354
6355 static void
6356 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6357 {
6358     struct banData *bData;
6359     char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6360     time_t set_time, triggered_time, expires_time;
6361
6362     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6363     {
6364         log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6365         return;
6366     }
6367
6368     set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6369     triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6370     s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6371     s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6372     owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6373     reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6374
6375     set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6376     triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6377     if(s_expires)
6378         expires_time = (time_t)strtoul(s_expires, NULL, 0);
6379     else if(s_duration)
6380         expires_time = set_time + atoi(s_duration);
6381     else
6382         expires_time = 0;
6383
6384     if(expires_time && (expires_time < now))
6385         return;
6386
6387     bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6388 }
6389
6390 static struct suspended *
6391 chanserv_read_suspended(dict_t obj)
6392 {
6393     struct suspended *suspended = calloc(1, sizeof(*suspended));
6394     char *str;
6395     dict_t previous;
6396
6397     str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6398     suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6399     str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6400     suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6401     str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6402     suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6403     suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6404     suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6405     previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6406     suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6407     return suspended;
6408 }
6409
6410 static int
6411 chanserv_channel_read(const char *key, struct record_data *hir)
6412 {
6413     struct suspended *suspended;
6414     struct mod_chanmode *modes;
6415     struct chanNode *cNode;
6416     struct chanData *cData;
6417     struct dict *channel, *obj;
6418     char *str, *argv[10];
6419     dict_iterator_t it;
6420     unsigned int argc;
6421
6422     channel = hir->d.object;
6423
6424     str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6425     if(!str)
6426         str = "<unknown>";
6427     cNode = AddChannel(key, now, NULL, NULL);
6428     if(!cNode)
6429     {
6430         log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6431         return 0;
6432     }
6433     cData = register_channel(cNode, str);
6434     if(!cData)
6435     {
6436         log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6437         return 0;
6438     }
6439
6440     if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6441     {
6442         enum levelOption lvlOpt;
6443         enum charOption chOpt;
6444         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6445         {
6446             if(!(str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING)))
6447                 continue;
6448             cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6449         }
6450         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6451         {
6452             if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6453                 continue;
6454             cData->chOpts[chOpt] = str[0];
6455         }
6456         if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6457             cData->flags = atoi(str);
6458     }
6459     else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6460     {
6461         enum levelOption lvlOpt;
6462         enum charOption chOpt;
6463         unsigned int count;
6464
6465         cData->flags = base64toint(str, 5);
6466         count = strlen(str += 5);
6467         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6468         {
6469             unsigned short lvl;
6470             switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6471             {
6472             case 'c': lvl = UL_COOWNER; break;
6473             case 'm': lvl = UL_MASTER; break;
6474             case 'n': lvl = UL_OWNER+1; break;
6475             case 'o': lvl = UL_OP; break;
6476             case 'p': lvl = UL_PEON; break;
6477             case 'w': lvl = UL_OWNER; break;
6478             default: lvl = 0; break;
6479             }
6480             cData->lvlOpts[lvlOpt] = lvl;
6481         }
6482         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6483             cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6484     }
6485    
6486     if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6487     {
6488         suspended = chanserv_read_suspended(obj);
6489         cData->suspended = suspended;
6490         suspended->cData = cData;
6491         /* We could use suspended->expires and suspended->revoked to
6492          * set the CHANNEL_SUSPENDED flag, but we don't. */
6493     }
6494     else if(cData->flags & CHANNEL_SUSPENDED)
6495     {
6496         suspended = calloc(1, sizeof(*suspended));
6497         suspended->issued = 0;
6498         suspended->revoked = 0;
6499         str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6500         suspended->expires = str ? atoi(str) : 0;
6501         suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6502         str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6503         suspended->reason = strdup(str ? str : "No reason");
6504         suspended->previous = NULL;
6505         cData->suspended = suspended;
6506         suspended->cData = cData;
6507     }
6508     else
6509         suspended = NULL;
6510
6511     if((cData->flags & CHANNEL_SUSPENDED)
6512        && suspended->expires
6513        && (suspended->expires <= now))
6514     {
6515         cData->flags &= ~CHANNEL_SUSPENDED;
6516     }
6517
6518     if((cData->flags & CHANNEL_SUSPENDED) && (suspended->expires > now))
6519     {
6520         timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6521     }
6522     else
6523     {
6524         struct mod_chanmode change;
6525         change.modes_set = change.modes_clear = 0;
6526         change.argc = 1;
6527         change.args[0].mode = MODE_CHANOP;
6528         change.args[0].member = AddChannelUser(chanserv, cNode);
6529         mod_chanmode_announce(chanserv, cNode, &change);
6530     }
6531
6532     str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6533     cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6534     str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6535     cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6536     str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6537     cData->max = str ? atoi(str) : 0;
6538     str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6539     cData->greeting = str ? strdup(str) : NULL;
6540     str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6541     cData->user_greeting = str ? strdup(str) : NULL;
6542     str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6543     cData->topic_mask = str ? strdup(str) : NULL;
6544     str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6545     cData->topic = str ? strdup(str) : NULL;
6546
6547     if((str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6548        && (argc = split_line(str, 0, ArrayLength(argv), argv))
6549        && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6550         cData->modes = *modes;
6551         if(cData->modes.argc > 1)
6552             cData->modes.argc = 1;
6553         if(!IsSuspended(cData))
6554             mod_chanmode_announce(chanserv, cNode, &cData->modes);
6555         mod_chanmode_free(modes);
6556     }
6557
6558     obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6559     for(it = dict_first(obj); it; it = iter_next(it))
6560         user_read_helper(iter_key(it), iter_data(it), cData);
6561
6562     if(!cData->users && !IsProtected(cData))
6563     {
6564         log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6565         unregister_channel(cData, "has empty user list.");
6566         return 0;
6567     }
6568
6569     obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6570     for(it = dict_first(obj); it; it = iter_next(it))
6571         ban_read_helper(iter_key(it), iter_data(it), cData);
6572
6573     obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6574     for(it = dict_first(obj); it; it = iter_next(it))
6575     {
6576         struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6577         struct record_data *rd = iter_data(it);
6578         const char *note, *setter;
6579
6580         if(rd->type != RECDB_OBJECT)
6581         {
6582             log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6583         }
6584         else if(!ntype)
6585         {
6586             log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6587         }
6588         else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6589         {
6590             log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6591         }
6592         else
6593         {
6594             setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6595             if(!setter) setter = "<unknown>";
6596             chanserv_add_channel_note(cData, ntype, setter, note);
6597         }
6598     }
6599
6600     return 0;
6601 }
6602
6603 static void
6604 chanserv_dnr_read(const char *key, struct record_data *hir)
6605 {
6606     const char *setter, *reason, *str;
6607     struct do_not_register *dnr;
6608
6609     setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6610     if(!setter)
6611     {
6612         log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6613         return;
6614     }
6615     reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6616     if(!reason)
6617     {
6618         log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6619         return;
6620     }
6621     dnr = chanserv_add_dnr(key, setter, reason);
6622     if(!dnr)
6623         return;
6624     str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6625     if(str)
6626         dnr->set = atoi(str);
6627     else
6628         dnr->set = 0;
6629 }
6630
6631 static int
6632 chanserv_saxdb_read(struct dict *database)
6633 {
6634     struct dict *section;
6635     dict_iterator_t it;
6636
6637     if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6638         for(it = dict_first(section); it; it = iter_next(it))
6639             chanserv_note_type_read(iter_key(it), iter_data(it));
6640
6641     if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6642         for(it = dict_first(section); it; it = iter_next(it))
6643             chanserv_channel_read(iter_key(it), iter_data(it));
6644
6645     if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6646         for(it = dict_first(section); it; it = iter_next(it))
6647             chanserv_dnr_read(iter_key(it), iter_data(it));
6648
6649     return 0;
6650 }
6651
6652 static int
6653 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6654 {
6655     int high_present = 0;
6656     saxdb_start_record(ctx, KEY_USERS, 1);
6657     for(; uData; uData = uData->next)
6658     {
6659         if((uData->access >= UL_PRESENT) && uData->present)
6660             high_present = 1;
6661         saxdb_start_record(ctx, uData->handle->handle, 0);
6662         saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6663         saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6664         if(uData->flags)
6665             saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6666         if(uData->info)
6667             saxdb_write_string(ctx, KEY_INFO, uData->info);
6668         saxdb_end_record(ctx);
6669     }
6670     saxdb_end_record(ctx);
6671     return high_present;
6672 }
6673
6674 static void
6675 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6676 {
6677     if(!bData)
6678         return;
6679     saxdb_start_record(ctx, KEY_BANS, 1);
6680     for(; bData; bData = bData->next)
6681     {
6682         saxdb_start_record(ctx, bData->mask, 0);
6683         saxdb_write_int(ctx, KEY_SET, bData->set);
6684         if(bData->triggered)
6685             saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6686         if(bData->expires)
6687             saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6688         if(bData->owner[0])
6689             saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6690         if(bData->reason)
6691             saxdb_write_string(ctx, KEY_REASON, bData->reason);
6692         saxdb_end_record(ctx);
6693     }
6694     saxdb_end_record(ctx);
6695 }
6696
6697 static void
6698 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6699 {
6700     saxdb_start_record(ctx, name, 0);
6701     saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6702     saxdb_write_string(ctx, KEY_REASON, susp->reason);
6703     if(susp->issued)
6704         saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6705     if(susp->expires)
6706         saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6707     if(susp->revoked)
6708         saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6709     if(susp->previous)
6710         chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6711     saxdb_end_record(ctx);
6712 }
6713
6714 static void
6715 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6716 {
6717     char buf[MAXLEN];
6718     int high_present;
6719     enum levelOption lvlOpt;
6720     enum charOption chOpt;
6721
6722     saxdb_start_record(ctx, channel->channel->name, 1);
6723
6724     saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6725     saxdb_write_int(ctx, KEY_MAX, channel->max);
6726     if(channel->topic)
6727         saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6728     if(channel->registrar)
6729         saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6730     if(channel->greeting)
6731         saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6732     if(channel->user_greeting)
6733         saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6734     if(channel->topic_mask)
6735         saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6736     if(channel->suspended)
6737         chanserv_write_suspended(ctx, "suspended", channel->suspended);
6738
6739     saxdb_start_record(ctx, KEY_OPTIONS, 0);
6740     saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6741     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6742         saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6743     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6744     {
6745         buf[0] = channel->chOpts[chOpt];
6746         buf[1] = '\0';
6747         saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6748     }
6749     saxdb_end_record(ctx);
6750
6751     if(channel->modes.modes_set || channel->modes.modes_clear)
6752     {
6753         mod_chanmode_format(&channel->modes, buf);
6754         saxdb_write_string(ctx, KEY_MODES, buf);
6755     }
6756
6757     high_present = chanserv_write_users(ctx, channel->users);
6758     chanserv_write_bans(ctx, channel->bans);
6759
6760     if(dict_size(channel->notes))
6761     {
6762         dict_iterator_t it;
6763
6764         saxdb_start_record(ctx, KEY_NOTES, 1);
6765         for(it = dict_first(channel->notes); it; it = iter_next(it))
6766         {
6767             struct note *note = iter_data(it);
6768             saxdb_start_record(ctx, iter_key(it), 0);
6769             saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6770             saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6771             saxdb_end_record(ctx);
6772         }
6773         saxdb_end_record(ctx);
6774     }
6775
6776     saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6777     saxdb_end_record(ctx);
6778 }
6779
6780 static void
6781 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6782 {
6783     const char *str;
6784
6785     saxdb_start_record(ctx, ntype->name, 0);
6786     switch(ntype->set_access_type)
6787     {
6788     case NOTE_SET_CHANNEL_ACCESS:
6789         saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6790         break;
6791     case NOTE_SET_CHANNEL_SETTER:
6792         saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6793         break;
6794     case NOTE_SET_PRIVILEGED: default:
6795         saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6796         break;
6797     }
6798     switch(ntype->visible_type)
6799     {
6800     case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6801     case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6802     case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6803     }
6804     saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6805     saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6806     saxdb_end_record(ctx);
6807 }
6808
6809 static void
6810 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
6811 {
6812     struct do_not_register *dnr;
6813     dict_iterator_t it;
6814
6815     for(it = dict_first(dnrs); it; it = iter_next(it))
6816     {
6817         dnr = iter_data(it);
6818         saxdb_start_record(ctx, dnr->chan_name, 0);
6819         if(dnr->set)
6820             saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
6821         saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
6822         saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
6823         saxdb_end_record(ctx);
6824     }
6825 }
6826
6827 static int
6828 chanserv_saxdb_write(struct saxdb_context *ctx)
6829 {
6830     dict_iterator_t it;
6831     struct chanData *channel;
6832
6833     /* Notes */
6834     saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
6835     for(it = dict_first(note_types); it; it = iter_next(it))
6836         chanserv_write_note_type(ctx, iter_data(it));
6837     saxdb_end_record(ctx);
6838
6839     /* DNRs */
6840     saxdb_start_record(ctx, KEY_DNR, 1);
6841     write_dnrs_helper(ctx, handle_dnrs);
6842     write_dnrs_helper(ctx, plain_dnrs);
6843     write_dnrs_helper(ctx, mask_dnrs);
6844     saxdb_end_record(ctx);
6845
6846     /* Channels */
6847     saxdb_start_record(ctx, KEY_CHANNELS, 1);
6848     for(channel = channelList; channel; channel = channel->next)
6849         chanserv_write_channel(ctx, channel);
6850     saxdb_end_record(ctx);
6851
6852     return 0;
6853 }
6854
6855 static void
6856 chanserv_db_cleanup(void) {
6857     unsigned int ii;
6858     unreg_part_func(handle_part);
6859     while(channelList)
6860         unregister_channel(channelList, "terminating.");
6861     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6862         UnlockChannel(chanserv_conf.support_channels.list[ii]);
6863     free(chanserv_conf.support_channels.list);
6864     dict_delete(handle_dnrs);
6865     dict_delete(plain_dnrs);
6866     dict_delete(mask_dnrs);
6867     dict_delete(note_types);
6868     free_string_list(chanserv_conf.eightball);
6869     free_string_list(chanserv_conf.old_ban_names);
6870     free_string_list(chanserv_conf.set_shows);
6871     free(set_shows_list.list);
6872     free(uset_shows_list.list);
6873     while(helperList)
6874     {
6875         struct userData *helper = helperList;
6876         helperList = helperList->next;
6877         free(helper);
6878     }
6879 }
6880
6881 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
6882 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
6883 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
6884
6885 void
6886 init_chanserv(const char *nick)
6887 {
6888     chanserv = AddService(nick, "Channel Services");
6889     CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
6890     conf_register_reload(chanserv_conf_read);
6891
6892     reg_server_link_func(handle_server_link);
6893
6894     reg_new_channel_func(handle_new_channel);
6895     reg_join_func(handle_join);
6896     reg_part_func(handle_part);
6897     reg_kick_func(handle_kick);
6898     reg_topic_func(handle_topic);
6899     reg_mode_change_func(handle_mode);
6900     reg_nick_change_func(handle_nick_change);
6901
6902     reg_auth_func(handle_auth);
6903     reg_handle_rename_func(handle_rename);
6904     reg_unreg_func(handle_unreg);
6905
6906     handle_dnrs = dict_new();
6907     dict_set_free_data(handle_dnrs, free);
6908     plain_dnrs = dict_new();
6909     dict_set_free_data(plain_dnrs, free);
6910     mask_dnrs = dict_new();
6911     dict_set_free_data(mask_dnrs, free);
6912
6913     reg_svccmd_unbind_func(handle_svccmd_unbind);
6914     chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
6915     DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
6916     DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
6917     DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
6918     DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
6919     DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
6920     DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
6921     DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
6922     DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
6923
6924     DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
6925     DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
6926
6927     DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6928     DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6929     DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6930     DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6931     DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
6932
6933     DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
6934     DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
6935     DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
6936     DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6937     DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6938
6939     DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6940     DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN, "access", "peon", NULL);
6941     DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6942     DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
6943
6944     DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
6945     DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
6946     DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
6947     DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
6948     DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
6949     DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
6950     DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
6951     DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
6952
6953     DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6954     DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6955     DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6956     DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
6957     DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
6958     DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
6959     DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
6960     DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
6961     DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6962     DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "peon", NULL);
6963     DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
6964     DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
6965     DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6966     DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6967
6968     DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "coowner", NULL);
6969     DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "master", NULL);
6970     DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "master", NULL);
6971     DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "master", NULL);
6972     DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "peon", NULL);
6973
6974     DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "peon", "flags", "+nolog", NULL);
6975     DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
6976
6977     DEFINE_COMMAND(access, 1, 0, "flags", "+nolog,+acceptchan", NULL);
6978     DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6979     DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6980     DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6981     DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6982     DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6983     DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6984     DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6985     DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6986     DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6987
6988     DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
6989     DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
6990
6991     DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
6992     DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
6993     DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
6994     DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
6995
6996     DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
6997     DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
6998     DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
6999     DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7000     DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7001
7002     DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7003     DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7004     DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7005     DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7006     DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7007     DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7008
7009     /* Channel options */
7010     DEFINE_CHANNEL_OPTION(defaulttopic);
7011     DEFINE_CHANNEL_OPTION(topicmask);
7012     DEFINE_CHANNEL_OPTION(greeting);
7013     DEFINE_CHANNEL_OPTION(usergreeting);
7014     DEFINE_CHANNEL_OPTION(modes);
7015     DEFINE_CHANNEL_OPTION(enfops);
7016     DEFINE_CHANNEL_OPTION(giveops);
7017     DEFINE_CHANNEL_OPTION(protect);
7018     DEFINE_CHANNEL_OPTION(enfmodes);
7019     DEFINE_CHANNEL_OPTION(enftopic);
7020     DEFINE_CHANNEL_OPTION(pubcmd);
7021     DEFINE_CHANNEL_OPTION(voice);
7022     DEFINE_CHANNEL_OPTION(userinfo);
7023     DEFINE_CHANNEL_OPTION(dynlimit);
7024     DEFINE_CHANNEL_OPTION(topicsnarf);
7025     DEFINE_CHANNEL_OPTION(nodelete);
7026     DEFINE_CHANNEL_OPTION(toys);
7027     DEFINE_CHANNEL_OPTION(setters);
7028     DEFINE_CHANNEL_OPTION(topicrefresh);
7029     DEFINE_CHANNEL_OPTION(ctcpusers);
7030     DEFINE_CHANNEL_OPTION(ctcpreaction);
7031     DEFINE_CHANNEL_OPTION(peoninvite);
7032     modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7033
7034     /* Alias set topic to set defaulttopic for compatibility. */
7035     modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7036
7037     /* User options */
7038     DEFINE_USER_OPTION(noautoop);
7039     DEFINE_USER_OPTION(autoinvite);
7040     DEFINE_USER_OPTION(info);
7041
7042     /* Alias uset autovoice to uset autoop. */
7043     modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7044
7045     note_types = dict_new();
7046     dict_set_free_data(note_types, chanserv_deref_note_type);
7047     saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7048     reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7049
7050     if(chanserv_conf.channel_expire_frequency)
7051         timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7052
7053     if(chanserv_conf.refresh_period)
7054     {
7055         time_t next_refresh;
7056         next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7057         timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7058     }
7059     
7060     reg_exit_func(chanserv_db_cleanup);
7061     message_register_table(msgtab);
7062 }