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