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