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