60f8991681d7778b00ba994e46a4de1b61368eaa
[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 = def_list;
3442     (void)zoot_list; /* since it doesn't show user levels */
3443
3444     if(user->handle_info)
3445     {
3446         switch(user->handle_info->userlist_style)
3447         {
3448         case HI_STYLE_DEF: send_list = def_list; break;
3449         case HI_STYLE_ZOOT: send_list = def_list; break;
3450         }
3451     }
3452
3453     lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3454     matches = 0;
3455     for(uData = channel->channel_info->users; uData; uData = uData->next)
3456     {
3457         if((uData->access < lowest)
3458            || (uData->access > highest)
3459            || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3460             continue;
3461         lData.users[matches++] = uData;
3462     }
3463     qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3464
3465     lData.table.length = matches+1;
3466     lData.table.width = 4;
3467     lData.table.flags = TABLE_NO_FREE;
3468     lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3469     ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3470     lData.table.contents[0] = ary;
3471     ary[0] = "Access";
3472     ary[1] = "Account";
3473     ary[2] = "Last Seen";
3474     ary[3] = "Status";
3475     for(matches = 1; matches < lData.table.length; ++matches)
3476     {
3477         struct userData *uData = lData.users[matches-1];
3478         char seen[INTERVALLEN];
3479
3480         ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3481         lData.table.contents[matches] = ary;
3482         ary[0] = strtab(uData->access);
3483         ary[1] = uData->handle->handle;
3484         if(uData->present)
3485             ary[2] = "Here";
3486         else if(!uData->seen)
3487             ary[2] = "Never";
3488         else
3489             ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3490         ary[2] = strdup(ary[2]);
3491         if(IsUserSuspended(uData))
3492             ary[3] = "Suspended";
3493         else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3494             ary[3] = "Vacation";
3495         else
3496             ary[3] = "Normal";
3497     }
3498     send_list(&lData);
3499     for(matches = 1; matches < lData.table.length; ++matches)
3500     {
3501         free((char*)lData.table.contents[matches][2]);
3502         free(lData.table.contents[matches]);
3503     }
3504     free(lData.table.contents[0]);
3505     free(lData.table.contents);
3506     return 1;
3507 }
3508
3509 static CHANSERV_FUNC(cmd_users)
3510 {
3511     return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3512 }
3513
3514 static CHANSERV_FUNC(cmd_wlist)
3515 {
3516     return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3517 }
3518
3519 static CHANSERV_FUNC(cmd_clist)
3520 {
3521     return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3522 }
3523
3524 static CHANSERV_FUNC(cmd_mlist)
3525 {
3526     return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3527 }
3528
3529 static CHANSERV_FUNC(cmd_olist)
3530 {
3531     return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3532 }
3533
3534 static CHANSERV_FUNC(cmd_plist)
3535 {
3536     return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3537 }
3538
3539 static CHANSERV_FUNC(cmd_bans)
3540 {
3541     struct helpfile_table tbl;
3542     unsigned int matches = 0, timed = 0, ii;
3543     char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3544     const char *msg_never, *triggered, *expires;
3545     struct banData *ban, **bans;
3546
3547     if(argc > 1)
3548         search = argv[1];
3549     else
3550         search = NULL;
3551
3552     bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3553
3554     for(ban = channel->channel_info->bans; ban; ban = ban->next)
3555     {
3556         if(search && !match_ircglobs(search, ban->mask))
3557             continue;
3558         bans[matches++] = ban;
3559         if(ban->expires)
3560             timed = 1;
3561     }
3562
3563     tbl.length = matches + 1;
3564     tbl.width = 4 + timed;
3565     tbl.flags = 0;
3566     tbl.flags = TABLE_NO_FREE;
3567     tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3568     tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3569     tbl.contents[0][0] = "Mask";
3570     tbl.contents[0][1] = "Set By";
3571     tbl.contents[0][2] = "Triggered";
3572     if(timed)
3573     {
3574         tbl.contents[0][3] = "Expires";
3575         tbl.contents[0][4] = "Reason";
3576     }
3577     else
3578         tbl.contents[0][3] = "Reason";
3579     if(!matches)
3580     {
3581         table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3582         reply("MSG_NONE");
3583         free(tbl.contents[0]);
3584         free(tbl.contents);
3585         return 0;
3586     }
3587
3588     msg_never = user_find_message(user, "MSG_NEVER");
3589     for(ii = 0; ii < matches; )
3590     {
3591         ban = bans[ii];
3592
3593         if(!timed)
3594             expires = "";
3595         else if(ban->expires)
3596             expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3597         else
3598             expires = msg_never;
3599
3600         if(ban->triggered)
3601             triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3602         else
3603             triggered = msg_never;
3604
3605         tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3606         tbl.contents[ii][0] = ban->mask;
3607         tbl.contents[ii][1] = ban->owner;
3608         tbl.contents[ii][2] = strdup(triggered);
3609         if(timed)
3610         {
3611             tbl.contents[ii][3] = strdup(expires);
3612             tbl.contents[ii][4] = ban->reason;
3613         }
3614         else
3615             tbl.contents[ii][3] = ban->reason;
3616     }
3617     table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3618     reply("MSG_MATCH_COUNT", matches);
3619     for(ii = 1; ii < tbl.length; ++ii)
3620     {
3621         free((char*)tbl.contents[ii][2]);
3622         if(timed)
3623             free((char*)tbl.contents[ii][3]);
3624         free(tbl.contents[ii]);
3625     }
3626     free(tbl.contents[0]);
3627     free(tbl.contents);
3628     return 1;
3629 }
3630
3631 static int
3632 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3633 {
3634     struct chanData *cData = channel->channel_info;
3635     if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3636         return 0;
3637     if(cData->topic_mask)
3638         return !match_ircglob(new_topic, cData->topic_mask);
3639     else if(cData->topic)
3640         return irccasecmp(new_topic, cData->topic);
3641     else
3642         return 0;
3643 }
3644
3645 static CHANSERV_FUNC(cmd_topic)
3646 {
3647     struct chanData *cData;
3648     char *topic;
3649
3650     cData = channel->channel_info;
3651     if(argc < 2)
3652     {
3653         if(cData->topic)
3654         {
3655             SetChannelTopic(channel, chanserv, cData->topic, 1);
3656             reply("CSMSG_TOPIC_SET", cData->topic);
3657             return 1;
3658         }
3659
3660         reply("CSMSG_NO_TOPIC", channel->name);
3661         return 0;
3662     }
3663
3664     topic = unsplit_string(argv + 1, argc - 1, NULL);
3665     /* If they say "!topic *", use an empty topic. */
3666     if((topic[0] == '*') && (topic[1] == 0))
3667         topic[0] = 0;
3668     if(bad_topic(channel, user, topic))
3669     {
3670         char *topic_mask = cData->topic_mask;
3671         if(topic_mask)
3672         {
3673             char new_topic[TOPICLEN+1], tchar;
3674             int pos=0, starpos=-1, dpos=0, len;
3675
3676             while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3677             {
3678                 switch(tchar)
3679                 {
3680                 case '*':
3681                     if(starpos != -1)
3682                         goto bad_mask;
3683                     len = strlen(topic);
3684                     if((dpos + len) > TOPICLEN)
3685                         len = TOPICLEN + 1 - dpos;
3686                     memcpy(new_topic+dpos, topic, len);
3687                     dpos += len;
3688                     starpos = pos;
3689                     break;
3690                 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3691                 default: new_topic[dpos++] = tchar; break;
3692                 }
3693             }
3694             if((dpos > TOPICLEN) || tchar)
3695             {
3696             bad_mask:
3697                 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3698                 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3699                 return 0;
3700             }
3701             new_topic[dpos] = 0;
3702             SetChannelTopic(channel, chanserv, new_topic, 1);
3703         } else {
3704             reply("CSMSG_TOPIC_LOCKED", channel->name);
3705             return 0;
3706         }
3707     }
3708     else
3709         SetChannelTopic(channel, chanserv, topic, 1);
3710
3711     if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3712     {
3713         /* Grab the topic and save it as the default topic. */
3714         free(cData->topic);
3715         cData->topic = strdup(channel->topic);
3716     }
3717
3718     return 1;
3719 }
3720
3721 static CHANSERV_FUNC(cmd_mode)
3722 {
3723     struct mod_chanmode *change;
3724     
3725     if(argc < 2)
3726     {
3727         change = &channel->channel_info->modes;
3728         if(change->modes_set || change->modes_clear) {
3729             modcmd_chanmode_announce(change);
3730             reply("CSMSG_DEFAULTED_MODES", channel->name);
3731         } else
3732             reply("CSMSG_NO_MODES", channel->name);
3733         return 1;
3734     }
3735
3736     change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE);
3737     if(!change)
3738     {
3739         reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3740         return 0;
3741     }
3742
3743     if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3744        && mode_lock_violated(&channel->channel_info->modes, change))
3745     {
3746         char modes[MAXLEN];
3747         mod_chanmode_format(&channel->channel_info->modes, modes);
3748         reply("CSMSG_MODE_LOCKED", modes, channel->name);
3749         return 0;
3750     }
3751
3752     modcmd_chanmode_announce(change);
3753     mod_chanmode_free(change);
3754     reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3755     return 1;
3756 }
3757
3758 static CHANSERV_FUNC(cmd_invite)
3759 {
3760     struct userData *uData;
3761     struct userNode *invite;
3762
3763     uData = GetChannelUser(channel->channel_info, user->handle_info);
3764
3765     if(argc > 1)
3766     {
3767         if(!(invite = GetUserH(argv[1])))
3768         {
3769             reply("MSG_NICK_UNKNOWN", argv[1]);
3770             return 0;
3771         }
3772     }
3773     else
3774         invite = user;
3775
3776     if(GetUserMode(channel, invite))
3777     {
3778         reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3779         return 0;
3780     }
3781
3782     if(user != invite)
3783     {
3784         if(argc > 2)
3785         {
3786             char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3787             send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3788         }
3789         else
3790             send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3791     }
3792     irc_invite(chanserv, invite, channel);
3793     if(argc > 1)
3794         reply("CSMSG_INVITED_USER", argv[1], channel->name);
3795
3796     return 1;
3797 }
3798
3799 static CHANSERV_FUNC(cmd_inviteme)
3800 {
3801     if(GetUserMode(channel, user))
3802     {
3803         reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3804         return 0;
3805     }
3806     if(channel->channel_info
3807        && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3808     {
3809         reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3810         return 0;
3811     }
3812     irc_invite(cmd->parent->bot, user, channel);
3813     return 1;
3814 }
3815
3816 static void
3817 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3818 {
3819     unsigned int combo;
3820     char buf1[INTERVALLEN], buf2[INTERVALLEN];
3821
3822     /* We display things based on two dimensions:
3823      * - Issue time: present or absent
3824      * - Expiration: revoked, expired, expires in future, or indefinite expiration
3825      * (in order of precedence, so something both expired and revoked
3826      * only counts as revoked)
3827      */
3828     combo = (suspended->issued ? 4 : 0)
3829         + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3830     switch(combo) {
3831     case 0: /* no issue time, indefinite expiration */
3832         reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3833         break;
3834     case 1: /* no issue time, expires in future */
3835         intervalString(buf1, suspended->expires-now, user->handle_info);
3836         reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3837         break;
3838     case 2: /* no issue time, expired */
3839         intervalString(buf1, now-suspended->expires, user->handle_info);
3840         reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3841         break;
3842     case 3: /* no issue time, revoked */
3843         intervalString(buf1, now-suspended->revoked, user->handle_info);
3844         reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3845         break;
3846     case 4: /* issue time set, indefinite expiration */
3847         intervalString(buf1, now-suspended->issued, user->handle_info);
3848         reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3849         break;
3850     case 5: /* issue time set, expires in future */
3851         intervalString(buf1, now-suspended->issued, user->handle_info);
3852         intervalString(buf2, suspended->expires-now, user->handle_info);
3853         reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3854         break;
3855     case 6: /* issue time set, expired */
3856         intervalString(buf1, now-suspended->issued, user->handle_info);
3857         intervalString(buf2, now-suspended->expires, user->handle_info);
3858         reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3859         break;
3860     case 7: /* issue time set, revoked */
3861         intervalString(buf1, now-suspended->issued, user->handle_info);
3862         intervalString(buf2, now-suspended->revoked, user->handle_info);
3863         reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3864         break;
3865     default:
3866         log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3867         return;
3868     }
3869 }
3870
3871 static CHANSERV_FUNC(cmd_info)
3872 {
3873     char modes[MAXLEN], buffer[INTERVALLEN];
3874     struct userData *uData, *owner;
3875     struct chanData *cData;
3876     struct do_not_register *dnr;
3877     struct note *note;
3878     dict_iterator_t it;
3879     int privileged;
3880
3881     cData = channel->channel_info;
3882     reply("CSMSG_CHANNEL_INFO", channel->name);
3883
3884     uData = GetChannelUser(cData, user->handle_info);
3885     if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3886     {
3887         mod_chanmode_format(&cData->modes, modes);
3888         reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3889         reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3890     }
3891
3892     for(it = dict_first(cData->notes); it; it = iter_next(it))
3893     {
3894         int padding;
3895
3896         note = iter_data(it);
3897         if(!note_type_visible_to_user(cData, note->type, user))
3898             continue;
3899
3900         padding = PADLEN - 1 - strlen(iter_key(it));
3901         reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3902     }
3903
3904     reply("CSMSG_CHANNEL_MAX", cData->max);
3905     for(owner = cData->users; owner; owner = owner->next)
3906         if(owner->access == UL_OWNER)
3907             reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3908     reply("CSMSG_CHANNEL_USERS", cData->userCount);
3909     reply("CSMSG_CHANNEL_BANS", cData->banCount);
3910     reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3911     reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3912
3913     privileged = IsStaff(user);
3914     if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3915         reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3916
3917     if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3918         chanserv_show_dnrs(user, cmd, channel->name, NULL);
3919
3920     if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3921     {
3922         struct suspended *suspended;
3923         reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3924         for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3925             show_suspension_info(cmd, user, suspended);
3926     }
3927     else if(IsSuspended(cData))
3928     {
3929         reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3930         show_suspension_info(cmd, user, cData->suspended);
3931     }
3932     return 1;
3933 }
3934
3935 static CHANSERV_FUNC(cmd_netinfo)
3936 {
3937     extern time_t boot_time;
3938     extern unsigned long burst_length;
3939     char interval[INTERVALLEN];
3940
3941     reply("CSMSG_NETWORK_INFO");
3942     reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3943     reply("CSMSG_NETWORK_USERS", dict_size(clients));
3944     reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3945     reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3946     reply("CSMSG_NETWORK_BANS", banCount);
3947     reply("CSMSG_NETWORK_CHANUSERS", userCount);
3948     reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
3949     reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
3950     return 1;
3951 }
3952
3953 static void
3954 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3955 {
3956     struct helpfile_table table;
3957     unsigned int nn;
3958     struct userNode *user;
3959     char *nick;
3960
3961     table.length = 0;
3962     table.width = 1;
3963     table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3964     table.contents = alloca(list->used*sizeof(*table.contents));
3965     for(nn=0; nn<list->used; nn++)
3966     {
3967         user = list->list[nn];
3968         if(user->modes & skip_flags)
3969             continue;
3970         if(IsBot(user))
3971             continue;
3972         table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3973         if(IsAway(user))
3974         {
3975             nick = alloca(strlen(user->nick)+3);
3976             sprintf(nick, "(%s)", user->nick);
3977         }
3978         else
3979             nick = user->nick;
3980         table.contents[table.length][0] = nick;
3981         table.length++;
3982     }
3983     table_send(chanserv, to->nick, 0, NULL, table);
3984 }
3985
3986 static CHANSERV_FUNC(cmd_ircops)
3987 {
3988     reply("CSMSG_STAFF_OPERS");
3989     send_staff_list(user, &curr_opers, FLAGS_SERVICE);
3990     return 1;
3991 }
3992
3993 static CHANSERV_FUNC(cmd_helpers)
3994 {
3995     reply("CSMSG_STAFF_HELPERS");
3996     send_staff_list(user, &curr_helpers, FLAGS_OPER);
3997     return 1;
3998 }
3999
4000 static CHANSERV_FUNC(cmd_staff)
4001 {
4002     reply("CSMSG_NETWORK_STAFF");
4003     cmd_ircops(CSFUNC_ARGS);
4004     cmd_helpers(CSFUNC_ARGS);
4005     return 1;
4006 }
4007
4008 static CHANSERV_FUNC(cmd_peek)
4009 {
4010     struct modeNode *mn;
4011     char modes[MODELEN];
4012     unsigned int n;
4013     struct helpfile_table table;
4014
4015     irc_make_chanmode(channel, modes);
4016
4017     reply("CSMSG_PEEK_INFO", channel->name);
4018     reply("CSMSG_PEEK_TOPIC", channel->topic);
4019     reply("CSMSG_PEEK_MODES", modes);
4020     reply("CSMSG_PEEK_USERS", channel->members.used);
4021
4022     table.length = 0;
4023     table.width = 1;
4024     table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4025     table.contents = alloca(channel->members.used*sizeof(*table.contents));
4026     for(n = 0; n < channel->members.used; n++)
4027     {
4028         mn = channel->members.list[n];
4029         if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4030             continue;
4031         table.contents[table.length] = alloca(sizeof(**table.contents));
4032         table.contents[table.length][0] = mn->user->nick;
4033         table.length++;
4034     }
4035     if(table.length)
4036     {
4037         reply("CSMSG_PEEK_OPS");
4038         table_send(chanserv, user->nick, 0, NULL, table);
4039     }
4040     else
4041         reply("CSMSG_PEEK_NO_OPS");
4042     return 1;
4043 }
4044
4045 static MODCMD_FUNC(cmd_wipeinfo)
4046 {
4047     struct handle_info *victim;
4048     struct userData *ud, *actor;
4049
4050     REQUIRE_PARAMS(2);
4051     actor = GetChannelUser(channel->channel_info, user->handle_info);
4052     if(!(victim = modcmd_get_handle_info(user, argv[1])))
4053         return 0;
4054     if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4055     {
4056         reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4057         return 0;
4058     }
4059     if((ud->access >= actor->access) && (ud != actor))
4060     {
4061         reply("MSG_USER_OUTRANKED", victim->handle);
4062         return 0;
4063     }
4064     if(ud->info)
4065         free(ud->info);
4066     ud->info = NULL;
4067     reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4068     return 1;
4069 }
4070
4071 static CHANSERV_FUNC(cmd_resync)
4072 {
4073     struct mod_chanmode *changes;
4074     struct chanData *cData = channel->channel_info;
4075     unsigned int ii, used;
4076
4077     changes = mod_chanmode_alloc(channel->members.used * 2);
4078     for(ii = used = 0; ii < channel->members.used; ++ii)
4079     {
4080         struct modeNode *mn = channel->members.list[ii];
4081         struct userData *uData;
4082
4083         if(IsService(mn->user))
4084             continue;
4085
4086         uData = GetChannelAccess(cData, mn->user->handle_info);
4087         if(!cData->lvlOpts[lvlGiveOps]
4088            || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4089         {
4090             if(!(mn->modes & MODE_CHANOP))
4091             {
4092                 changes->args[used].mode = MODE_CHANOP;
4093                 changes->args[used++].member = mn;
4094             }
4095         }
4096         else if(!cData->lvlOpts[lvlGiveVoice]
4097                 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4098         {
4099             if(mn->modes & MODE_CHANOP)
4100             {
4101                 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4102                 changes->args[used++].member = mn;
4103             }
4104             if(!(mn->modes & MODE_VOICE))
4105             {
4106                 changes->args[used].mode = MODE_VOICE;
4107                 changes->args[used++].member = mn;
4108             }
4109         }
4110         else
4111         {
4112             if(mn->modes)
4113             {
4114                 changes->args[used].mode = MODE_REMOVE | mn->modes;
4115                 changes->args[used++].member = mn;
4116             }
4117         }
4118     }
4119     changes->argc = used;
4120     modcmd_chanmode_announce(changes);
4121     mod_chanmode_free(changes);
4122     reply("CSMSG_RESYNCED_USERS", channel->name);
4123     return 1;
4124 }
4125
4126 static CHANSERV_FUNC(cmd_seen)
4127 {
4128     struct userData *uData;
4129     struct handle_info *handle;
4130     char seen[INTERVALLEN];
4131
4132     REQUIRE_PARAMS(2);
4133
4134     if(!irccasecmp(argv[1], chanserv->nick))
4135     {
4136         reply("CSMSG_IS_CHANSERV");
4137         return 1;
4138     }
4139
4140     if(!(handle = get_handle_info(argv[1])))
4141     {
4142         reply("MSG_HANDLE_UNKNOWN", argv[1]);
4143         return 0;
4144     }
4145
4146     if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4147     {
4148         reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4149         return 0;
4150     }
4151
4152     if(uData->present)
4153         reply("CSMSG_USER_PRESENT", handle->handle);
4154     else if(uData->seen)
4155         reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4156     else
4157         reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4158
4159     if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4160         reply("CSMSG_USER_VACATION", handle->handle);
4161
4162     return 1;
4163 }
4164
4165 static MODCMD_FUNC(cmd_names)
4166 {
4167     struct userNode *targ;
4168     struct userData *targData;
4169     unsigned int ii, pos;
4170     char buf[400];
4171
4172     for(ii=pos=0; ii<channel->members.used; ++ii)
4173     {
4174         targ = channel->members.list[ii]->user;
4175         targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4176         if(!targData)
4177             continue;
4178         if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4179         {
4180             buf[pos] = 0;
4181             reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4182             pos = 0;
4183         }
4184         buf[pos++] = ' ';
4185         if(IsUserSuspended(targData))
4186             buf[pos++] = 's';
4187         pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4188     }
4189     buf[pos] = 0;
4190     reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4191     reply("CSMSG_END_NAMES", channel->name);
4192     return 1;
4193 }
4194
4195 static int
4196 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4197 {
4198     switch(ntype->visible_type)
4199     {
4200     case NOTE_VIS_ALL: return 1;
4201     case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4202     case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4203     }
4204 }
4205
4206 static int
4207 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4208 {
4209     struct userData *uData;
4210
4211     switch(ntype->set_access_type)
4212     {
4213     case NOTE_SET_CHANNEL_ACCESS:
4214         if(!user->handle_info)
4215             return 0;
4216         if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4217             return 0;
4218         return uData->access >= ntype->set_access.min_ulevel;
4219     case NOTE_SET_CHANNEL_SETTER:
4220         return check_user_level(channel, user, lvlSetters, 1, 0);
4221     case NOTE_SET_PRIVILEGED: default:
4222         return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4223     }
4224 }
4225
4226 static CHANSERV_FUNC(cmd_note)
4227 {
4228     struct chanData *cData;
4229     struct note *note;
4230     struct note_type *ntype;
4231
4232     cData = channel->channel_info;
4233     if(!cData)
4234     {
4235         reply("CSMSG_NOT_REGISTERED", channel->name);
4236         return 0;
4237     }
4238
4239     /* If no arguments, show all visible notes for the channel. */
4240     if(argc < 2)
4241     {
4242         dict_iterator_t it;
4243         unsigned int count;
4244
4245         for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4246         {
4247             note = iter_data(it);
4248             if(!note_type_visible_to_user(cData, note->type, user))
4249                 continue;
4250             if(!count++)
4251                 reply("CSMSG_NOTELIST_HEADER", channel->name);
4252             reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4253         }
4254         if(count)
4255             reply("CSMSG_NOTELIST_END", channel->name);
4256         else
4257             reply("CSMSG_NOTELIST_EMPTY", channel->name);
4258     }
4259     /* If one argument, show the named note. */
4260     else if(argc == 2)
4261     {
4262         if((note = dict_find(cData->notes, argv[1], NULL))
4263            && note_type_visible_to_user(cData, note->type, user))
4264         {
4265             reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4266         }
4267         else if((ntype = dict_find(note_types, argv[1], NULL))
4268                 && note_type_visible_to_user(NULL, ntype, user))
4269         {
4270             reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4271             return 0;
4272         }
4273         else
4274         {
4275             reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4276             return 0;
4277         }
4278     }
4279     /* Assume they're trying to set a note. */
4280     else
4281     {
4282         char *note_text;
4283         ntype = dict_find(note_types, argv[1], NULL);
4284         if(!ntype)
4285         {
4286             reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4287             return 0;
4288         }
4289         else if(note_type_settable_by_user(channel, ntype, user))
4290         {
4291             note_text = unsplit_string(argv+2, argc-2, NULL);
4292             if((note = dict_find(cData->notes, argv[1], NULL)))
4293                 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4294             chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4295             reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4296
4297             if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4298             {
4299                 /* The note is viewable to staff only, so return 0
4300                    to keep the invocation from getting logged (or
4301                    regular users can see it in !events). */
4302                 return 0;
4303             }
4304         }
4305         else
4306         {
4307             reply("CSMSG_NO_ACCESS");
4308             return 0;
4309         }
4310     }
4311     return 1;
4312 }
4313
4314 static CHANSERV_FUNC(cmd_delnote)
4315 {
4316     struct note *note;
4317
4318     REQUIRE_PARAMS(2);
4319     if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4320        || !note_type_settable_by_user(channel, note->type, user))
4321     {
4322         reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4323         return 0;
4324     }
4325     dict_remove(channel->channel_info->notes, note->type->name);
4326     reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4327     return 1;
4328 }
4329
4330 static CHANSERV_FUNC(cmd_events)
4331 {
4332     struct logSearch discrim;
4333     struct logReport report;
4334     unsigned int matches, limit;
4335
4336     limit = (argc > 1) ? atoi(argv[1]) : 10;
4337     if(limit < 1 || limit > 200)
4338         limit = 10;
4339
4340     memset(&discrim, 0, sizeof(discrim));
4341     discrim.masks.bot = chanserv;
4342     discrim.masks.channel_name = channel->name;
4343     if(argc > 2)
4344         discrim.masks.command = argv[2];
4345     discrim.limit = limit;
4346     discrim.max_time = INT_MAX;
4347     discrim.severities = 1 << LOG_COMMAND;
4348     report.reporter = chanserv;
4349     report.user = user;
4350     reply("CSMSG_EVENT_SEARCH_RESULTS");
4351     matches = log_entry_search(&discrim, log_report_entry, &report);
4352     if(matches)
4353         reply("MSG_MATCH_COUNT", matches);
4354     else
4355         reply("MSG_NO_MATCHES");
4356     return 1;
4357 }
4358
4359 static CHANSERV_FUNC(cmd_say)
4360 {
4361     char *msg;
4362     if(channel)
4363     {
4364         REQUIRE_PARAMS(2);
4365         msg = unsplit_string(argv + 1, argc - 1, NULL);
4366         send_channel_message(channel, cmd->parent->bot, "%s", msg);
4367     }
4368     else if(GetUserH(argv[1]))
4369     {
4370         REQUIRE_PARAMS(3);
4371         msg = unsplit_string(argv + 2, argc - 2, NULL);
4372         send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4373     }
4374     else
4375     {
4376         reply("MSG_NOT_TARGET_NAME");
4377         return 0;
4378     }
4379     return 1;
4380 }
4381
4382 static CHANSERV_FUNC(cmd_emote)
4383 {
4384     char *msg;
4385     assert(argc >= 2);
4386     if(channel)
4387     {
4388         /* CTCP is so annoying. */
4389         msg = unsplit_string(argv + 1, argc - 1, NULL);
4390         send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4391     }
4392     else if(GetUserH(argv[1]))
4393     {
4394         msg = unsplit_string(argv + 2, argc - 2, NULL);
4395         send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4396     }
4397     else
4398     {
4399         reply("MSG_NOT_TARGET_NAME");
4400         return 0;
4401     }
4402     return 1;
4403 }
4404
4405 struct channelList *
4406 chanserv_support_channels(void)
4407 {
4408     return &chanserv_conf.support_channels;
4409 }
4410
4411 static CHANSERV_FUNC(cmd_expire)
4412 {
4413     int channel_count = registered_channels;
4414     expire_channels(NULL);
4415     reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4416     return 1;
4417 }
4418
4419 static void
4420 chanserv_expire_suspension(void *data)
4421 {
4422     struct suspended *suspended = data;
4423     struct chanNode *channel;
4424     struct mod_chanmode change;
4425
4426     if(!suspended->expires || (now < suspended->expires))
4427         suspended->revoked = now;
4428     channel = suspended->cData->channel;
4429     suspended->cData->channel = channel;
4430     suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4431     mod_chanmode_init(&change);
4432     change.argc = 1;
4433     change.args[0].mode = MODE_CHANOP;
4434     change.args[0].member = AddChannelUser(chanserv, channel);
4435     mod_chanmode_announce(chanserv, channel, &change);
4436 }
4437
4438 static CHANSERV_FUNC(cmd_csuspend)
4439 {
4440     struct suspended *suspended;
4441     char reason[MAXLEN];
4442     time_t expiry, duration;
4443     struct userData *uData;
4444
4445     REQUIRE_PARAMS(3);
4446
4447     if(IsProtected(channel->channel_info))
4448     {
4449         reply("CSMSG_SUSPEND_NODELETE", channel->name);
4450         return 0;
4451     }
4452
4453     if(argv[1][0] == '!')
4454         argv[1]++;
4455     else if(IsSuspended(channel->channel_info))
4456     {
4457         reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4458         show_suspension_info(cmd, user, channel->channel_info->suspended);
4459         return 0;
4460     }
4461
4462     if(!strcmp(argv[1], "0"))
4463         expiry = 0;
4464     else if((duration = ParseInterval(argv[1])))
4465         expiry = now + duration;
4466     else
4467     {
4468         reply("MSG_INVALID_DURATION", argv[1]);
4469         return 0;
4470     }
4471
4472     unsplit_string(argv + 2, argc - 2, reason);
4473
4474     suspended = calloc(1, sizeof(*suspended));
4475     suspended->revoked = 0;
4476     suspended->issued = now;
4477     suspended->suspender = strdup(user->handle_info->handle);
4478     suspended->expires = expiry;
4479     suspended->reason = strdup(reason);
4480     suspended->cData = channel->channel_info;
4481     suspended->previous = suspended->cData->suspended;
4482     suspended->cData->suspended = suspended;
4483
4484     if(suspended->expires)
4485         timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4486
4487     if(IsSuspended(channel->channel_info))
4488     {
4489         suspended->previous->revoked = now;
4490         if(suspended->previous->expires)
4491             timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4492         sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4493         global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4494     }
4495     else
4496     {
4497         /* Mark all users in channel as absent. */
4498         for(uData = channel->channel_info->users; uData; uData = uData->next)
4499         {
4500             if(uData->present)
4501             {
4502                 uData->seen = now;
4503                 uData->present = 0;
4504             }
4505         }
4506
4507         /* Mark the channel as suspended, then part. */
4508         channel->channel_info->flags |= CHANNEL_SUSPENDED;
4509         DelChannelUser(chanserv, channel, suspended->reason, 0);
4510         reply("CSMSG_SUSPENDED", channel->name);
4511         sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4512         global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4513     }
4514     return 1;
4515 }
4516
4517 static CHANSERV_FUNC(cmd_cunsuspend)
4518 {
4519     struct suspended *suspended;
4520     char message[MAXLEN];
4521
4522     if(!IsSuspended(channel->channel_info))
4523     {
4524         reply("CSMSG_NOT_SUSPENDED", channel->name);
4525         return 0;
4526     }
4527
4528     suspended = channel->channel_info->suspended;
4529
4530     /* Expire the suspension and join ChanServ to the channel. */
4531     timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4532     chanserv_expire_suspension(suspended);
4533     reply("CSMSG_UNSUSPENDED", channel->name);
4534     sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4535     global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4536     return 1;
4537 }
4538
4539 typedef struct chanservSearch
4540 {
4541     char *name;
4542     char *registrar;
4543
4544     time_t unvisited;
4545     time_t registered;
4546
4547     unsigned long flags;
4548     unsigned int limit;
4549 } *search_t;
4550
4551 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4552
4553 static search_t
4554 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4555 {
4556     search_t search;
4557     unsigned int i;
4558
4559     search = malloc(sizeof(struct chanservSearch));
4560     memset(search, 0, sizeof(*search));
4561     search->limit = 25;
4562
4563     for(i = 0; i < argc; i++)
4564     {
4565         /* Assume all criteria require arguments. */
4566         if(i == (argc - 1))
4567         {
4568             send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4569             goto fail;
4570         }
4571
4572         if(!irccasecmp(argv[i], "name"))
4573             search->name = argv[++i];
4574         else if(!irccasecmp(argv[i], "registrar"))
4575             search->registrar = argv[++i];
4576         else if(!irccasecmp(argv[i], "unvisited"))
4577             search->unvisited = ParseInterval(argv[++i]);
4578         else if(!irccasecmp(argv[i], "registered"))
4579             search->registered = ParseInterval(argv[++i]);
4580         else if(!irccasecmp(argv[i], "flags"))
4581         {
4582             i++;
4583             if(!irccasecmp(argv[i], "nodelete"))
4584                 search->flags |= CHANNEL_NODELETE;
4585             else if(!irccasecmp(argv[i], "suspended"))
4586                 search->flags |= CHANNEL_SUSPENDED;
4587             else
4588             {
4589                 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4590                 goto fail;
4591             }
4592         }
4593         else if(!irccasecmp(argv[i], "limit"))
4594             search->limit = strtoul(argv[++i], NULL, 10);
4595         else
4596         {
4597             send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4598             goto fail;
4599         }
4600     }
4601
4602     if(search->name && !strcmp(search->name, "*"))
4603         search->name = 0;
4604     if(search->registrar && !strcmp(search->registrar, "*"))
4605         search->registrar = 0;
4606
4607     return search;
4608   fail:
4609     free(search);
4610     return NULL;
4611 }
4612
4613 static int
4614 chanserv_channel_match(struct chanData *channel, search_t search)
4615 {
4616     const char *name = channel->channel->name;
4617     if((search->name && !match_ircglob(name, search->name)) ||
4618        (search->registrar && !channel->registrar) ||
4619        (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4620        (search->unvisited && (now - channel->visited) < search->unvisited) ||
4621        (search->registered && (now - channel->registered) > search->registered) ||
4622        (search->flags && ((search->flags & channel->flags) != search->flags)))
4623         return 0;
4624
4625     return 1;
4626 }
4627
4628 static unsigned int
4629 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4630 {
4631     struct chanData *channel;
4632     unsigned int matches = 0;
4633
4634     for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4635     {
4636         if(!chanserv_channel_match(channel, search))
4637             continue;
4638         matches++;
4639         smf(channel, data);
4640     }
4641
4642     return matches;
4643 }
4644
4645 static void
4646 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4647 {
4648 }
4649
4650 static void
4651 search_print(struct chanData *channel, void *data)
4652 {
4653     send_message_type(4, data, chanserv, "%s", channel->channel->name);
4654 }
4655
4656 static CHANSERV_FUNC(cmd_search)
4657 {
4658     search_t search;
4659     unsigned int matches;
4660     channel_search_func action;
4661
4662     REQUIRE_PARAMS(3);
4663
4664     if(!irccasecmp(argv[1], "count"))
4665         action = search_count;
4666     else if(!irccasecmp(argv[1], "print"))
4667         action = search_print;
4668     else
4669     {
4670         reply("CSMSG_ACTION_INVALID", argv[1]);
4671         return 0;
4672     }
4673
4674     search = chanserv_search_create(user, argc - 2, argv + 2);
4675     if(!search)
4676         return 0;
4677
4678     if(action == search_count)
4679         search->limit = INT_MAX;
4680
4681     if(action == search_print)
4682         reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4683
4684     matches = chanserv_channel_search(search, action, user);
4685
4686     if(matches)
4687         reply("MSG_MATCH_COUNT", matches);
4688     else
4689         reply("MSG_NO_MATCHES");
4690
4691     free(search);
4692     return 1;
4693 }
4694
4695 static CHANSERV_FUNC(cmd_unvisited)
4696 {
4697     struct chanData *cData;
4698     time_t interval = chanserv_conf.channel_expire_delay;
4699     char buffer[INTERVALLEN];
4700     unsigned int limit = 25, matches = 0;
4701
4702     if(argc > 1)
4703     {
4704         interval = ParseInterval(argv[1]);
4705         if(argc > 2)
4706             limit = atoi(argv[2]);
4707     }
4708
4709     intervalString(buffer, interval, user->handle_info);
4710     reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4711
4712     for(cData = channelList; cData && matches < limit; cData = cData->next)
4713     {
4714         if((now - cData->visited) < interval)
4715             continue;
4716
4717         intervalString(buffer, now - cData->visited, user->handle_info);
4718         reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4719         matches++;
4720     }
4721
4722     return 1;
4723 }
4724
4725 static MODCMD_FUNC(chan_opt_defaulttopic)
4726 {
4727     if(argc > 1)
4728     {
4729         char *topic;
4730
4731         if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4732         {
4733             reply("CSMSG_TOPIC_LOCKED", channel->name);
4734             return 0;
4735         }
4736
4737         topic = unsplit_string(argv+1, argc-1, NULL);
4738
4739         free(channel->channel_info->topic);
4740         if(topic[0] == '*' && topic[1] == 0)
4741         {
4742             topic = channel->channel_info->topic = NULL;
4743         }
4744         else
4745         {
4746             topic = channel->channel_info->topic = strdup(topic);
4747             if(channel->channel_info->topic_mask
4748                && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4749                 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4750         }
4751         SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4752     }
4753
4754     if(channel->channel_info->topic)
4755         reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4756     else
4757         reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4758     return 1;
4759 }
4760
4761 static MODCMD_FUNC(chan_opt_topicmask)
4762 {
4763     if(argc > 1)
4764     {
4765         struct chanData *cData = channel->channel_info;
4766         char *mask;
4767
4768         if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4769         {
4770             reply("CSMSG_TOPIC_LOCKED", channel->name);
4771             return 0;
4772         }
4773
4774         mask = unsplit_string(argv+1, argc-1, NULL);
4775
4776         if(cData->topic_mask)
4777             free(cData->topic_mask);
4778         if(mask[0] == '*' && mask[1] == 0)
4779         {
4780             cData->topic_mask = 0;
4781         }
4782         else
4783         {
4784             cData->topic_mask = strdup(mask);
4785             if(!cData->topic)
4786                 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4787             else if(!match_ircglob(cData->topic, cData->topic_mask))
4788                 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4789         }
4790     }
4791
4792     if(channel->channel_info->topic_mask)
4793         reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4794     else
4795         reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4796     return 1;
4797 }
4798
4799 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4800 {
4801     if(argc > 1)
4802     {
4803         char *greeting = unsplit_string(argv+1, argc-1, NULL);
4804         char *previous;
4805
4806         previous = *data;
4807         if(greeting[0] == '*' && greeting[1] == 0)
4808             *data = NULL;
4809         else
4810         {
4811             unsigned int length = strlen(greeting);
4812             if(length > chanserv_conf.greeting_length)
4813             {
4814                 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4815                 return 0;
4816             }
4817             *data = strdup(greeting);
4818         }
4819         if(previous)
4820             free(previous);
4821     }
4822
4823     if(*data)
4824         reply(name, *data);
4825     else
4826         reply(name, user_find_message(user, "MSG_NONE"));
4827     return 1;
4828 }
4829
4830 static MODCMD_FUNC(chan_opt_greeting)
4831 {
4832     return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4833 }
4834
4835 static MODCMD_FUNC(chan_opt_usergreeting)
4836 {
4837     return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4838 }
4839
4840 static MODCMD_FUNC(chan_opt_modes)
4841 {
4842     struct mod_chanmode *new_modes;
4843     char modes[MODELEN];
4844
4845     if(argc > 1)
4846     {
4847         if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4848         {
4849             reply("CSMSG_NO_ACCESS");
4850             return 0;
4851         }
4852         if(argv[1][0] == '*' && argv[1][1] == 0)
4853         {
4854             memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4855         }
4856         else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE)))
4857         {
4858             reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4859             return 0;
4860         }
4861         else if(new_modes->argc > 1)
4862         {
4863             reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4864             mod_chanmode_free(new_modes);
4865             return 0;
4866         }
4867         else
4868         {
4869             channel->channel_info->modes = *new_modes;
4870             modcmd_chanmode_announce(new_modes);
4871             mod_chanmode_free(new_modes);
4872         }
4873     }
4874
4875     mod_chanmode_format(&channel->channel_info->modes, modes);
4876     if(modes[0])
4877         reply("CSMSG_SET_MODES", modes);
4878     else
4879         reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4880     return 1;
4881 }
4882
4883 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4884 static int
4885 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4886 {
4887     struct chanData *cData = channel->channel_info;
4888     int value;
4889
4890     if(argc > 1)
4891     {
4892         /* Set flag according to value. */
4893         if(enabled_string(argv[1]))
4894         {
4895             cData->flags |= mask;
4896             value = 1;
4897         }
4898         else if(disabled_string(argv[1]))
4899         {
4900             cData->flags &= ~mask;
4901             value = 0;
4902         }
4903         else
4904         {
4905             reply("MSG_INVALID_BINARY", argv[1]);
4906             return 0;
4907         }
4908     }
4909     else
4910     {
4911         /* Find current option value. */
4912         value = (cData->flags & mask) ? 1 : 0;
4913     }
4914
4915     if(value)
4916         reply(name, user_find_message(user, "MSG_ON"));
4917     else
4918         reply(name, user_find_message(user, "MSG_OFF"));
4919     return 1;
4920 }
4921
4922 static MODCMD_FUNC(chan_opt_nodelete)
4923 {
4924     if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4925     {
4926         reply("MSG_SETTING_PRIVILEGED", argv[0]);
4927         return 0;
4928     }
4929
4930     CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4931 }
4932
4933 static MODCMD_FUNC(chan_opt_dynlimit)
4934 {
4935     CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4936 }
4937
4938 static MODCMD_FUNC(chan_opt_offchannel)
4939 {
4940     struct chanData *cData = channel->channel_info;
4941     int value;
4942
4943     if(argc > 1)
4944     {
4945         /* Set flag according to value. */
4946         if(enabled_string(argv[1]))
4947         {
4948             if(!IsOffChannel(cData))
4949                 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
4950             cData->flags |= CHANNEL_OFFCHANNEL;
4951             value = 1;
4952         }
4953         else if(disabled_string(argv[1]))
4954         {
4955             if(IsOffChannel(cData))
4956             {
4957                 struct mod_chanmode change;
4958                 mod_chanmode_init(&change);
4959                 change.argc = 1;
4960                 change.args[0].mode = MODE_CHANOP;
4961                 change.args[0].member = AddChannelUser(chanserv, channel);
4962                 mod_chanmode_announce(chanserv, channel, &change);
4963             }
4964             cData->flags &= ~CHANNEL_OFFCHANNEL;
4965             value = 0;
4966         }
4967         else
4968         {
4969             reply("MSG_INVALID_BINARY", argv[1]);
4970             return 0;
4971         }
4972     }
4973     else
4974     {
4975         /* Find current option value. */
4976         value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
4977     }
4978
4979     if(value)
4980         reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
4981     else
4982         reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
4983     return 1;
4984 }
4985
4986 static MODCMD_FUNC(chan_opt_defaults)
4987 {
4988     struct userData *uData;
4989     struct chanData *cData;
4990     const char *confirm;
4991     enum levelOption lvlOpt;
4992     enum charOption chOpt;
4993
4994     cData = channel->channel_info;
4995     uData = GetChannelUser(cData, user->handle_info);
4996     if(!uData || (uData->access < UL_OWNER))
4997     {
4998         reply("CSMSG_OWNER_DEFAULTS", channel->name);
4999         return 0;
5000     }
5001     confirm = make_confirmation_string(uData);
5002     if((argc < 2) || strcmp(argv[1], confirm))
5003     {
5004         reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5005         return 0;
5006     }
5007     cData->flags = CHANNEL_DEFAULT_FLAGS;
5008     cData->modes = chanserv_conf.default_modes;
5009     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5010         cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5011     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5012         cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5013     reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5014     return 1;
5015 }
5016
5017 static int
5018 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5019 {
5020     struct chanData *cData = channel->channel_info;
5021     struct userData *uData;
5022     unsigned short value;
5023
5024     if(argc > 1)
5025     {
5026         if(!check_user_level(channel, user, option, 1, 1))
5027         {
5028             reply("CSMSG_CANNOT_SET");
5029             return 0;
5030         }
5031         value = user_level_from_name(argv[1], UL_OWNER+1);
5032         if(!value && strcmp(argv[1], "0"))
5033         {
5034             reply("CSMSG_INVALID_ACCESS", argv[1]);
5035             return 0;
5036         }
5037         uData = GetChannelUser(cData, user->handle_info);
5038         if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5039         {
5040             reply("CSMSG_BAD_SETLEVEL");
5041             return 0;
5042         }
5043         switch(option)
5044         {
5045         case lvlGiveVoice:
5046             if(value > cData->lvlOpts[lvlGiveOps])
5047             {
5048                 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5049                 return 0;
5050             }
5051             break;
5052         case lvlGiveOps:
5053             if(value < cData->lvlOpts[lvlGiveVoice])
5054             {
5055                 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5056                 return 0;
5057             }
5058             break;
5059         case lvlSetters:
5060             /* This test only applies to owners, since non-owners
5061              * trying to set an option to above their level get caught
5062              * by the CSMSG_BAD_SETLEVEL test above.
5063              */
5064             if(value > uData->access)
5065             {
5066                 reply("CSMSG_BAD_SETTERS");
5067                 return 0;
5068             }
5069             break;
5070         default:
5071             break;
5072         }
5073         cData->lvlOpts[option] = value;
5074     }
5075     reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5076     return argc > 1;
5077 }
5078
5079 static MODCMD_FUNC(chan_opt_enfops)
5080 {
5081     return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5082 }
5083
5084 static MODCMD_FUNC(chan_opt_giveops)
5085 {
5086     return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5087 }
5088
5089 static MODCMD_FUNC(chan_opt_enfmodes)
5090 {
5091     return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5092 }
5093
5094 static MODCMD_FUNC(chan_opt_enftopic)
5095 {
5096     return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5097 }
5098
5099 static MODCMD_FUNC(chan_opt_pubcmd)
5100 {
5101     return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5102 }
5103
5104 static MODCMD_FUNC(chan_opt_setters)
5105 {
5106     return channel_level_option(lvlSetters, CSFUNC_ARGS);
5107 }
5108
5109 static MODCMD_FUNC(chan_opt_ctcpusers)
5110 {
5111     return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5112 }
5113
5114 static MODCMD_FUNC(chan_opt_userinfo)
5115 {
5116     return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5117 }
5118
5119 static MODCMD_FUNC(chan_opt_givevoice)
5120 {
5121     return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5122 }
5123
5124 static MODCMD_FUNC(chan_opt_topicsnarf)
5125 {
5126     return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5127 }
5128
5129 static MODCMD_FUNC(chan_opt_inviteme)
5130 {
5131     return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5132 }
5133
5134 static int
5135 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5136 {
5137     struct chanData *cData = channel->channel_info;
5138     int count = charOptions[option].count, index;
5139
5140     if(argc > 1)
5141     {
5142         index = atoi(argv[1]);
5143
5144         if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5145         {
5146             reply("CSMSG_INVALID_NUMERIC", index);
5147             /* Show possible values. */
5148             for(index = 0; index < count; index++)
5149                 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5150             return 0;
5151         }
5152
5153         cData->chOpts[option] = charOptions[option].values[index].value;
5154     }
5155     else
5156     {
5157         /* Find current option value. */
5158       find_value:
5159         for(index = 0;
5160             (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5161             index++);
5162         if(index == count)
5163         {
5164             /* Somehow, the option value is corrupt; reset it to the default. */
5165             cData->chOpts[option] = charOptions[option].default_value;
5166             goto find_value;
5167         }
5168     }
5169
5170     reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5171     return 1;
5172 }
5173
5174 static MODCMD_FUNC(chan_opt_protect)
5175 {
5176     return channel_multiple_option(chProtect, CSFUNC_ARGS);
5177 }
5178
5179 static MODCMD_FUNC(chan_opt_toys)
5180 {
5181     return channel_multiple_option(chToys, CSFUNC_ARGS);
5182 }
5183
5184 static MODCMD_FUNC(chan_opt_ctcpreaction)
5185 {
5186     return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5187 }
5188
5189 static MODCMD_FUNC(chan_opt_topicrefresh)
5190 {
5191     return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5192 }
5193
5194 static struct svccmd_list set_shows_list;
5195
5196 static void
5197 handle_svccmd_unbind(struct svccmd *target) {
5198     unsigned int ii;
5199     for(ii=0; ii<set_shows_list.used; ++ii)
5200         if(target == set_shows_list.list[ii])
5201             set_shows_list.used = 0;
5202 }
5203
5204 static CHANSERV_FUNC(cmd_set)
5205 {
5206     struct svccmd *subcmd;
5207     char buf[MAXLEN];
5208     unsigned int ii;
5209
5210     /* Check if we need to (re-)initialize set_shows_list. */
5211     if(!set_shows_list.used)
5212     {
5213         if(!set_shows_list.size)
5214         {
5215             set_shows_list.size = chanserv_conf.set_shows->used;
5216             set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5217         }
5218         for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5219         {
5220             const char *name = chanserv_conf.set_shows->list[ii];
5221             sprintf(buf, "%s %s", argv[0], name);
5222             subcmd = dict_find(cmd->parent->commands, buf, NULL);
5223             if(!subcmd)
5224             {
5225                 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5226                 continue;
5227             }
5228             svccmd_list_append(&set_shows_list, subcmd);
5229         }
5230     }
5231
5232     if(argc < 2)
5233     {
5234         reply("CSMSG_CHANNEL_OPTIONS");
5235         for(ii = 0; ii < set_shows_list.used; ii++)
5236         {
5237             subcmd = set_shows_list.list[ii];
5238             subcmd->command->func(user, channel, 1, argv+1, subcmd);
5239         }
5240         return 1;
5241     }
5242
5243     sprintf(buf, "%s %s", argv[0], argv[1]);
5244     subcmd = dict_find(cmd->parent->commands, buf, NULL);
5245     if(!subcmd)
5246     {
5247         reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5248         return 0;
5249     }
5250     if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5251     {
5252         reply("CSMSG_NO_ACCESS");
5253         return 0;
5254     }
5255
5256     return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5257 }
5258
5259 static int
5260 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5261 {
5262     struct userData *uData;
5263
5264     uData = GetChannelAccess(channel->channel_info, user->handle_info);
5265     if(!uData)
5266     {
5267         reply("CSMSG_NOT_USER", channel->name);
5268         return 0;
5269     }
5270
5271     if(argc < 2)
5272     {
5273         /* Just show current option value. */
5274     }
5275     else if(enabled_string(argv[1]))
5276     {
5277         uData->flags |= mask;
5278     }
5279     else if(disabled_string(argv[1]))
5280     {
5281         uData->flags &= ~mask;
5282     }
5283     else
5284     {
5285         reply("MSG_INVALID_BINARY", argv[1]);
5286         return 0;
5287     }
5288
5289     reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5290     return 1;
5291 }
5292
5293 static MODCMD_FUNC(user_opt_noautoop)
5294 {
5295     struct userData *uData;
5296
5297     uData = GetChannelAccess(channel->channel_info, user->handle_info);
5298     if(!uData)
5299     {
5300         reply("CSMSG_NOT_USER", channel->name);
5301         return 0;
5302     }
5303     if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5304         return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5305     else
5306         return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5307 }
5308
5309 static MODCMD_FUNC(user_opt_autoinvite)
5310 {
5311     return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5312 }
5313
5314 static MODCMD_FUNC(user_opt_info)
5315 {
5316     struct userData *uData;
5317     char *infoline;
5318
5319     uData = GetChannelAccess(channel->channel_info, user->handle_info);
5320
5321     if(!uData)
5322     {
5323         /* If they got past the command restrictions (which require access)
5324          * but fail this test, we have some fool with security override on.
5325          */
5326         reply("CSMSG_NOT_USER", channel->name);
5327         return 0;
5328     }
5329
5330     if(argc > 1)
5331     {
5332         size_t bp;
5333         infoline = unsplit_string(argv + 1, argc - 1, NULL);
5334         if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5335         {
5336             reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5337             return 0;
5338         }
5339         bp = strcspn(infoline, "\001");
5340         if(infoline[bp])
5341         {
5342             reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5343             return 0;
5344         }
5345         if(uData->info)
5346             free(uData->info);
5347         if(infoline[0] == '*' && infoline[1] == 0)
5348             uData->info = NULL;
5349         else
5350             uData->info = strdup(infoline);
5351     }
5352     if(uData->info)
5353         reply("CSMSG_USET_INFO", uData->info);
5354     else
5355         reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5356     return 1;
5357 }
5358
5359 struct svccmd_list uset_shows_list;
5360
5361 static CHANSERV_FUNC(cmd_uset)
5362 {
5363     struct svccmd *subcmd;
5364     char buf[MAXLEN];
5365     unsigned int ii;
5366
5367     /* Check if we need to (re-)initialize uset_shows_list. */
5368     if(!uset_shows_list.used)
5369     {
5370         char *options[] =
5371         {
5372             "NoAutoOp", "AutoInvite", "Info"
5373         };
5374
5375         if(!uset_shows_list.size)
5376         {
5377             uset_shows_list.size = ArrayLength(options);
5378             uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5379         }
5380         for(ii = 0; ii < ArrayLength(options); ii++)
5381         {
5382             const char *name = options[ii];
5383             sprintf(buf, "%s %s", argv[0], name);
5384             subcmd = dict_find(cmd->parent->commands, buf, NULL);
5385             if(!subcmd)
5386             {
5387                 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5388                 continue;
5389             }
5390             svccmd_list_append(&uset_shows_list, subcmd);
5391         }
5392     }
5393
5394     if(argc < 2)
5395     {
5396         /* Do this so options are presented in a consistent order. */
5397         reply("CSMSG_USER_OPTIONS");
5398         for(ii = 0; ii < uset_shows_list.used; ii++)
5399             uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5400         return 1;
5401     }
5402
5403     sprintf(buf, "%s %s", argv[0], argv[1]);
5404     subcmd = dict_find(cmd->parent->commands, buf, NULL);
5405     if(!subcmd)
5406     {
5407         reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5408         return 0;
5409     }
5410
5411     return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5412 }
5413
5414 static CHANSERV_FUNC(cmd_giveownership)
5415 {
5416     struct handle_info *new_owner_hi;
5417     struct userData *new_owner, *curr_user;
5418     struct chanData *cData = channel->channel_info;
5419     struct do_not_register *dnr;
5420     unsigned int force;
5421     unsigned short co_access;
5422     char reason[MAXLEN];
5423
5424     REQUIRE_PARAMS(2);
5425     curr_user = GetChannelAccess(cData, user->handle_info);
5426     force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5427     if(!curr_user || (curr_user->access != UL_OWNER))
5428     {
5429         struct userData *owner = NULL;
5430         for(curr_user = channel->channel_info->users;
5431             curr_user;
5432             curr_user = curr_user->next)
5433         {
5434             if(curr_user->access != UL_OWNER)
5435                 continue;
5436             if(owner)
5437             {
5438                 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5439                 return 0;
5440             }
5441             owner = curr_user;
5442         }
5443         curr_user = owner;
5444     }
5445     if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5446         return 0;
5447     if(new_owner_hi == user->handle_info)
5448     {
5449         reply("CSMSG_NO_TRANSFER_SELF");
5450         return 0;
5451     }
5452     new_owner = GetChannelAccess(cData, new_owner_hi);
5453     if(!new_owner)
5454     {
5455         reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5456         return 0;
5457     }
5458     if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5459     {
5460         reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5461         return 0;
5462     }
5463     if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5464         if(!IsHelping(user))
5465             reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5466         else
5467             chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5468         return 0;
5469     }
5470     if(new_owner->access >= UL_COOWNER)
5471         co_access = new_owner->access;
5472     else
5473         co_access = UL_COOWNER;
5474     new_owner->access = UL_OWNER;
5475     if(curr_user)
5476         curr_user->access = co_access;
5477     reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5478     sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5479     global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5480     return 1;
5481 }
5482
5483 static CHANSERV_FUNC(cmd_suspend)
5484 {
5485     struct handle_info *hi;
5486     struct userData *self, *target;
5487
5488     REQUIRE_PARAMS(2);
5489     if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5490     self = GetChannelUser(channel->channel_info, user->handle_info);
5491     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5492     {
5493         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5494         return 0;
5495     }
5496     if(target->access >= self->access)
5497     {
5498         reply("MSG_USER_OUTRANKED", hi->handle);
5499         return 0;
5500     }
5501     if(target->flags & USER_SUSPENDED)
5502     {
5503         reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5504         return 0;
5505     }
5506     if(target->present)
5507     {
5508         target->present = 0;
5509         target->seen = now;
5510     }
5511     target->flags |= USER_SUSPENDED;
5512     reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5513     return 1;
5514 }
5515
5516 static CHANSERV_FUNC(cmd_unsuspend)
5517 {
5518     struct handle_info *hi;
5519     struct userData *self, *target;
5520
5521     REQUIRE_PARAMS(2);
5522     if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5523     self = GetChannelUser(channel->channel_info, user->handle_info);
5524     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5525     {
5526         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5527         return 0;
5528     }
5529     if(target->access >= self->access)
5530     {
5531         reply("MSG_USER_OUTRANKED", hi->handle);
5532         return 0;
5533     }
5534     if(!(target->flags & USER_SUSPENDED))
5535     {
5536         reply("CSMSG_NOT_SUSPENDED", hi->handle);
5537         return 0;
5538     }
5539     target->flags &= ~USER_SUSPENDED;
5540     reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5541     return 1;
5542 }
5543
5544 static MODCMD_FUNC(cmd_deleteme)
5545 {
5546     struct handle_info *hi;
5547     struct userData *target;
5548     const char *confirm_string;
5549     unsigned short access;
5550     char *channel_name;
5551
5552     hi = user->handle_info;
5553     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5554     {
5555         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5556         return 0;
5557     }
5558     if(target->access == UL_OWNER)
5559     {
5560         reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5561         return 0;
5562     }
5563     confirm_string = make_confirmation_string(target);
5564     if((argc < 2) || strcmp(argv[1], confirm_string))
5565     {
5566         reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5567         return 0;
5568     }
5569     access = target->access;
5570     channel_name = strdup(channel->name);
5571     del_channel_user(target, 1);
5572     reply("CSMSG_DELETED_YOU", access, channel_name);
5573     free(channel_name);
5574     return 1;
5575 }
5576
5577 static void
5578 chanserv_refresh_topics(UNUSED_ARG(void *data))
5579 {
5580     unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5581     struct chanData *cData;
5582     char opt;
5583
5584     for(cData = channelList; cData; cData = cData->next)
5585     {
5586         if(IsSuspended(cData))
5587             continue;
5588         opt = cData->chOpts[chTopicRefresh];
5589         if(opt == 'n')
5590             continue;
5591         if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5592             continue;
5593         if(cData->topic)
5594             SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5595         cData->last_refresh = refresh_num;
5596     }
5597     timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5598 }
5599
5600 static CHANSERV_FUNC(cmd_unf)
5601 {
5602     if(channel)
5603     {
5604         char response[MAXLEN];
5605         const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5606         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
5607         irc_privmsg(cmd->parent->bot, channel->name, response);
5608     }
5609     else
5610         reply("CSMSG_UNF_RESPONSE");
5611     return 1;
5612 }
5613
5614 static CHANSERV_FUNC(cmd_ping)
5615 {
5616     if(channel)
5617     {
5618         char response[MAXLEN];
5619         const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5620         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
5621         irc_privmsg(cmd->parent->bot, channel->name, response);
5622     }
5623     else
5624         reply("CSMSG_PING_RESPONSE");
5625     return 1;
5626 }
5627
5628 static CHANSERV_FUNC(cmd_wut)
5629 {
5630     if(channel)
5631     {
5632         char response[MAXLEN];
5633         const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5634         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
5635         irc_privmsg(cmd->parent->bot, channel->name, response);
5636     }
5637     else
5638         reply("CSMSG_WUT_RESPONSE");
5639     return 1;
5640 }
5641
5642 static CHANSERV_FUNC(cmd_8ball)
5643 {
5644     unsigned int i, j, accum;
5645     const char *resp;
5646
5647     REQUIRE_PARAMS(2);
5648     accum = 0;
5649     for(i=1; i<argc; i++)
5650         for(j=0; argv[i][j]; j++)
5651             accum = (accum << 5) - accum + toupper(argv[i][j]);
5652     resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5653     if(channel)
5654     {
5655         char response[MAXLEN];
5656         sprintf(response, "\ 2%s\ 2: %s", user->nick, resp);
5657         irc_privmsg(cmd->parent->bot, channel->name, response);
5658     }
5659     else
5660         send_message_type(4, user, cmd->parent->bot, "%s", resp);
5661     return 1;
5662 }
5663
5664 static CHANSERV_FUNC(cmd_d)
5665 {
5666     unsigned long sides, count, modifier, ii, total;
5667     char response[MAXLEN], *sep;
5668     const char *fmt;
5669
5670     REQUIRE_PARAMS(2);
5671     if((count = strtoul(argv[1], &sep, 10)) < 1)
5672         goto no_dice;
5673     if(sep[0] == 0)
5674     {
5675         if(count == 1)
5676             goto no_dice;
5677         sides = count;
5678         count = 1;
5679         modifier = 0;
5680     }
5681     else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5682             && (sides = strtoul(sep+1, &sep, 10)) > 1)
5683     {
5684         if(sep[0] == 0)
5685             modifier = 0;
5686         else if((sep[0] == '-') && isdigit(sep[1]))
5687             modifier = strtoul(sep, NULL, 10);
5688         else if((sep[0] == '+') && isdigit(sep[1]))
5689             modifier = strtoul(sep+1, NULL, 10);
5690         else
5691             goto no_dice;
5692     }
5693     else
5694     {
5695       no_dice:
5696         reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5697         return 0;
5698     }
5699     if(count > 10)
5700     {
5701         reply("CSMSG_BAD_DICE_COUNT", count, 10);
5702         return 0;
5703     }
5704     for(total = ii = 0; ii < count; ++ii)
5705         total += (rand() % sides) + 1;
5706     total += modifier;
5707
5708     if((count > 1) || modifier)
5709     {
5710         fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5711         sprintf(response, fmt, total, count, sides, modifier);
5712     }
5713     else
5714     {
5715         fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5716         sprintf(response, fmt, total, sides);
5717     }
5718     if(channel)
5719         send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5720     else
5721         send_message_type(4, user, cmd->parent->bot, "%s", response);
5722     return 1;
5723 }
5724
5725 static CHANSERV_FUNC(cmd_huggle)
5726 {
5727     /* CTCP must be via PRIVMSG, never notice */
5728     if(channel)
5729         send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5730     else
5731         send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5732     return 1;
5733 }
5734
5735 static void
5736 chanserv_adjust_limit(void *data)
5737 {
5738     struct mod_chanmode change;
5739     struct chanData *cData = data;
5740     struct chanNode *channel = cData->channel;
5741     unsigned int limit;
5742
5743     if(IsSuspended(cData))
5744         return;
5745
5746     cData->limitAdjusted = now;
5747     limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5748     if(cData->modes.modes_set & MODE_LIMIT)
5749     {
5750         if(limit > cData->modes.new_limit)
5751             limit = cData->modes.new_limit;
5752         else if(limit == cData->modes.new_limit)
5753             return;
5754     }
5755
5756     mod_chanmode_init(&change);
5757     change.modes_set = MODE_LIMIT;
5758     change.new_limit = limit;
5759     mod_chanmode_announce(chanserv, channel, &change);
5760 }
5761
5762 static void
5763 handle_new_channel(struct chanNode *channel)
5764 {
5765     struct chanData *cData;
5766
5767     if(!(cData = channel->channel_info))
5768         return;
5769
5770     if(cData->modes.modes_set || cData->modes.modes_clear)
5771         mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5772
5773     if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5774         SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5775 }
5776
5777 /* Welcome to my worst nightmare. Warning: Read (or modify)
5778    the code below at your own risk. */
5779 static int
5780 handle_join(struct modeNode *mNode)
5781 {
5782     struct mod_chanmode change;
5783     struct userNode *user = mNode->user;
5784     struct chanNode *channel = mNode->channel;
5785     struct chanData *cData;
5786     struct userData *uData = NULL;
5787     struct banData *bData;
5788     struct handle_info *handle;
5789     unsigned int modes = 0, info = 0;
5790     char *greeting;
5791
5792     if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5793         return 0;
5794
5795     cData = channel->channel_info;
5796     if(channel->members.used > cData->max)
5797         cData->max = channel->members.used;
5798
5799     /* Check for bans.  If they're joining through a ban, one of two
5800      * cases applies:
5801      *   1: Join during a netburst, by riding the break.  Kick them
5802      *      unless they have ops or voice in the channel.
5803      *   2: They're allowed to join through the ban (an invite in
5804      *   ircu2.10, or a +e on Hybrid, or something).
5805      * If they're not joining through a ban, and the banlist is not
5806      * full, see if they're on the banlist for the channel.  If so,
5807      * kickban them.
5808      */
5809     if(user->uplink->burst && !mNode->modes)
5810     {
5811         unsigned int ii;
5812         for(ii = 0; ii < channel->banlist.used; ii++)
5813         {
5814             if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5815             {
5816                 /* Riding a netburst.  Naughty. */
5817                 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5818                 return 1;
5819             }
5820         }
5821     }
5822
5823     mod_chanmode_init(&change);
5824     change.argc = 1;
5825     if(channel->banlist.used < MAXBANS)
5826     {
5827         /* Not joining through a ban. */
5828         for(bData = cData->bans;
5829                 bData && !user_matches_glob(user, bData->mask, 1);
5830                 bData = bData->next);
5831
5832         if(bData)
5833         {
5834             char kick_reason[MAXLEN];
5835             sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5836
5837             bData->triggered = now;
5838             if(bData != cData->bans)
5839             {
5840                 /* Shuffle the ban to the head of the list. */
5841                 if(bData->next)
5842                     bData->next->prev = bData->prev;
5843                 if(bData->prev)
5844                     bData->prev->next = bData->next;
5845
5846                 bData->prev = NULL;
5847                 bData->next = cData->bans;
5848
5849                 if(cData->bans)
5850                     cData->bans->prev = bData;
5851                 cData->bans = bData;
5852             }
5853
5854             change.args[0].mode = MODE_BAN;
5855             change.args[0].hostmask = bData->mask;
5856             mod_chanmode_announce(chanserv, channel, &change);
5857             KickChannelUser(user, channel, chanserv, kick_reason);
5858             return 1;
5859         }
5860     }
5861
5862     /* ChanServ will not modify the limits in join-flooded channels.
5863        It will also skip DynLimit processing when the user (or srvx)
5864        is bursting in, because there are likely more incoming. */
5865     if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5866        && !user->uplink->burst
5867        && !channel->join_flooded
5868        && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5869     {
5870         /* The user count has begun "bumping" into the channel limit,
5871            so set a timer to raise the limit a bit. Any previous
5872            timers are removed so three incoming users within the delay
5873            results in one limit change, not three. */
5874
5875         timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5876         timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5877     }
5878
5879     if(channel->join_flooded)
5880     {
5881         /* don't automatically give ops or voice during a join flood */
5882     }
5883     else if(cData->lvlOpts[lvlGiveOps] == 0)
5884         modes |= MODE_CHANOP;
5885     else if(cData->lvlOpts[lvlGiveVoice] == 0)
5886         modes |= MODE_VOICE;
5887
5888     greeting = cData->greeting;
5889     if(user->handle_info)
5890     {
5891         handle = user->handle_info;
5892
5893         if(IsHelper(user) && !IsHelping(user))
5894         {
5895             unsigned int ii;
5896             for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5897             {
5898                 if(channel == chanserv_conf.support_channels.list[ii])
5899                 {
5900                     HANDLE_SET_FLAG(user->handle_info, HELPING);
5901                     break;
5902                 }
5903             }
5904         }
5905
5906         uData = GetTrueChannelAccess(cData, handle);
5907         if(uData && !IsUserSuspended(uData))
5908         {
5909             /* Ops and above were handled by the above case. */
5910             if(IsUserAutoOp(uData))
5911             {
5912                 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5913                     modes |= MODE_CHANOP;
5914                 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5915                     modes |= MODE_VOICE;
5916             }
5917             if(uData->access >= UL_PRESENT)
5918                 cData->visited = now;
5919             if(cData->user_greeting)
5920                 greeting = cData->user_greeting;
5921             if(uData->info
5922                && (uData->access >= cData->lvlOpts[lvlUserInfo])
5923                && ((now - uData->seen) >= chanserv_conf.info_delay)
5924                && !uData->present)
5925                 info = 1;
5926             uData->seen = now;
5927             uData->present = 1;
5928         }
5929     }
5930     if(!user->uplink->burst)
5931     {
5932         if(modes)
5933         {
5934             if(modes & MODE_CHANOP)
5935                 modes &= ~MODE_VOICE;
5936             change.args[0].mode = modes;
5937             change.args[0].member = mNode;
5938             mod_chanmode_announce(chanserv, channel, &change);
5939         }
5940         if(greeting && !user->uplink->burst)
5941             send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5942         if(uData && info)
5943             send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5944     }
5945     return 0;
5946 }
5947
5948 static void
5949 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5950 {
5951     struct mod_chanmode change;
5952     struct userData *channel;
5953     unsigned int ii, jj;
5954
5955     if(!user->handle_info)
5956         return;
5957
5958     mod_chanmode_init(&change);
5959     change.argc = 1;
5960     for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5961     {
5962         struct chanNode *cn;
5963         struct modeNode *mn;
5964         if(IsUserSuspended(channel)
5965            || IsSuspended(channel->channel)
5966            || !(cn = channel->channel->channel))
5967             continue;
5968
5969         mn = GetUserMode(cn, user);
5970         if(!mn)
5971         {
5972             if(!IsUserSuspended(channel)
5973                && IsUserAutoInvite(channel)
5974                && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
5975                && !self->burst
5976                && !user->uplink->burst)
5977                 irc_invite(chanserv, user, cn);
5978             continue;
5979         }
5980
5981         if(channel->access >= UL_PRESENT)
5982             channel->channel->visited = now;
5983
5984         if(IsUserAutoOp(channel))
5985         {
5986             if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5987                 change.args[0].mode = MODE_CHANOP;
5988             else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
5989                 change.args[0].mode = MODE_VOICE;
5990             else
5991                 change.args[0].mode = 0;
5992             change.args[0].member = mn;
5993             if(change.args[0].mode)
5994                 mod_chanmode_announce(chanserv, cn, &change);
5995         }
5996
5997         channel->seen = now;
5998         channel->present = 1;
5999     }
6000
6001     for(ii = 0; ii < user->channels.used; ++ii)
6002     {
6003         struct chanNode *channel = user->channels.list[ii]->channel;
6004         struct banData *ban;
6005
6006         if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6007            || !channel->channel_info)
6008             continue;
6009         for(jj = 0; jj < channel->banlist.used; ++jj)
6010             if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6011                 break;
6012         if(jj < channel->banlist.used)
6013             continue;
6014         for(ban = channel->channel_info->bans; ban; ban = ban->next)
6015         {
6016             char kick_reason[MAXLEN];
6017             if(!user_matches_glob(user, ban->mask, 1))
6018                 continue;
6019             change.args[0].mode = MODE_BAN;
6020             change.args[0].hostmask = ban->mask;
6021             mod_chanmode_announce(chanserv, channel, &change);
6022             sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6023             KickChannelUser(user, channel, chanserv, kick_reason);
6024             ban->triggered = now;
6025             break;
6026         }
6027     }
6028
6029     if(IsSupportHelper(user))
6030     {
6031         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6032         {
6033             if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6034             {
6035                 HANDLE_SET_FLAG(user->handle_info, HELPING);
6036                 break;
6037             }
6038         }
6039     }
6040 }
6041
6042 static void
6043 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6044 {
6045     struct chanData *cData;
6046     struct userData *uData;
6047
6048     cData = mn->channel->channel_info;
6049     if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6050         return;
6051
6052     if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6053     {
6054         /* Allow for a bit of padding so that the limit doesn't
6055            track the user count exactly, which could get annoying. */
6056         if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6057         {
6058             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6059             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6060         }
6061     }
6062
6063     if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6064     {
6065         scan_user_presence(uData, mn->user);
6066         uData->seen = now;
6067     }
6068
6069     if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6070     {
6071         unsigned int ii, jj;
6072         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6073         {
6074             for(jj = 0; jj < mn->user->channels.used; ++jj)
6075                 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6076                     break;
6077             if(jj < mn->user->channels.used)
6078                 break;
6079         }
6080         if(ii == chanserv_conf.support_channels.used)
6081             HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6082     }
6083 }
6084
6085 static void
6086 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6087 {
6088     struct userData *uData;
6089
6090     if(!channel->channel_info || !kicker || IsService(kicker)
6091        || (kicker == victim) || IsSuspended(channel->channel_info)
6092        || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6093         return;
6094
6095     if(protect_user(victim, kicker, channel->channel_info))
6096     {
6097         const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6098         KickChannelUser(kicker, channel, chanserv, reason);
6099     }
6100
6101     if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6102         uData->seen = now;
6103 }
6104
6105 static int
6106 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6107 {
6108     struct chanData *cData;
6109
6110     if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6111         return 0;
6112
6113     cData = channel->channel_info;
6114     if(bad_topic(channel, user, channel->topic))
6115     {
6116         send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6117         if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6118             SetChannelTopic(channel, chanserv, old_topic, 1);
6119         else if(cData->topic)
6120             SetChannelTopic(channel, chanserv, cData->topic, 1);
6121         return 1;
6122     }
6123     /* With topicsnarf, grab the topic and save it as the default topic. */
6124     if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6125     {
6126         free(cData->topic);
6127         cData->topic = strdup(channel->topic);
6128     }
6129     return 0;
6130 }
6131
6132 static void
6133 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6134 {
6135     struct mod_chanmode *bounce = NULL;
6136     unsigned int bnc, ii;
6137     char deopped = 0;
6138
6139     if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6140         return;
6141
6142     if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6143        && mode_lock_violated(&channel->channel_info->modes, change))
6144     {
6145         char correct[MAXLEN];
6146         bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6147         mod_chanmode_format(&channel->channel_info->modes, correct);
6148         send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6149     }
6150     for(ii = bnc = 0; ii < change->argc; ++ii)
6151     {
6152         if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6153         {
6154             const struct userNode *victim = change->args[ii].member->user;
6155             if(!protect_user(victim, user, channel->channel_info))
6156                 continue;
6157             if(!bounce)
6158                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6159             if(!deopped)
6160             {
6161                 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6162                 bounce->args[bnc].member = GetUserMode(channel, user);
6163                 if(bounce->args[bnc].member)
6164                     bnc++;
6165                 deopped = 1;
6166             }
6167             bounce->args[bnc].mode = MODE_CHANOP;
6168             bounce->args[bnc].member = change->args[ii].member;
6169             bnc++;
6170             send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6171         }
6172         else if(change->args[ii].mode & MODE_CHANOP)
6173         {
6174             const struct userNode *victim = change->args[ii].member->user;
6175             if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6176                 continue;
6177             if(!bounce)
6178                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6179             bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6180             bounce->args[bnc].member = change->args[ii].member;
6181             bnc++;
6182         }
6183         else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6184         {
6185             const char *ban = change->args[ii].hostmask;
6186             if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6187                 continue;
6188             if(!bounce)
6189                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6190             bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6191             bounce->args[bnc].hostmask = ban;
6192             bnc++;
6193             send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6194         }
6195     }
6196     if(bounce)
6197     {
6198         if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6199             mod_chanmode_announce(chanserv, channel, bounce);
6200         mod_chanmode_free(bounce);
6201     }
6202 }
6203
6204 static void
6205 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6206 {
6207     struct chanNode *channel;
6208     struct banData *bData;
6209     struct mod_chanmode change;
6210     unsigned int ii, jj;
6211     char kick_reason[MAXLEN];
6212
6213     mod_chanmode_init(&change);
6214     change.argc = 1;
6215     change.args[0].mode = MODE_BAN;
6216     for(ii = 0; ii < user->channels.used; ++ii)
6217     {
6218         channel = user->channels.list[ii]->channel;
6219         /* Need not check for bans if they're opped or voiced. */
6220         if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6221             continue;
6222         /* Need not check for bans unless channel registration is active. */
6223         if(!channel->channel_info || IsSuspended(channel->channel_info))
6224             continue;
6225         /* Look for a matching ban already on the channel. */
6226         for(jj = 0; jj < channel->banlist.used; ++jj)
6227             if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6228                 break;
6229         /* Need not act if we found one. */
6230         if(jj < channel->banlist.used)
6231             continue;
6232         /* Look for a matching ban in this channel. */
6233         for(bData = channel->channel_info->bans; bData; bData = bData->next)
6234         {
6235             if(!user_matches_glob(user, bData->mask, 1))
6236                 continue;
6237             change.args[0].hostmask = bData->mask;
6238             mod_chanmode_announce(chanserv, channel, &change);
6239             sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6240             KickChannelUser(user, channel, chanserv, kick_reason);
6241             bData->triggered = now;
6242             break; /* we don't need to check any more bans in the channel */
6243         }
6244     }
6245 }
6246
6247 static void handle_rename(struct handle_info *handle, const char *old_handle)
6248 {
6249     struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6250
6251     if(dnr)
6252     {
6253         dict_remove2(handle_dnrs, old_handle, 1);
6254         safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6255         dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6256     }
6257 }
6258
6259 static void
6260 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6261 {
6262     struct userNode *h_user;
6263
6264     if(handle->channels)
6265     {
6266         for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6267             send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6268
6269         while(handle->channels)
6270             del_channel_user(handle->channels, 1);
6271     }
6272 }
6273
6274 static void
6275 handle_server_link(UNUSED_ARG(struct server *server))
6276 {
6277     struct chanData *cData;
6278
6279     for(cData = channelList; cData; cData = cData->next)
6280     {
6281         if(!IsSuspended(cData))
6282             cData->may_opchan = 1;
6283         if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6284            && !cData->channel->join_flooded
6285            && ((cData->channel->limit - cData->channel->members.used)
6286                < chanserv_conf.adjust_threshold))
6287         {
6288             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6289             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6290         }
6291     }
6292 }
6293
6294 static void
6295 chanserv_conf_read(void)
6296 {
6297     dict_t conf_node;
6298     const char *str;
6299     char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6300     struct mod_chanmode *change;
6301     struct string_list *strlist;
6302     struct chanNode *chan;
6303     unsigned int ii;
6304
6305     if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6306     {
6307         log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6308         return;
6309     }
6310     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6311         UnlockChannel(chanserv_conf.support_channels.list[ii]);
6312     chanserv_conf.support_channels.used = 0;
6313     if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6314     {
6315         for(ii = 0; ii < strlist->used; ++ii)
6316         {
6317             const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6318             if(!str2)
6319                 str2 = "+nt";
6320             chan = AddChannel(strlist->list[ii], now, str2, NULL);
6321             LockChannel(chan);
6322             channelList_append(&chanserv_conf.support_channels, chan);
6323         }
6324     }
6325     else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6326     {
6327         const char *str2;
6328         str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6329         if(!str2)
6330             str2 = "+nt";
6331         chan = AddChannel(str, now, str2, NULL);
6332         LockChannel(chan);
6333         channelList_append(&chanserv_conf.support_channels, chan);
6334     }
6335     str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6336     chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6337     str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6338     chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6339     str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6340     chanserv_conf.greeting_length = str ? atoi(str) : 120;
6341     str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6342     chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6343     str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6344     chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6345     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6346     chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6347     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6348     chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6349     str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6350     chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6351     str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6352     chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6353     str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6354     chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6355     str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6356     chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6357     str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6358     if(chanserv && str)
6359         NickChange(chanserv, str, 0);
6360     str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6361     chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6362     str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6363     chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6364     str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6365     chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6366     str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6367     chanserv_conf.max_owned = str ? atoi(str) : 5;
6368     str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6369     chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6370     str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6371     chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6372     str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6373     chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6374     str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6375     if(!str)
6376         str = "+nt";
6377     safestrncpy(mode_line, str, sizeof(mode_line));
6378     ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6379     if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6380     {
6381         chanserv_conf.default_modes = *change;
6382         mod_chanmode_free(change);
6383     }
6384     free_string_list(chanserv_conf.set_shows);
6385     strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6386     if(strlist)
6387         strlist = string_list_copy(strlist);
6388     else
6389     {
6390         static const char *list[] = {
6391             /* free form text */
6392             "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6393             /* options based on user level */
6394             "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6395             "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6396             /* multiple choice options */
6397             "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6398             /* binary options */
6399             "DynLimit", "NoDelete",
6400             /* delimiter */
6401             NULL
6402         };
6403         unsigned int ii;
6404         strlist = alloc_string_list(ArrayLength(list)-1);
6405         for(ii=0; list[ii]; ii++)
6406             string_list_append(strlist, strdup(list[ii]));
6407     }
6408     chanserv_conf.set_shows = strlist;
6409     /* We don't look things up now, in case the list refers to options
6410      * defined by modules initialized after this point.  Just mark the
6411      * function list as invalid, so it will be initialized.
6412      */
6413     set_shows_list.used = 0;
6414     free_string_list(chanserv_conf.eightball);
6415     strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6416     if(strlist)
6417     {
6418         strlist = string_list_copy(strlist);
6419     }
6420     else
6421     {
6422         strlist = alloc_string_list(4);
6423         string_list_append(strlist, strdup("Yes."));
6424         string_list_append(strlist, strdup("No."));
6425         string_list_append(strlist, strdup("Maybe so."));
6426     }
6427     chanserv_conf.eightball = strlist;
6428     free_string_list(chanserv_conf.old_ban_names);
6429     strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6430     if(strlist)
6431         strlist = string_list_copy(strlist);
6432     else
6433         strlist = alloc_string_list(2);
6434     chanserv_conf.old_ban_names = strlist;
6435     /* the variable itself is actually declared in proto-common.c; this is equally 
6436      * parse issue. */
6437     str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6438     off_channel = (str && enabled_string(str)) ? 1 : 0;
6439 }
6440
6441 static void
6442 chanserv_note_type_read(const char *key, struct record_data *rd)
6443 {
6444     dict_t obj;
6445     struct note_type *ntype;
6446     const char *str;
6447
6448     if(!(obj = GET_RECORD_OBJECT(rd)))
6449     {
6450         log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6451         return;
6452     }
6453     if(!(ntype = chanserv_create_note_type(key)))
6454     {
6455         log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6456         return;
6457     }
6458
6459     /* Figure out set access */
6460     if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6461     {
6462         ntype->set_access_type = NOTE_SET_PRIVILEGED;
6463         ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6464     }
6465     else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6466     {
6467         ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6468         ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6469     }
6470     else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6471     {
6472         ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6473     }
6474     else
6475     {
6476         log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6477         ntype->set_access_type = NOTE_SET_PRIVILEGED;
6478         ntype->set_access.min_opserv = 0;
6479     }
6480
6481     /* Figure out visibility */
6482     if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6483         ntype->visible_type = NOTE_VIS_PRIVILEGED;
6484     else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6485         ntype->visible_type = NOTE_VIS_PRIVILEGED;
6486     else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6487         ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6488     else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6489         ntype->visible_type = NOTE_VIS_ALL;
6490     else
6491         ntype->visible_type = NOTE_VIS_PRIVILEGED;
6492
6493     str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6494     ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6495 }
6496
6497 static void
6498 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6499 {
6500     struct handle_info *handle;
6501     struct userData *uData;
6502     char *seen, *inf, *flags;
6503     time_t last_seen;
6504     unsigned short access;
6505
6506     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6507     {
6508         log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6509         return;
6510     }
6511
6512     access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6513     if(access > UL_OWNER)
6514     {
6515         log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6516         return;
6517     }
6518
6519     inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6520     seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6521     last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6522     flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6523     handle = get_handle_info(key);
6524     if(!handle)
6525     {
6526         log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6527         return;
6528     }
6529
6530     uData = add_channel_user(chan, handle, access, last_seen, inf);
6531     uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6532 }
6533
6534 static void
6535 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6536 {
6537     struct banData *bData;
6538     char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6539     time_t set_time, triggered_time, expires_time;
6540
6541     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6542     {
6543         log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6544         return;
6545     }
6546
6547     set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6548     triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6549     s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6550     s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6551     owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6552     reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6553
6554     set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6555     triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6556     if(s_expires)
6557         expires_time = (time_t)strtoul(s_expires, NULL, 0);
6558     else if(s_duration)
6559         expires_time = set_time + atoi(s_duration);
6560     else
6561         expires_time = 0;
6562
6563     if(expires_time && (expires_time < now))
6564         return;
6565
6566     bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6567 }
6568
6569 static struct suspended *
6570 chanserv_read_suspended(dict_t obj)
6571 {
6572     struct suspended *suspended = calloc(1, sizeof(*suspended));
6573     char *str;
6574     dict_t previous;
6575
6576     str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6577     suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6578     str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6579     suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6580     str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6581     suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6582     suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6583     suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6584     previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6585     suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6586     return suspended;
6587 }
6588
6589 static int
6590 chanserv_channel_read(const char *key, struct record_data *hir)
6591 {
6592     struct suspended *suspended;
6593     struct mod_chanmode *modes;
6594     struct chanNode *cNode;
6595     struct chanData *cData;
6596     struct dict *channel, *obj;
6597     char *str, *argv[10];
6598     dict_iterator_t it;
6599     unsigned int argc;
6600
6601     channel = hir->d.object;
6602
6603     str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6604     if(!str)
6605         str = "<unknown>";
6606     cNode = AddChannel(key, now, NULL, NULL);
6607     if(!cNode)
6608     {
6609         log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6610         return 0;
6611     }
6612     cData = register_channel(cNode, str);
6613     if(!cData)
6614     {
6615         log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6616         return 0;
6617     }
6618
6619     if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6620     {
6621         enum levelOption lvlOpt;
6622         enum charOption chOpt;
6623
6624         if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6625             cData->flags = atoi(str);
6626
6627         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6628         {
6629             str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6630             if(str)
6631                 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6632             else if(levelOptions[lvlOpt].old_flag)
6633             {
6634                 if(cData->flags & levelOptions[lvlOpt].old_flag)
6635                     cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6636                 else
6637                     cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6638             }
6639         }
6640
6641         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6642         {
6643             if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6644                 continue;
6645             cData->chOpts[chOpt] = str[0];
6646         }
6647     }
6648     else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6649     {
6650         enum levelOption lvlOpt;
6651         enum charOption chOpt;
6652         unsigned int count;
6653
6654         cData->flags = base64toint(str, 5);
6655         count = strlen(str += 5);
6656         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6657         {
6658             unsigned short lvl;
6659             if(levelOptions[lvlOpt].old_flag)
6660             {
6661                 if(cData->flags & levelOptions[lvlOpt].old_flag)
6662                     lvl = levelOptions[lvlOpt].flag_value;
6663                 else
6664                     lvl = levelOptions[lvlOpt].default_value;
6665             }
6666             else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6667             {
6668             case 'c': lvl = UL_COOWNER; break;
6669             case 'm': lvl = UL_MASTER; break;
6670             case 'n': lvl = UL_OWNER+1; break;
6671             case 'o': lvl = UL_OP; break;
6672             case 'p': lvl = UL_PEON; break;
6673             case 'w': lvl = UL_OWNER; break;
6674             default: lvl = 0; break;
6675             }
6676             cData->lvlOpts[lvlOpt] = lvl;
6677         }
6678         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6679             cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6680     }
6681    
6682     if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6683     {
6684         suspended = chanserv_read_suspended(obj);
6685         cData->suspended = suspended;
6686         suspended->cData = cData;
6687         /* We could use suspended->expires and suspended->revoked to
6688          * set the CHANNEL_SUSPENDED flag, but we don't. */
6689     }
6690     else if(IsSuspended(cData))
6691     {
6692         suspended = calloc(1, sizeof(*suspended));
6693         suspended->issued = 0;
6694         suspended->revoked = 0;
6695         str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6696         suspended->expires = str ? atoi(str) : 0;
6697         suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6698         str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6699         suspended->reason = strdup(str ? str : "No reason");
6700         suspended->previous = NULL;
6701         cData->suspended = suspended;
6702         suspended->cData = cData;
6703     }
6704     else
6705         suspended = NULL; /* to squelch a warning */
6706
6707     if(IsSuspended(cData)) {
6708         if(suspended->expires > now)
6709             timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6710         else if(suspended->expires)
6711             cData->flags &= ~CHANNEL_SUSPENDED;
6712     }
6713
6714     if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6715         struct mod_chanmode change;
6716         mod_chanmode_init(&change);
6717         change.argc = 1;
6718         change.args[0].mode = MODE_CHANOP;
6719         change.args[0].member = AddChannelUser(chanserv, cNode);
6720         mod_chanmode_announce(chanserv, cNode, &change);
6721     }
6722
6723     str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6724     cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6725     str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6726     cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6727     str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6728     cData->max = str ? atoi(str) : 0;
6729     str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6730     cData->greeting = str ? strdup(str) : NULL;
6731     str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6732     cData->user_greeting = str ? strdup(str) : NULL;
6733     str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6734     cData->topic_mask = str ? strdup(str) : NULL;
6735     str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6736     cData->topic = str ? strdup(str) : NULL;
6737
6738     if(!IsSuspended(cData)
6739        && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6740        && (argc = split_line(str, 0, ArrayLength(argv), argv))
6741        && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6742         cData->modes = *modes;
6743         cData->modes.modes_set |= MODE_REGISTERED;
6744         if(cData->modes.argc > 1)
6745             cData->modes.argc = 1;
6746         mod_chanmode_announce(chanserv, cNode, &cData->modes);
6747         mod_chanmode_free(modes);
6748     }
6749
6750     obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6751     for(it = dict_first(obj); it; it = iter_next(it))
6752         user_read_helper(iter_key(it), iter_data(it), cData);
6753
6754     if(!cData->users && !IsProtected(cData))
6755     {
6756         log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6757         unregister_channel(cData, "has empty user list.");
6758         return 0;
6759     }
6760
6761     obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6762     for(it = dict_first(obj); it; it = iter_next(it))
6763         ban_read_helper(iter_key(it), iter_data(it), cData);
6764
6765     obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6766     for(it = dict_first(obj); it; it = iter_next(it))
6767     {
6768         struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6769         struct record_data *rd = iter_data(it);
6770         const char *note, *setter;
6771
6772         if(rd->type != RECDB_OBJECT)
6773         {
6774             log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6775         }
6776         else if(!ntype)
6777         {
6778             log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6779         }
6780         else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6781         {
6782             log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6783         }
6784         else
6785         {
6786             setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6787             if(!setter) setter = "<unknown>";
6788             chanserv_add_channel_note(cData, ntype, setter, note);
6789         }
6790     }
6791
6792     return 0;
6793 }
6794
6795 static void
6796 chanserv_dnr_read(const char *key, struct record_data *hir)
6797 {
6798     const char *setter, *reason, *str;
6799     struct do_not_register *dnr;
6800
6801     setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6802     if(!setter)
6803     {
6804         log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6805         return;
6806     }
6807     reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6808     if(!reason)
6809     {
6810         log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6811         return;
6812     }
6813     dnr = chanserv_add_dnr(key, setter, reason);
6814     if(!dnr)
6815         return;
6816     str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6817     if(str)
6818         dnr->set = atoi(str);
6819     else
6820         dnr->set = 0;
6821 }
6822
6823 static int
6824 chanserv_saxdb_read(struct dict *database)
6825 {
6826     struct dict *section;
6827     dict_iterator_t it;
6828
6829     if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6830         for(it = dict_first(section); it; it = iter_next(it))
6831             chanserv_note_type_read(iter_key(it), iter_data(it));
6832
6833     if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6834         for(it = dict_first(section); it; it = iter_next(it))
6835             chanserv_channel_read(iter_key(it), iter_data(it));
6836
6837     if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6838         for(it = dict_first(section); it; it = iter_next(it))
6839             chanserv_dnr_read(iter_key(it), iter_data(it));
6840
6841     return 0;
6842 }
6843
6844 static int
6845 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6846 {
6847     int high_present = 0;
6848     saxdb_start_record(ctx, KEY_USERS, 1);
6849     for(; uData; uData = uData->next)
6850     {
6851         if((uData->access >= UL_PRESENT) && uData->present)
6852             high_present = 1;
6853         saxdb_start_record(ctx, uData->handle->handle, 0);
6854         saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6855         saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6856         if(uData->flags)
6857             saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6858         if(uData->info)
6859             saxdb_write_string(ctx, KEY_INFO, uData->info);
6860         saxdb_end_record(ctx);
6861     }
6862     saxdb_end_record(ctx);
6863     return high_present;
6864 }
6865
6866 static void
6867 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6868 {
6869     if(!bData)
6870         return;
6871     saxdb_start_record(ctx, KEY_BANS, 1);
6872     for(; bData; bData = bData->next)
6873     {
6874         saxdb_start_record(ctx, bData->mask, 0);
6875         saxdb_write_int(ctx, KEY_SET, bData->set);
6876         if(bData->triggered)
6877             saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6878         if(bData->expires)
6879             saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6880         if(bData->owner[0])
6881             saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6882         if(bData->reason)
6883             saxdb_write_string(ctx, KEY_REASON, bData->reason);
6884         saxdb_end_record(ctx);
6885     }
6886     saxdb_end_record(ctx);
6887 }
6888
6889 static void
6890 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6891 {
6892     saxdb_start_record(ctx, name, 0);
6893     saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6894     saxdb_write_string(ctx, KEY_REASON, susp->reason);
6895     if(susp->issued)
6896         saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6897     if(susp->expires)
6898         saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6899     if(susp->revoked)
6900         saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6901     if(susp->previous)
6902         chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6903     saxdb_end_record(ctx);
6904 }
6905
6906 static void
6907 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6908 {
6909     char buf[MAXLEN];
6910     int high_present;
6911     enum levelOption lvlOpt;
6912     enum charOption chOpt;
6913
6914     saxdb_start_record(ctx, channel->channel->name, 1);
6915
6916     saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6917     saxdb_write_int(ctx, KEY_MAX, channel->max);
6918     if(channel->topic)
6919         saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6920     if(channel->registrar)
6921         saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6922     if(channel->greeting)
6923         saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6924     if(channel->user_greeting)
6925         saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6926     if(channel->topic_mask)
6927         saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6928     if(channel->suspended)
6929         chanserv_write_suspended(ctx, "suspended", channel->suspended);
6930
6931     saxdb_start_record(ctx, KEY_OPTIONS, 0);
6932     saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6933     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6934         saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6935     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6936     {
6937         buf[0] = channel->chOpts[chOpt];
6938         buf[1] = '\0';
6939         saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6940     }
6941     saxdb_end_record(ctx);
6942
6943     if(channel->modes.modes_set || channel->modes.modes_clear)
6944     {
6945         mod_chanmode_format(&channel->modes, buf);
6946         saxdb_write_string(ctx, KEY_MODES, buf);
6947     }
6948
6949     high_present = chanserv_write_users(ctx, channel->users);
6950     chanserv_write_bans(ctx, channel->bans);
6951
6952     if(dict_size(channel->notes))
6953     {
6954         dict_iterator_t it;
6955
6956         saxdb_start_record(ctx, KEY_NOTES, 1);
6957         for(it = dict_first(channel->notes); it; it = iter_next(it))
6958         {
6959             struct note *note = iter_data(it);
6960             saxdb_start_record(ctx, iter_key(it), 0);
6961             saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6962             saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6963             saxdb_end_record(ctx);
6964         }
6965         saxdb_end_record(ctx);
6966     }
6967
6968     saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6969     saxdb_end_record(ctx);
6970 }
6971
6972 static void
6973 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6974 {
6975     const char *str;
6976
6977     saxdb_start_record(ctx, ntype->name, 0);
6978     switch(ntype->set_access_type)
6979     {
6980     case NOTE_SET_CHANNEL_ACCESS:
6981         saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6982         break;
6983     case NOTE_SET_CHANNEL_SETTER:
6984         saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6985         break;
6986     case NOTE_SET_PRIVILEGED: default:
6987         saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6988         break;
6989     }
6990     switch(ntype->visible_type)
6991     {
6992     case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6993     case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6994     case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6995     }
6996     saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6997     saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6998     saxdb_end_record(ctx);
6999 }
7000
7001 static void
7002 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7003 {
7004     struct do_not_register *dnr;
7005     dict_iterator_t it;
7006
7007     for(it = dict_first(dnrs); it; it = iter_next(it))
7008     {
7009         dnr = iter_data(it);
7010         saxdb_start_record(ctx, dnr->chan_name, 0);
7011         if(dnr->set)
7012             saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7013         saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7014         saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7015         saxdb_end_record(ctx);
7016     }
7017 }
7018
7019 static int
7020 chanserv_saxdb_write(struct saxdb_context *ctx)
7021 {
7022     dict_iterator_t it;
7023     struct chanData *channel;
7024
7025     /* Notes */
7026     saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7027     for(it = dict_first(note_types); it; it = iter_next(it))
7028         chanserv_write_note_type(ctx, iter_data(it));
7029     saxdb_end_record(ctx);
7030
7031     /* DNRs */
7032     saxdb_start_record(ctx, KEY_DNR, 1);
7033     write_dnrs_helper(ctx, handle_dnrs);
7034     write_dnrs_helper(ctx, plain_dnrs);
7035     write_dnrs_helper(ctx, mask_dnrs);
7036     saxdb_end_record(ctx);
7037
7038     /* Channels */
7039     saxdb_start_record(ctx, KEY_CHANNELS, 1);
7040     for(channel = channelList; channel; channel = channel->next)
7041         chanserv_write_channel(ctx, channel);
7042     saxdb_end_record(ctx);
7043
7044     return 0;
7045 }
7046
7047 static void
7048 chanserv_db_cleanup(void) {
7049     unsigned int ii;
7050     unreg_part_func(handle_part);
7051     while(channelList)
7052         unregister_channel(channelList, "terminating.");
7053     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7054         UnlockChannel(chanserv_conf.support_channels.list[ii]);
7055     free(chanserv_conf.support_channels.list);
7056     dict_delete(handle_dnrs);
7057     dict_delete(plain_dnrs);
7058     dict_delete(mask_dnrs);
7059     dict_delete(note_types);
7060     free_string_list(chanserv_conf.eightball);
7061     free_string_list(chanserv_conf.old_ban_names);
7062     free_string_list(chanserv_conf.set_shows);
7063     free(set_shows_list.list);
7064     free(uset_shows_list.list);
7065     while(helperList)
7066     {
7067         struct userData *helper = helperList;
7068         helperList = helperList->next;
7069         free(helper);
7070     }
7071 }
7072
7073 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7074 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7075 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7076
7077 void
7078 init_chanserv(const char *nick)
7079 {
7080     CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7081     conf_register_reload(chanserv_conf_read);
7082
7083     reg_server_link_func(handle_server_link);
7084
7085     reg_new_channel_func(handle_new_channel);
7086     reg_join_func(handle_join);
7087     reg_part_func(handle_part);
7088     reg_kick_func(handle_kick);
7089     reg_topic_func(handle_topic);
7090     reg_mode_change_func(handle_mode);
7091     reg_nick_change_func(handle_nick_change);
7092
7093     reg_auth_func(handle_auth);
7094     reg_handle_rename_func(handle_rename);
7095     reg_unreg_func(handle_unreg);
7096
7097     handle_dnrs = dict_new();
7098     dict_set_free_data(handle_dnrs, free);
7099     plain_dnrs = dict_new();
7100     dict_set_free_data(plain_dnrs, free);
7101     mask_dnrs = dict_new();
7102     dict_set_free_data(mask_dnrs, free);
7103
7104     reg_svccmd_unbind_func(handle_svccmd_unbind);
7105     chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7106     DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7107     DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7108     DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7109     DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7110     DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7111     DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7112     DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7113     DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7114
7115     DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7116     DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7117
7118     DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7119     DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7120     DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7121     DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7122     DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7123
7124     DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7125     DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7126     DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7127     DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7128     DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7129
7130     DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7131     DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7132     DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7133     DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7134
7135     DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7136     DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7137     DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7138     DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7139     DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7140     DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7141     DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7142     DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7143
7144     DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7145     DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7146     DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7147     DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7148     DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7149     DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7150     DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7151     DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7152     DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7153     DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7154     DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7155     DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7156     DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7157     DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7158
7159     DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7160     DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7161     DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7162     DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7163     DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7164
7165     DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7166     DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7167
7168     DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7169     DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7170     DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7171     DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7172     DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7173     DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7174     DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7175     DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7176     DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7177     DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7178     DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7179
7180     DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7181     DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7182
7183     DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7184     DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7185     DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7186     DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7187
7188     DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7189     DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7190     DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7191     DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7192     DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7193
7194     DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7195     DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7196     DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7197     DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7198     DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7199     DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7200
7201     /* Channel options */
7202     DEFINE_CHANNEL_OPTION(defaulttopic);
7203     DEFINE_CHANNEL_OPTION(topicmask);
7204     DEFINE_CHANNEL_OPTION(greeting);
7205     DEFINE_CHANNEL_OPTION(usergreeting);
7206     DEFINE_CHANNEL_OPTION(modes);
7207     DEFINE_CHANNEL_OPTION(enfops);
7208     DEFINE_CHANNEL_OPTION(giveops);
7209     DEFINE_CHANNEL_OPTION(protect);
7210     DEFINE_CHANNEL_OPTION(enfmodes);
7211     DEFINE_CHANNEL_OPTION(enftopic);
7212     DEFINE_CHANNEL_OPTION(pubcmd);
7213     DEFINE_CHANNEL_OPTION(givevoice);
7214     DEFINE_CHANNEL_OPTION(userinfo);
7215     DEFINE_CHANNEL_OPTION(dynlimit);
7216     DEFINE_CHANNEL_OPTION(topicsnarf);
7217     DEFINE_CHANNEL_OPTION(nodelete);
7218     DEFINE_CHANNEL_OPTION(toys);
7219     DEFINE_CHANNEL_OPTION(setters);
7220     DEFINE_CHANNEL_OPTION(topicrefresh);
7221     DEFINE_CHANNEL_OPTION(ctcpusers);
7222     DEFINE_CHANNEL_OPTION(ctcpreaction);
7223     DEFINE_CHANNEL_OPTION(inviteme);
7224     if(off_channel)
7225         DEFINE_CHANNEL_OPTION(offchannel);
7226     modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7227
7228     /* Alias set topic to set defaulttopic for compatibility. */
7229     modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7230
7231     /* User options */
7232     DEFINE_USER_OPTION(noautoop);
7233     DEFINE_USER_OPTION(autoinvite);
7234     DEFINE_USER_OPTION(info);
7235
7236     /* Alias uset autovoice to uset autoop. */
7237     modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7238
7239     note_types = dict_new();
7240     dict_set_free_data(note_types, chanserv_deref_note_type);
7241     if(nick)
7242     {
7243         chanserv = AddService(nick, "Channel Services", NULL);
7244         service_register(chanserv)->trigger = '!';
7245         reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7246     }
7247     saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7248
7249     if(chanserv_conf.channel_expire_frequency)
7250         timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7251
7252     if(chanserv_conf.refresh_period)
7253     {
7254         time_t next_refresh;
7255         next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7256         timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7257     }
7258     
7259     reg_exit_func(chanserv_db_cleanup);
7260     message_register_table(msgtab);
7261 }