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