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