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