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