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