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