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