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