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