2979451e9d59561251977e1ac7976b29670cc7e9
[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 = (uData->access >= UL_OWNER) ? UL_OWNER : (uData->access - 1);
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     scan_user_presence(target, NULL);
5548     reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5549     return 1;
5550 }
5551
5552 static MODCMD_FUNC(cmd_deleteme)
5553 {
5554     struct handle_info *hi;
5555     struct userData *target;
5556     const char *confirm_string;
5557     unsigned short access;
5558     char *channel_name;
5559
5560     hi = user->handle_info;
5561     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5562     {
5563         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5564         return 0;
5565     }
5566     if(target->access == UL_OWNER)
5567     {
5568         reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5569         return 0;
5570     }
5571     confirm_string = make_confirmation_string(target);
5572     if((argc < 2) || strcmp(argv[1], confirm_string))
5573     {
5574         reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5575         return 0;
5576     }
5577     access = target->access;
5578     channel_name = strdup(channel->name);
5579     del_channel_user(target, 1);
5580     reply("CSMSG_DELETED_YOU", access, channel_name);
5581     free(channel_name);
5582     return 1;
5583 }
5584
5585 static void
5586 chanserv_refresh_topics(UNUSED_ARG(void *data))
5587 {
5588     unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5589     struct chanData *cData;
5590     char opt;
5591
5592     for(cData = channelList; cData; cData = cData->next)
5593     {
5594         if(IsSuspended(cData))
5595             continue;
5596         opt = cData->chOpts[chTopicRefresh];
5597         if(opt == 'n')
5598             continue;
5599         if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5600             continue;
5601         if(cData->topic)
5602             SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5603         cData->last_refresh = refresh_num;
5604     }
5605     timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5606 }
5607
5608 static CHANSERV_FUNC(cmd_unf)
5609 {
5610     if(channel)
5611     {
5612         char response[MAXLEN];
5613         const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5614         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
5615         irc_privmsg(cmd->parent->bot, channel->name, response);
5616     }
5617     else
5618         reply("CSMSG_UNF_RESPONSE");
5619     return 1;
5620 }
5621
5622 static CHANSERV_FUNC(cmd_ping)
5623 {
5624     if(channel)
5625     {
5626         char response[MAXLEN];
5627         const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5628         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
5629         irc_privmsg(cmd->parent->bot, channel->name, response);
5630     }
5631     else
5632         reply("CSMSG_PING_RESPONSE");
5633     return 1;
5634 }
5635
5636 static CHANSERV_FUNC(cmd_wut)
5637 {
5638     if(channel)
5639     {
5640         char response[MAXLEN];
5641         const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5642         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
5643         irc_privmsg(cmd->parent->bot, channel->name, response);
5644     }
5645     else
5646         reply("CSMSG_WUT_RESPONSE");
5647     return 1;
5648 }
5649
5650 static CHANSERV_FUNC(cmd_8ball)
5651 {
5652     unsigned int i, j, accum;
5653     const char *resp;
5654
5655     REQUIRE_PARAMS(2);
5656     accum = 0;
5657     for(i=1; i<argc; i++)
5658         for(j=0; argv[i][j]; j++)
5659             accum = (accum << 5) - accum + toupper(argv[i][j]);
5660     resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5661     if(channel)
5662     {
5663         char response[MAXLEN];
5664         sprintf(response, "\ 2%s\ 2: %s", user->nick, resp);
5665         irc_privmsg(cmd->parent->bot, channel->name, response);
5666     }
5667     else
5668         send_message_type(4, user, cmd->parent->bot, "%s", resp);
5669     return 1;
5670 }
5671
5672 static CHANSERV_FUNC(cmd_d)
5673 {
5674     unsigned long sides, count, modifier, ii, total;
5675     char response[MAXLEN], *sep;
5676     const char *fmt;
5677
5678     REQUIRE_PARAMS(2);
5679     if((count = strtoul(argv[1], &sep, 10)) < 1)
5680         goto no_dice;
5681     if(sep[0] == 0)
5682     {
5683         if(count == 1)
5684             goto no_dice;
5685         sides = count;
5686         count = 1;
5687         modifier = 0;
5688     }
5689     else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5690             && (sides = strtoul(sep+1, &sep, 10)) > 1)
5691     {
5692         if(sep[0] == 0)
5693             modifier = 0;
5694         else if((sep[0] == '-') && isdigit(sep[1]))
5695             modifier = strtoul(sep, NULL, 10);
5696         else if((sep[0] == '+') && isdigit(sep[1]))
5697             modifier = strtoul(sep+1, NULL, 10);
5698         else
5699             goto no_dice;
5700     }
5701     else
5702     {
5703       no_dice:
5704         reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5705         return 0;
5706     }
5707     if(count > 10)
5708     {
5709         reply("CSMSG_BAD_DICE_COUNT", count, 10);
5710         return 0;
5711     }
5712     for(total = ii = 0; ii < count; ++ii)
5713         total += (rand() % sides) + 1;
5714     total += modifier;
5715
5716     if((count > 1) || modifier)
5717     {
5718         fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5719         sprintf(response, fmt, total, count, sides, modifier);
5720     }
5721     else
5722     {
5723         fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5724         sprintf(response, fmt, total, sides);
5725     }
5726     if(channel)
5727         send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5728     else
5729         send_message_type(4, user, cmd->parent->bot, "%s", response);
5730     return 1;
5731 }
5732
5733 static CHANSERV_FUNC(cmd_huggle)
5734 {
5735     /* CTCP must be via PRIVMSG, never notice */
5736     if(channel)
5737         send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5738     else
5739         send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5740     return 1;
5741 }
5742
5743 static void
5744 chanserv_adjust_limit(void *data)
5745 {
5746     struct mod_chanmode change;
5747     struct chanData *cData = data;
5748     struct chanNode *channel = cData->channel;
5749     unsigned int limit;
5750
5751     if(IsSuspended(cData))
5752         return;
5753
5754     cData->limitAdjusted = now;
5755     limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5756     if(cData->modes.modes_set & MODE_LIMIT)
5757     {
5758         if(limit > cData->modes.new_limit)
5759             limit = cData->modes.new_limit;
5760         else if(limit == cData->modes.new_limit)
5761             return;
5762     }
5763
5764     mod_chanmode_init(&change);
5765     change.modes_set = MODE_LIMIT;
5766     change.new_limit = limit;
5767     mod_chanmode_announce(chanserv, channel, &change);
5768 }
5769
5770 static void
5771 handle_new_channel(struct chanNode *channel)
5772 {
5773     struct chanData *cData;
5774
5775     if(!(cData = channel->channel_info))
5776         return;
5777
5778     if(cData->modes.modes_set || cData->modes.modes_clear)
5779         mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5780
5781     if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5782         SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5783 }
5784
5785 /* Welcome to my worst nightmare. Warning: Read (or modify)
5786    the code below at your own risk. */
5787 static int
5788 handle_join(struct modeNode *mNode)
5789 {
5790     struct mod_chanmode change;
5791     struct userNode *user = mNode->user;
5792     struct chanNode *channel = mNode->channel;
5793     struct chanData *cData;
5794     struct userData *uData = NULL;
5795     struct banData *bData;
5796     struct handle_info *handle;
5797     unsigned int modes = 0, info = 0;
5798     char *greeting;
5799
5800     if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5801         return 0;
5802
5803     cData = channel->channel_info;
5804     if(channel->members.used > cData->max)
5805         cData->max = channel->members.used;
5806
5807     /* Check for bans.  If they're joining through a ban, one of two
5808      * cases applies:
5809      *   1: Join during a netburst, by riding the break.  Kick them
5810      *      unless they have ops or voice in the channel.
5811      *   2: They're allowed to join through the ban (an invite in
5812      *   ircu2.10, or a +e on Hybrid, or something).
5813      * If they're not joining through a ban, and the banlist is not
5814      * full, see if they're on the banlist for the channel.  If so,
5815      * kickban them.
5816      */
5817     if(user->uplink->burst && !mNode->modes)
5818     {
5819         unsigned int ii;
5820         for(ii = 0; ii < channel->banlist.used; ii++)
5821         {
5822             if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5823             {
5824                 /* Riding a netburst.  Naughty. */
5825                 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5826                 return 1;
5827             }
5828         }
5829     }
5830
5831     mod_chanmode_init(&change);
5832     change.argc = 1;
5833     if(channel->banlist.used < MAXBANS)
5834     {
5835         /* Not joining through a ban. */
5836         for(bData = cData->bans;
5837                 bData && !user_matches_glob(user, bData->mask, 1);
5838                 bData = bData->next);
5839
5840         if(bData)
5841         {
5842             char kick_reason[MAXLEN];
5843             sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5844
5845             bData->triggered = now;
5846             if(bData != cData->bans)
5847             {
5848                 /* Shuffle the ban to the head of the list. */
5849                 if(bData->next)
5850                     bData->next->prev = bData->prev;
5851                 if(bData->prev)
5852                     bData->prev->next = bData->next;
5853
5854                 bData->prev = NULL;
5855                 bData->next = cData->bans;
5856
5857                 if(cData->bans)
5858                     cData->bans->prev = bData;
5859                 cData->bans = bData;
5860             }
5861
5862             change.args[0].mode = MODE_BAN;
5863             change.args[0].u.hostmask = bData->mask;
5864             mod_chanmode_announce(chanserv, channel, &change);
5865             KickChannelUser(user, channel, chanserv, kick_reason);
5866             return 1;
5867         }
5868     }
5869
5870     /* ChanServ will not modify the limits in join-flooded channels.
5871        It will also skip DynLimit processing when the user (or srvx)
5872        is bursting in, because there are likely more incoming. */
5873     if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5874        && !user->uplink->burst
5875        && !channel->join_flooded
5876        && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5877     {
5878         /* The user count has begun "bumping" into the channel limit,
5879            so set a timer to raise the limit a bit. Any previous
5880            timers are removed so three incoming users within the delay
5881            results in one limit change, not three. */
5882
5883         timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5884         timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5885     }
5886
5887     if(channel->join_flooded)
5888     {
5889         /* don't automatically give ops or voice during a join flood */
5890     }
5891     else if(cData->lvlOpts[lvlGiveOps] == 0)
5892         modes |= MODE_CHANOP;
5893     else if(cData->lvlOpts[lvlGiveVoice] == 0)
5894         modes |= MODE_VOICE;
5895
5896     greeting = cData->greeting;
5897     if(user->handle_info)
5898     {
5899         handle = user->handle_info;
5900
5901         if(IsHelper(user) && !IsHelping(user))
5902         {
5903             unsigned int ii;
5904             for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5905             {
5906                 if(channel == chanserv_conf.support_channels.list[ii])
5907                 {
5908                     HANDLE_SET_FLAG(user->handle_info, HELPING);
5909                     break;
5910                 }
5911             }
5912         }
5913
5914         uData = GetTrueChannelAccess(cData, handle);
5915         if(uData && !IsUserSuspended(uData))
5916         {
5917             /* Ops and above were handled by the above case. */
5918             if(IsUserAutoOp(uData))
5919             {
5920                 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5921                     modes |= MODE_CHANOP;
5922                 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5923                     modes |= MODE_VOICE;
5924             }
5925             if(uData->access >= UL_PRESENT)
5926                 cData->visited = now;
5927             if(cData->user_greeting)
5928                 greeting = cData->user_greeting;
5929             if(uData->info
5930                && (uData->access >= cData->lvlOpts[lvlUserInfo])
5931                && ((now - uData->seen) >= chanserv_conf.info_delay)
5932                && !uData->present)
5933                 info = 1;
5934             uData->seen = now;
5935             uData->present = 1;
5936         }
5937     }
5938     if(!user->uplink->burst)
5939     {
5940         if(modes)
5941         {
5942             if(modes & MODE_CHANOP)
5943                 modes &= ~MODE_VOICE;
5944             change.args[0].mode = modes;
5945             change.args[0].u.member = mNode;
5946             mod_chanmode_announce(chanserv, channel, &change);
5947         }
5948         if(greeting && !user->uplink->burst)
5949             send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5950         if(uData && info)
5951             send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5952     }
5953     return 0;
5954 }
5955
5956 static void
5957 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5958 {
5959     struct mod_chanmode change;
5960     struct userData *channel;
5961     unsigned int ii, jj;
5962
5963     if(!user->handle_info)
5964         return;
5965
5966     mod_chanmode_init(&change);
5967     change.argc = 1;
5968     for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5969     {
5970         struct chanNode *cn;
5971         struct modeNode *mn;
5972         if(IsUserSuspended(channel)
5973            || IsSuspended(channel->channel)
5974            || !(cn = channel->channel->channel))
5975             continue;
5976
5977         mn = GetUserMode(cn, user);
5978         if(!mn)
5979         {
5980             if(!IsUserSuspended(channel)
5981                && IsUserAutoInvite(channel)
5982                && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
5983                && !self->burst
5984                && !user->uplink->burst)
5985                 irc_invite(chanserv, user, cn);
5986             continue;
5987         }
5988
5989         if(channel->access >= UL_PRESENT)
5990             channel->channel->visited = now;
5991
5992         if(IsUserAutoOp(channel))
5993         {
5994             if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5995                 change.args[0].mode = MODE_CHANOP;
5996             else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
5997                 change.args[0].mode = MODE_VOICE;
5998             else
5999                 change.args[0].mode = 0;
6000             change.args[0].u.member = mn;
6001             if(change.args[0].mode)
6002                 mod_chanmode_announce(chanserv, cn, &change);
6003         }
6004
6005         channel->seen = now;
6006         channel->present = 1;
6007     }
6008
6009     for(ii = 0; ii < user->channels.used; ++ii)
6010     {
6011         struct chanNode *channel = user->channels.list[ii]->channel;
6012         struct banData *ban;
6013
6014         if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6015            || !channel->channel_info)
6016             continue;
6017         for(jj = 0; jj < channel->banlist.used; ++jj)
6018             if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6019                 break;
6020         if(jj < channel->banlist.used)
6021             continue;
6022         for(ban = channel->channel_info->bans; ban; ban = ban->next)
6023         {
6024             char kick_reason[MAXLEN];
6025             if(!user_matches_glob(user, ban->mask, 1))
6026                 continue;
6027             change.args[0].mode = MODE_BAN;
6028             change.args[0].u.hostmask = ban->mask;
6029             mod_chanmode_announce(chanserv, channel, &change);
6030             sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6031             KickChannelUser(user, channel, chanserv, kick_reason);
6032             ban->triggered = now;
6033             break;
6034         }
6035     }
6036
6037     if(IsSupportHelper(user))
6038     {
6039         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6040         {
6041             if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6042             {
6043                 HANDLE_SET_FLAG(user->handle_info, HELPING);
6044                 break;
6045             }
6046         }
6047     }
6048 }
6049
6050 static void
6051 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6052 {
6053     struct chanData *cData;
6054     struct userData *uData;
6055
6056     cData = mn->channel->channel_info;
6057     if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6058         return;
6059
6060     if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6061     {
6062         /* Allow for a bit of padding so that the limit doesn't
6063            track the user count exactly, which could get annoying. */
6064         if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6065         {
6066             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6067             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6068         }
6069     }
6070
6071     if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6072     {
6073         scan_user_presence(uData, mn->user);
6074         uData->seen = now;
6075     }
6076
6077     if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6078     {
6079         unsigned int ii, jj;
6080         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6081         {
6082             for(jj = 0; jj < mn->user->channels.used; ++jj)
6083                 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6084                     break;
6085             if(jj < mn->user->channels.used)
6086                 break;
6087         }
6088         if(ii == chanserv_conf.support_channels.used)
6089             HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6090     }
6091 }
6092
6093 static void
6094 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6095 {
6096     struct userData *uData;
6097
6098     if(!channel->channel_info || !kicker || IsService(kicker)
6099        || (kicker == victim) || IsSuspended(channel->channel_info)
6100        || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6101         return;
6102
6103     if(protect_user(victim, kicker, channel->channel_info))
6104     {
6105         const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6106         KickChannelUser(kicker, channel, chanserv, reason);
6107     }
6108
6109     if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6110         uData->seen = now;
6111 }
6112
6113 static int
6114 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6115 {
6116     struct chanData *cData;
6117
6118     if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6119         return 0;
6120
6121     cData = channel->channel_info;
6122     if(bad_topic(channel, user, channel->topic))
6123     {
6124         send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6125         if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6126             SetChannelTopic(channel, chanserv, old_topic, 1);
6127         else if(cData->topic)
6128             SetChannelTopic(channel, chanserv, cData->topic, 1);
6129         return 1;
6130     }
6131     /* With topicsnarf, grab the topic and save it as the default topic. */
6132     if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6133     {
6134         free(cData->topic);
6135         cData->topic = strdup(channel->topic);
6136     }
6137     return 0;
6138 }
6139
6140 static void
6141 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6142 {
6143     struct mod_chanmode *bounce = NULL;
6144     unsigned int bnc, ii;
6145     char deopped = 0;
6146
6147     if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6148         return;
6149
6150     if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6151        && mode_lock_violated(&channel->channel_info->modes, change))
6152     {
6153         char correct[MAXLEN];
6154         bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6155         mod_chanmode_format(&channel->channel_info->modes, correct);
6156         send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6157     }
6158     for(ii = bnc = 0; ii < change->argc; ++ii)
6159     {
6160         if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6161         {
6162             const struct userNode *victim = change->args[ii].u.member->user;
6163             if(!protect_user(victim, user, channel->channel_info))
6164                 continue;
6165             if(!bounce)
6166                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6167             if(!deopped)
6168             {
6169                 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6170                 bounce->args[bnc].u.member = GetUserMode(channel, user);
6171                 if(bounce->args[bnc].u.member)
6172                     bnc++;
6173                 deopped = 1;
6174             }
6175             bounce->args[bnc].mode = MODE_CHANOP;
6176             bounce->args[bnc].u.member = change->args[ii].u.member;
6177             bnc++;
6178             send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6179         }
6180         else if(change->args[ii].mode & MODE_CHANOP)
6181         {
6182             const struct userNode *victim = change->args[ii].u.member->user;
6183             if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6184                 continue;
6185             if(!bounce)
6186                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6187             bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6188             bounce->args[bnc].u.member = change->args[ii].u.member;
6189             bnc++;
6190         }
6191         else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6192         {
6193             const char *ban = change->args[ii].u.hostmask;
6194             if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6195                 continue;
6196             if(!bounce)
6197                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6198             bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6199             bounce->args[bnc].u.hostmask = ban;
6200             bnc++;
6201             send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6202         }
6203     }
6204     if(bounce)
6205     {
6206         if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6207             mod_chanmode_announce(chanserv, channel, bounce);
6208         mod_chanmode_free(bounce);
6209     }
6210 }
6211
6212 static void
6213 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6214 {
6215     struct chanNode *channel;
6216     struct banData *bData;
6217     struct mod_chanmode change;
6218     unsigned int ii, jj;
6219     char kick_reason[MAXLEN];
6220
6221     mod_chanmode_init(&change);
6222     change.argc = 1;
6223     change.args[0].mode = MODE_BAN;
6224     for(ii = 0; ii < user->channels.used; ++ii)
6225     {
6226         channel = user->channels.list[ii]->channel;
6227         /* Need not check for bans if they're opped or voiced. */
6228         if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6229             continue;
6230         /* Need not check for bans unless channel registration is active. */
6231         if(!channel->channel_info || IsSuspended(channel->channel_info))
6232             continue;
6233         /* Look for a matching ban already on the channel. */
6234         for(jj = 0; jj < channel->banlist.used; ++jj)
6235             if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6236                 break;
6237         /* Need not act if we found one. */
6238         if(jj < channel->banlist.used)
6239             continue;
6240         /* Look for a matching ban in this channel. */
6241         for(bData = channel->channel_info->bans; bData; bData = bData->next)
6242         {
6243             if(!user_matches_glob(user, bData->mask, 1))
6244                 continue;
6245             change.args[0].u.hostmask = bData->mask;
6246             mod_chanmode_announce(chanserv, channel, &change);
6247             sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6248             KickChannelUser(user, channel, chanserv, kick_reason);
6249             bData->triggered = now;
6250             break; /* we don't need to check any more bans in the channel */
6251         }
6252     }
6253 }
6254
6255 static void handle_rename(struct handle_info *handle, const char *old_handle)
6256 {
6257     struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6258
6259     if(dnr)
6260     {
6261         dict_remove2(handle_dnrs, old_handle, 1);
6262         safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6263         dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6264     }
6265 }
6266
6267 static void
6268 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6269 {
6270     struct userNode *h_user;
6271
6272     if(handle->channels)
6273     {
6274         for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6275             send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6276
6277         while(handle->channels)
6278             del_channel_user(handle->channels, 1);
6279     }
6280 }
6281
6282 static void
6283 handle_server_link(UNUSED_ARG(struct server *server))
6284 {
6285     struct chanData *cData;
6286
6287     for(cData = channelList; cData; cData = cData->next)
6288     {
6289         if(!IsSuspended(cData))
6290             cData->may_opchan = 1;
6291         if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6292            && !cData->channel->join_flooded
6293            && ((cData->channel->limit - cData->channel->members.used)
6294                < chanserv_conf.adjust_threshold))
6295         {
6296             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6297             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6298         }
6299     }
6300 }
6301
6302 static void
6303 chanserv_conf_read(void)
6304 {
6305     dict_t conf_node;
6306     const char *str;
6307     char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6308     struct mod_chanmode *change;
6309     struct string_list *strlist;
6310     struct chanNode *chan;
6311     unsigned int ii;
6312
6313     if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6314     {
6315         log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6316         return;
6317     }
6318     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6319         UnlockChannel(chanserv_conf.support_channels.list[ii]);
6320     chanserv_conf.support_channels.used = 0;
6321     if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6322     {
6323         for(ii = 0; ii < strlist->used; ++ii)
6324         {
6325             const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6326             if(!str2)
6327                 str2 = "+nt";
6328             chan = AddChannel(strlist->list[ii], now, str2, NULL);
6329             LockChannel(chan);
6330             channelList_append(&chanserv_conf.support_channels, chan);
6331         }
6332     }
6333     else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6334     {
6335         const char *str2;
6336         str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6337         if(!str2)
6338             str2 = "+nt";
6339         chan = AddChannel(str, now, str2, NULL);
6340         LockChannel(chan);
6341         channelList_append(&chanserv_conf.support_channels, chan);
6342     }
6343     str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6344     chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6345     str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6346     chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6347     str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6348     chanserv_conf.greeting_length = str ? atoi(str) : 120;
6349     str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6350     chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6351     str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6352     chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6353     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6354     chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6355     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6356     chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6357     str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6358     chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6359     str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6360     chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6361     str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6362     chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6363     str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6364     chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6365     str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6366     if(chanserv && str)
6367         NickChange(chanserv, str, 0);
6368     str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6369     chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6370     str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6371     chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6372     str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6373     chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6374     str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6375     chanserv_conf.max_owned = str ? atoi(str) : 5;
6376     str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6377     chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6378     str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6379     chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6380     str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6381     chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6382     str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6383     if(!str)
6384         str = "+nt";
6385     safestrncpy(mode_line, str, sizeof(mode_line));
6386     ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6387     if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6388     {
6389         chanserv_conf.default_modes = *change;
6390         mod_chanmode_free(change);
6391     }
6392     free_string_list(chanserv_conf.set_shows);
6393     strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6394     if(strlist)
6395         strlist = string_list_copy(strlist);
6396     else
6397     {
6398         static const char *list[] = {
6399             /* free form text */
6400             "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6401             /* options based on user level */
6402             "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6403             "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6404             /* multiple choice options */
6405             "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6406             /* binary options */
6407             "DynLimit", "NoDelete",
6408             /* delimiter */
6409             NULL
6410         };
6411         unsigned int ii;
6412         strlist = alloc_string_list(ArrayLength(list)-1);
6413         for(ii=0; list[ii]; ii++)
6414             string_list_append(strlist, strdup(list[ii]));
6415     }
6416     chanserv_conf.set_shows = strlist;
6417     /* We don't look things up now, in case the list refers to options
6418      * defined by modules initialized after this point.  Just mark the
6419      * function list as invalid, so it will be initialized.
6420      */
6421     set_shows_list.used = 0;
6422     free_string_list(chanserv_conf.eightball);
6423     strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6424     if(strlist)
6425     {
6426         strlist = string_list_copy(strlist);
6427     }
6428     else
6429     {
6430         strlist = alloc_string_list(4);
6431         string_list_append(strlist, strdup("Yes."));
6432         string_list_append(strlist, strdup("No."));
6433         string_list_append(strlist, strdup("Maybe so."));
6434     }
6435     chanserv_conf.eightball = strlist;
6436     free_string_list(chanserv_conf.old_ban_names);
6437     strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6438     if(strlist)
6439         strlist = string_list_copy(strlist);
6440     else
6441         strlist = alloc_string_list(2);
6442     chanserv_conf.old_ban_names = strlist;
6443     /* the variable itself is actually declared in proto-common.c; this is equally 
6444      * parse issue. */
6445     str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6446     off_channel = (str && enabled_string(str)) ? 1 : 0;
6447     str = database_get_data(conf_node, "use_registered_mode", RECDB_QSTRING);
6448     chanserv_conf.use_registered_mode = (str && enabled_string(str)) ? 1 : 0;
6449 }
6450
6451 static void
6452 chanserv_note_type_read(const char *key, struct record_data *rd)
6453 {
6454     dict_t obj;
6455     struct note_type *ntype;
6456     const char *str;
6457
6458     if(!(obj = GET_RECORD_OBJECT(rd)))
6459     {
6460         log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6461         return;
6462     }
6463     if(!(ntype = chanserv_create_note_type(key)))
6464     {
6465         log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6466         return;
6467     }
6468
6469     /* Figure out set access */
6470     if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6471     {
6472         ntype->set_access_type = NOTE_SET_PRIVILEGED;
6473         ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6474     }
6475     else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6476     {
6477         ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6478         ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6479     }
6480     else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6481     {
6482         ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6483     }
6484     else
6485     {
6486         log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6487         ntype->set_access_type = NOTE_SET_PRIVILEGED;
6488         ntype->set_access.min_opserv = 0;
6489     }
6490
6491     /* Figure out visibility */
6492     if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6493         ntype->visible_type = NOTE_VIS_PRIVILEGED;
6494     else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6495         ntype->visible_type = NOTE_VIS_PRIVILEGED;
6496     else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6497         ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6498     else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6499         ntype->visible_type = NOTE_VIS_ALL;
6500     else
6501         ntype->visible_type = NOTE_VIS_PRIVILEGED;
6502
6503     str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6504     ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6505 }
6506
6507 static void
6508 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6509 {
6510     struct handle_info *handle;
6511     struct userData *uData;
6512     char *seen, *inf, *flags;
6513     time_t last_seen;
6514     unsigned short access;
6515
6516     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6517     {
6518         log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6519         return;
6520     }
6521
6522     access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6523     if(access > UL_OWNER)
6524     {
6525         log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6526         return;
6527     }
6528
6529     inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6530     seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6531     last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6532     flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6533     handle = get_handle_info(key);
6534     if(!handle)
6535     {
6536         log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6537         return;
6538     }
6539
6540     uData = add_channel_user(chan, handle, access, last_seen, inf);
6541     uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6542 }
6543
6544 static void
6545 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6546 {
6547     struct banData *bData;
6548     char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6549     time_t set_time, triggered_time, expires_time;
6550
6551     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6552     {
6553         log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6554         return;
6555     }
6556
6557     set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6558     triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6559     s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6560     s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6561     owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6562     reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6563
6564     set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6565     triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6566     if(s_expires)
6567         expires_time = (time_t)strtoul(s_expires, NULL, 0);
6568     else if(s_duration)
6569         expires_time = set_time + atoi(s_duration);
6570     else
6571         expires_time = 0;
6572
6573     if(expires_time && (expires_time < now))
6574         return;
6575
6576     bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6577 }
6578
6579 static struct suspended *
6580 chanserv_read_suspended(dict_t obj)
6581 {
6582     struct suspended *suspended = calloc(1, sizeof(*suspended));
6583     char *str;
6584     dict_t previous;
6585
6586     str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6587     suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6588     str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6589     suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6590     str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6591     suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6592     suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6593     suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6594     previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6595     suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6596     return suspended;
6597 }
6598
6599 static int
6600 chanserv_channel_read(const char *key, struct record_data *hir)
6601 {
6602     struct suspended *suspended;
6603     struct mod_chanmode *modes;
6604     struct chanNode *cNode;
6605     struct chanData *cData;
6606     struct dict *channel, *obj;
6607     char *str, *argv[10];
6608     dict_iterator_t it;
6609     unsigned int argc;
6610
6611     channel = hir->d.object;
6612
6613     str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6614     if(!str)
6615         str = "<unknown>";
6616     cNode = AddChannel(key, now, NULL, NULL);
6617     if(!cNode)
6618     {
6619         log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6620         return 0;
6621     }
6622     cData = register_channel(cNode, str);
6623     if(!cData)
6624     {
6625         log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6626         return 0;
6627     }
6628
6629     if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6630     {
6631         enum levelOption lvlOpt;
6632         enum charOption chOpt;
6633
6634         if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6635             cData->flags = atoi(str);
6636
6637         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6638         {
6639             str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6640             if(str)
6641                 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6642             else if(levelOptions[lvlOpt].old_flag)
6643             {
6644                 if(cData->flags & levelOptions[lvlOpt].old_flag)
6645                     cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6646                 else
6647                     cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6648             }
6649         }
6650
6651         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6652         {
6653             if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6654                 continue;
6655             cData->chOpts[chOpt] = str[0];
6656         }
6657     }
6658     else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6659     {
6660         enum levelOption lvlOpt;
6661         enum charOption chOpt;
6662         unsigned int count;
6663
6664         cData->flags = base64toint(str, 5);
6665         count = strlen(str += 5);
6666         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6667         {
6668             unsigned short lvl;
6669             if(levelOptions[lvlOpt].old_flag)
6670             {
6671                 if(cData->flags & levelOptions[lvlOpt].old_flag)
6672                     lvl = levelOptions[lvlOpt].flag_value;
6673                 else
6674                     lvl = levelOptions[lvlOpt].default_value;
6675             }
6676             else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6677             {
6678             case 'c': lvl = UL_COOWNER; break;
6679             case 'm': lvl = UL_MASTER; break;
6680             case 'n': lvl = UL_OWNER+1; break;
6681             case 'o': lvl = UL_OP; break;
6682             case 'p': lvl = UL_PEON; break;
6683             case 'w': lvl = UL_OWNER; break;
6684             default: lvl = 0; break;
6685             }
6686             cData->lvlOpts[lvlOpt] = lvl;
6687         }
6688         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6689             cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6690     }
6691    
6692     if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6693     {
6694         suspended = chanserv_read_suspended(obj);
6695         cData->suspended = suspended;
6696         suspended->cData = cData;
6697         /* We could use suspended->expires and suspended->revoked to
6698          * set the CHANNEL_SUSPENDED flag, but we don't. */
6699     }
6700     else if(IsSuspended(cData))
6701     {
6702         suspended = calloc(1, sizeof(*suspended));
6703         suspended->issued = 0;
6704         suspended->revoked = 0;
6705         str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6706         suspended->expires = str ? atoi(str) : 0;
6707         suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6708         str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6709         suspended->reason = strdup(str ? str : "No reason");
6710         suspended->previous = NULL;
6711         cData->suspended = suspended;
6712         suspended->cData = cData;
6713     }
6714     else
6715         suspended = NULL; /* to squelch a warning */
6716
6717     if(IsSuspended(cData)) {
6718         if(suspended->expires > now)
6719             timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6720         else if(suspended->expires)
6721             cData->flags &= ~CHANNEL_SUSPENDED;
6722     }
6723
6724     if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6725         struct mod_chanmode change;
6726         mod_chanmode_init(&change);
6727         change.argc = 1;
6728         change.args[0].mode = MODE_CHANOP;
6729         change.args[0].u.member = AddChannelUser(chanserv, cNode);
6730         mod_chanmode_announce(chanserv, cNode, &change);
6731     }
6732
6733     str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6734     cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6735     str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6736     cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6737     str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6738     cData->max = str ? atoi(str) : 0;
6739     str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6740     cData->greeting = str ? strdup(str) : NULL;
6741     str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6742     cData->user_greeting = str ? strdup(str) : NULL;
6743     str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6744     cData->topic_mask = str ? strdup(str) : NULL;
6745     str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6746     cData->topic = str ? strdup(str) : NULL;
6747
6748     if(!IsSuspended(cData)
6749        && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6750        && (argc = split_line(str, 0, ArrayLength(argv), argv))
6751        && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6752         cData->modes = *modes;
6753         if(chanserv_conf.use_registered_mode)
6754           cData->modes.modes_set |= MODE_REGISTERED;
6755         if(cData->modes.argc > 1)
6756             cData->modes.argc = 1;
6757         mod_chanmode_announce(chanserv, cNode, &cData->modes);
6758         mod_chanmode_free(modes);
6759     }
6760
6761     obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6762     for(it = dict_first(obj); it; it = iter_next(it))
6763         user_read_helper(iter_key(it), iter_data(it), cData);
6764
6765     if(!cData->users && !IsProtected(cData))
6766     {
6767         log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6768         unregister_channel(cData, "has empty user list.");
6769         return 0;
6770     }
6771
6772     obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6773     for(it = dict_first(obj); it; it = iter_next(it))
6774         ban_read_helper(iter_key(it), iter_data(it), cData);
6775
6776     obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6777     for(it = dict_first(obj); it; it = iter_next(it))
6778     {
6779         struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6780         struct record_data *rd = iter_data(it);
6781         const char *note, *setter;
6782
6783         if(rd->type != RECDB_OBJECT)
6784         {
6785             log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6786         }
6787         else if(!ntype)
6788         {
6789             log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6790         }
6791         else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6792         {
6793             log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6794         }
6795         else
6796         {
6797             setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6798             if(!setter) setter = "<unknown>";
6799             chanserv_add_channel_note(cData, ntype, setter, note);
6800         }
6801     }
6802
6803     return 0;
6804 }
6805
6806 static void
6807 chanserv_dnr_read(const char *key, struct record_data *hir)
6808 {
6809     const char *setter, *reason, *str;
6810     struct do_not_register *dnr;
6811
6812     setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6813     if(!setter)
6814     {
6815         log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6816         return;
6817     }
6818     reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6819     if(!reason)
6820     {
6821         log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6822         return;
6823     }
6824     dnr = chanserv_add_dnr(key, setter, reason);
6825     if(!dnr)
6826         return;
6827     str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6828     if(str)
6829         dnr->set = atoi(str);
6830     else
6831         dnr->set = 0;
6832 }
6833
6834 static int
6835 chanserv_saxdb_read(struct dict *database)
6836 {
6837     struct dict *section;
6838     dict_iterator_t it;
6839
6840     if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6841         for(it = dict_first(section); it; it = iter_next(it))
6842             chanserv_note_type_read(iter_key(it), iter_data(it));
6843
6844     if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6845         for(it = dict_first(section); it; it = iter_next(it))
6846             chanserv_channel_read(iter_key(it), iter_data(it));
6847
6848     if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6849         for(it = dict_first(section); it; it = iter_next(it))
6850             chanserv_dnr_read(iter_key(it), iter_data(it));
6851
6852     return 0;
6853 }
6854
6855 static int
6856 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6857 {
6858     int high_present = 0;
6859     saxdb_start_record(ctx, KEY_USERS, 1);
6860     for(; uData; uData = uData->next)
6861     {
6862         if((uData->access >= UL_PRESENT) && uData->present)
6863             high_present = 1;
6864         saxdb_start_record(ctx, uData->handle->handle, 0);
6865         saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6866         saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6867         if(uData->flags)
6868             saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6869         if(uData->info)
6870             saxdb_write_string(ctx, KEY_INFO, uData->info);
6871         saxdb_end_record(ctx);
6872     }
6873     saxdb_end_record(ctx);
6874     return high_present;
6875 }
6876
6877 static void
6878 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6879 {
6880     if(!bData)
6881         return;
6882     saxdb_start_record(ctx, KEY_BANS, 1);
6883     for(; bData; bData = bData->next)
6884     {
6885         saxdb_start_record(ctx, bData->mask, 0);
6886         saxdb_write_int(ctx, KEY_SET, bData->set);
6887         if(bData->triggered)
6888             saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6889         if(bData->expires)
6890             saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6891         if(bData->owner[0])
6892             saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6893         if(bData->reason)
6894             saxdb_write_string(ctx, KEY_REASON, bData->reason);
6895         saxdb_end_record(ctx);
6896     }
6897     saxdb_end_record(ctx);
6898 }
6899
6900 static void
6901 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6902 {
6903     saxdb_start_record(ctx, name, 0);
6904     saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6905     saxdb_write_string(ctx, KEY_REASON, susp->reason);
6906     if(susp->issued)
6907         saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6908     if(susp->expires)
6909         saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6910     if(susp->revoked)
6911         saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6912     if(susp->previous)
6913         chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6914     saxdb_end_record(ctx);
6915 }
6916
6917 static void
6918 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6919 {
6920     char buf[MAXLEN];
6921     int high_present;
6922     enum levelOption lvlOpt;
6923     enum charOption chOpt;
6924
6925     saxdb_start_record(ctx, channel->channel->name, 1);
6926
6927     saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6928     saxdb_write_int(ctx, KEY_MAX, channel->max);
6929     if(channel->topic)
6930         saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6931     if(channel->registrar)
6932         saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6933     if(channel->greeting)
6934         saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6935     if(channel->user_greeting)
6936         saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6937     if(channel->topic_mask)
6938         saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6939     if(channel->suspended)
6940         chanserv_write_suspended(ctx, "suspended", channel->suspended);
6941
6942     saxdb_start_record(ctx, KEY_OPTIONS, 0);
6943     saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6944     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6945         saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6946     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6947     {
6948         buf[0] = channel->chOpts[chOpt];
6949         buf[1] = '\0';
6950         saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6951     }
6952     saxdb_end_record(ctx);
6953
6954     if(channel->modes.modes_set || channel->modes.modes_clear)
6955     {
6956         mod_chanmode_format(&channel->modes, buf);
6957         saxdb_write_string(ctx, KEY_MODES, buf);
6958     }
6959
6960     high_present = chanserv_write_users(ctx, channel->users);
6961     chanserv_write_bans(ctx, channel->bans);
6962
6963     if(dict_size(channel->notes))
6964     {
6965         dict_iterator_t it;
6966
6967         saxdb_start_record(ctx, KEY_NOTES, 1);
6968         for(it = dict_first(channel->notes); it; it = iter_next(it))
6969         {
6970             struct note *note = iter_data(it);
6971             saxdb_start_record(ctx, iter_key(it), 0);
6972             saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6973             saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6974             saxdb_end_record(ctx);
6975         }
6976         saxdb_end_record(ctx);
6977     }
6978
6979     saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6980     saxdb_end_record(ctx);
6981 }
6982
6983 static void
6984 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6985 {
6986     const char *str;
6987
6988     saxdb_start_record(ctx, ntype->name, 0);
6989     switch(ntype->set_access_type)
6990     {
6991     case NOTE_SET_CHANNEL_ACCESS:
6992         saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6993         break;
6994     case NOTE_SET_CHANNEL_SETTER:
6995         saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6996         break;
6997     case NOTE_SET_PRIVILEGED: default:
6998         saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6999         break;
7000     }
7001     switch(ntype->visible_type)
7002     {
7003     case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7004     case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7005     case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7006     }
7007     saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7008     saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7009     saxdb_end_record(ctx);
7010 }
7011
7012 static void
7013 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7014 {
7015     struct do_not_register *dnr;
7016     dict_iterator_t it;
7017
7018     for(it = dict_first(dnrs); it; it = iter_next(it))
7019     {
7020         dnr = iter_data(it);
7021         saxdb_start_record(ctx, dnr->chan_name, 0);
7022         if(dnr->set)
7023             saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7024         saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7025         saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7026         saxdb_end_record(ctx);
7027     }
7028 }
7029
7030 static int
7031 chanserv_saxdb_write(struct saxdb_context *ctx)
7032 {
7033     dict_iterator_t it;
7034     struct chanData *channel;
7035
7036     /* Notes */
7037     saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7038     for(it = dict_first(note_types); it; it = iter_next(it))
7039         chanserv_write_note_type(ctx, iter_data(it));
7040     saxdb_end_record(ctx);
7041
7042     /* DNRs */
7043     saxdb_start_record(ctx, KEY_DNR, 1);
7044     write_dnrs_helper(ctx, handle_dnrs);
7045     write_dnrs_helper(ctx, plain_dnrs);
7046     write_dnrs_helper(ctx, mask_dnrs);
7047     saxdb_end_record(ctx);
7048
7049     /* Channels */
7050     saxdb_start_record(ctx, KEY_CHANNELS, 1);
7051     for(channel = channelList; channel; channel = channel->next)
7052         chanserv_write_channel(ctx, channel);
7053     saxdb_end_record(ctx);
7054
7055     return 0;
7056 }
7057
7058 static void
7059 chanserv_db_cleanup(void) {
7060     unsigned int ii;
7061     unreg_part_func(handle_part);
7062     while(channelList)
7063         unregister_channel(channelList, "terminating.");
7064     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7065         UnlockChannel(chanserv_conf.support_channels.list[ii]);
7066     free(chanserv_conf.support_channels.list);
7067     dict_delete(handle_dnrs);
7068     dict_delete(plain_dnrs);
7069     dict_delete(mask_dnrs);
7070     dict_delete(note_types);
7071     free_string_list(chanserv_conf.eightball);
7072     free_string_list(chanserv_conf.old_ban_names);
7073     free_string_list(chanserv_conf.set_shows);
7074     free(set_shows_list.list);
7075     free(uset_shows_list.list);
7076     while(helperList)
7077     {
7078         struct userData *helper = helperList;
7079         helperList = helperList->next;
7080         free(helper);
7081     }
7082 }
7083
7084 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7085 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7086 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7087
7088 void
7089 init_chanserv(const char *nick)
7090 {
7091     CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7092     conf_register_reload(chanserv_conf_read);
7093
7094     reg_server_link_func(handle_server_link);
7095
7096     reg_new_channel_func(handle_new_channel);
7097     reg_join_func(handle_join);
7098     reg_part_func(handle_part);
7099     reg_kick_func(handle_kick);
7100     reg_topic_func(handle_topic);
7101     reg_mode_change_func(handle_mode);
7102     reg_nick_change_func(handle_nick_change);
7103
7104     reg_auth_func(handle_auth);
7105     reg_handle_rename_func(handle_rename);
7106     reg_unreg_func(handle_unreg);
7107
7108     handle_dnrs = dict_new();
7109     dict_set_free_data(handle_dnrs, free);
7110     plain_dnrs = dict_new();
7111     dict_set_free_data(plain_dnrs, free);
7112     mask_dnrs = dict_new();
7113     dict_set_free_data(mask_dnrs, free);
7114
7115     reg_svccmd_unbind_func(handle_svccmd_unbind);
7116     chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7117     DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7118     DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7119     DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7120     DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7121     DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7122     DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7123     DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7124     DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7125
7126     DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7127     DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7128
7129     DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7130     DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7131     DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7132     DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7133     DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7134
7135     DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7136     DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7137     DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7138     DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7139     DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7140
7141     DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7142     DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7143     DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7144     DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7145
7146     DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7147     DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7148     DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7149     DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7150     DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7151     DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7152     DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7153     DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7154
7155     DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7156     DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7157     DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7158     DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7159     DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7160     DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7161     DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7162     DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7163     DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7164     DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7165     DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7166     DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7167     DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7168     DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7169
7170     DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7171     DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7172     DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7173     DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7174     DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7175
7176     DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7177     DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7178
7179     DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7180     DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7181     DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7182     DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7183     DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7184     DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7185     DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7186     DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7187     DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7188     DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7189     DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7190
7191     DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7192     DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7193
7194     DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7195     DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7196     DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7197     DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7198
7199     DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7200     DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7201     DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7202     DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7203     DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7204
7205     DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7206     DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7207     DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7208     DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7209     DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7210     DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7211
7212     /* Channel options */
7213     DEFINE_CHANNEL_OPTION(defaulttopic);
7214     DEFINE_CHANNEL_OPTION(topicmask);
7215     DEFINE_CHANNEL_OPTION(greeting);
7216     DEFINE_CHANNEL_OPTION(usergreeting);
7217     DEFINE_CHANNEL_OPTION(modes);
7218     DEFINE_CHANNEL_OPTION(enfops);
7219     DEFINE_CHANNEL_OPTION(giveops);
7220     DEFINE_CHANNEL_OPTION(protect);
7221     DEFINE_CHANNEL_OPTION(enfmodes);
7222     DEFINE_CHANNEL_OPTION(enftopic);
7223     DEFINE_CHANNEL_OPTION(pubcmd);
7224     DEFINE_CHANNEL_OPTION(givevoice);
7225     DEFINE_CHANNEL_OPTION(userinfo);
7226     DEFINE_CHANNEL_OPTION(dynlimit);
7227     DEFINE_CHANNEL_OPTION(topicsnarf);
7228     DEFINE_CHANNEL_OPTION(nodelete);
7229     DEFINE_CHANNEL_OPTION(toys);
7230     DEFINE_CHANNEL_OPTION(setters);
7231     DEFINE_CHANNEL_OPTION(topicrefresh);
7232     DEFINE_CHANNEL_OPTION(ctcpusers);
7233     DEFINE_CHANNEL_OPTION(ctcpreaction);
7234     DEFINE_CHANNEL_OPTION(inviteme);
7235     if(off_channel)
7236         DEFINE_CHANNEL_OPTION(offchannel);
7237     modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7238
7239     /* Alias set topic to set defaulttopic for compatibility. */
7240     modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7241
7242     /* User options */
7243     DEFINE_USER_OPTION(noautoop);
7244     DEFINE_USER_OPTION(autoinvite);
7245     DEFINE_USER_OPTION(info);
7246
7247     /* Alias uset autovoice to uset autoop. */
7248     modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7249
7250     note_types = dict_new();
7251     dict_set_free_data(note_types, chanserv_deref_note_type);
7252     if(nick)
7253     {
7254         const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7255         chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
7256         service_register(chanserv)->trigger = '!';
7257         reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7258     }
7259     saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7260
7261     if(chanserv_conf.channel_expire_frequency)
7262         timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7263
7264     if(chanserv_conf.refresh_period)
7265     {
7266         time_t next_refresh;
7267         next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7268         timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7269     }
7270     
7271     reg_exit_func(chanserv_db_cleanup);
7272     message_register_table(msgtab);
7273 }