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