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