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