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