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