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