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