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