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