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