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