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