ChanServ seen fixes; other cleanups
[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
4040         if(IsService(mn->user))
4041             continue;
4042
4043         uData = GetChannelAccess(cData, mn->user->handle_info);
4044         if(!cData->lvlOpts[lvlGiveOps]
4045            || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4046         {
4047             if(!(mn->modes & MODE_CHANOP))
4048             {
4049                 changes->args[used].mode = MODE_CHANOP;
4050                 changes->args[used++].member = mn;
4051             }
4052         }
4053         else if(!cData->lvlOpts[lvlGiveVoice]
4054                 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4055         {
4056             if(mn->modes & MODE_CHANOP)
4057             {
4058                 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4059                 changes->args[used++].member = mn;
4060             }
4061             if(!(mn->modes & MODE_VOICE))
4062             {
4063                 changes->args[used].mode = MODE_VOICE;
4064                 changes->args[used++].member = mn;
4065             }
4066         }
4067         else
4068         {
4069             if(mn->modes)
4070             {
4071                 changes->args[used].mode = MODE_REMOVE | mn->modes;
4072                 changes->args[used++].member = mn;
4073             }
4074         }
4075     }
4076     changes->argc = used;
4077     modcmd_chanmode_announce(changes);
4078     mod_chanmode_free(changes);
4079     reply("CSMSG_RESYNCED_USERS", channel->name);
4080     return 1;
4081 }
4082
4083 static CHANSERV_FUNC(cmd_seen)
4084 {
4085     struct userData *uData;
4086     struct handle_info *handle;
4087     char seen[INTERVALLEN];
4088
4089     REQUIRE_PARAMS(2);
4090
4091     if(!irccasecmp(argv[1], chanserv->nick))
4092     {
4093         reply("CSMSG_IS_CHANSERV");
4094         return 1;
4095     }
4096
4097     if(!(handle = get_handle_info(argv[1])))
4098     {
4099         reply("MSG_HANDLE_UNKNOWN", argv[1]);
4100         return 0;
4101     }
4102
4103     if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4104     {
4105         reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4106         return 0;
4107     }
4108
4109     if(uData->present)
4110         reply("CSMSG_USER_PRESENT", handle->handle);
4111     else if(uData->seen)
4112         reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen));
4113     else
4114         reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4115
4116     if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4117         reply("CSMSG_USER_VACATION", handle->handle);
4118
4119     return 1;
4120 }
4121
4122 static MODCMD_FUNC(cmd_names)
4123 {
4124     struct userNode *targ;
4125     struct userData *targData;
4126     unsigned int ii, pos;
4127     char buf[400];
4128
4129     for(ii=pos=0; ii<channel->members.used; ++ii)
4130     {
4131         targ = channel->members.list[ii]->user;
4132         targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4133         if(!targData)
4134             continue;
4135         if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 6 > sizeof(buf))
4136         {
4137             buf[pos] = 0;
4138             reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4139             pos = 0;
4140         }
4141         buf[pos++] = ' ';
4142         if(IsUserSuspended(targData))
4143             buf[pos++] = 's';
4144         pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4145     }
4146     buf[pos] = 0;
4147     reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4148     reply("CSMSG_END_NAMES", channel->name);
4149     return 1;
4150 }
4151
4152 static int
4153 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4154 {
4155     switch(ntype->visible_type)
4156     {
4157     case NOTE_VIS_ALL: return 1;
4158     case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4159     case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4160     }
4161 }
4162
4163 static int
4164 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4165 {
4166     struct userData *uData;
4167
4168     switch(ntype->set_access_type)
4169     {
4170     case NOTE_SET_CHANNEL_ACCESS:
4171         if(!user->handle_info)
4172             return 0;
4173         if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4174             return 0;
4175         return uData->access >= ntype->set_access.min_ulevel;
4176     case NOTE_SET_CHANNEL_SETTER:
4177         return check_user_level(channel, user, lvlSetters, 1, 0);
4178     case NOTE_SET_PRIVILEGED: default:
4179         return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4180     }
4181 }
4182
4183 static CHANSERV_FUNC(cmd_note)
4184 {
4185     struct chanData *cData;
4186     struct note *note;
4187     struct note_type *ntype;
4188
4189     cData = channel->channel_info;
4190     if(!cData)
4191     {
4192         reply("CSMSG_NOT_REGISTERED", channel->name);
4193         return 0;
4194     }
4195
4196     /* If no arguments, show all visible notes for the channel. */
4197     if(argc < 2)
4198     {
4199         dict_iterator_t it;
4200         unsigned int count;
4201
4202         for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4203         {
4204             note = iter_data(it);
4205             if(!note_type_visible_to_user(cData, note->type, user))
4206                 continue;
4207             if(!count++)
4208                 reply("CSMSG_NOTELIST_HEADER", channel->name);
4209             reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4210         }
4211         if(count)
4212             reply("CSMSG_NOTELIST_END", channel->name);
4213         else
4214             reply("CSMSG_NOTELIST_EMPTY", channel->name);
4215     }
4216     /* If one argument, show the named note. */
4217     else if(argc == 2)
4218     {
4219         if((note = dict_find(cData->notes, argv[1], NULL))
4220            && note_type_visible_to_user(cData, note->type, user))
4221         {
4222             reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4223         }
4224         else if((ntype = dict_find(note_types, argv[1], NULL))
4225                 && note_type_visible_to_user(NULL, ntype, user))
4226         {
4227             reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4228             return 0;
4229         }
4230         else
4231         {
4232             reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4233             return 0;
4234         }
4235     }
4236     /* Assume they're trying to set a note. */
4237     else
4238     {
4239         char *note_text;
4240         ntype = dict_find(note_types, argv[1], NULL);
4241         if(!ntype)
4242         {
4243             reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4244             return 0;
4245         }
4246         else if(note_type_settable_by_user(channel, ntype, user))
4247         {
4248             note_text = unsplit_string(argv+2, argc-2, NULL);
4249             if((note = dict_find(cData->notes, argv[1], NULL)))
4250                 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4251             chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4252             reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4253
4254             if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4255             {
4256                 /* The note is viewable to staff only, so return 0
4257                    to keep the invocation from getting logged (or
4258                    regular users can see it in !events). */
4259                 return 0;
4260             }
4261         }
4262         else
4263         {
4264             reply("CSMSG_NO_ACCESS");
4265             return 0;
4266         }
4267     }
4268     return 1;
4269 }
4270
4271 static CHANSERV_FUNC(cmd_delnote)
4272 {
4273     struct note *note;
4274
4275     REQUIRE_PARAMS(2);
4276     if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4277        || !note_type_settable_by_user(channel, note->type, user))
4278     {
4279         reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4280         return 0;
4281     }
4282     dict_remove(channel->channel_info->notes, note->type->name);
4283     reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4284     return 1;
4285 }
4286
4287 static CHANSERV_FUNC(cmd_events)
4288 {
4289     struct logSearch discrim;
4290     struct logReport report;
4291     unsigned int matches, limit;
4292
4293     limit = (argc > 1) ? atoi(argv[1]) : 10;
4294     if(limit < 1 || limit > 200) limit = 10;
4295
4296     memset(&discrim, 0, sizeof(discrim));
4297     discrim.masks.bot = chanserv;
4298     discrim.masks.channel_name = channel->name;
4299     if(argc > 2) discrim.masks.command = argv[2];
4300     discrim.limit = limit;
4301     discrim.max_time = INT_MAX;
4302     discrim.severities = 1 << LOG_COMMAND;
4303     report.reporter = chanserv;
4304     report.user = user;
4305     reply("CSMSG_EVENT_SEARCH_RESULTS");
4306     matches = log_entry_search(&discrim, log_report_entry, &report);
4307     if(matches)
4308         reply("MSG_MATCH_COUNT", matches);
4309     else
4310         reply("MSG_NO_MATCHES");
4311     return 1;
4312 }
4313
4314 static CHANSERV_FUNC(cmd_say)
4315 {
4316     char *msg;
4317     if(channel)
4318     {
4319         REQUIRE_PARAMS(2);
4320         msg = unsplit_string(argv + 1, argc - 1, NULL);
4321         send_channel_message(channel, cmd->parent->bot, "%s", msg);
4322     }
4323     else if(GetUserH(argv[1]))
4324     {
4325         REQUIRE_PARAMS(3);
4326         msg = unsplit_string(argv + 2, argc - 2, NULL);
4327         send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4328     }
4329     else
4330     {
4331         reply("MSG_NOT_TARGET_NAME");
4332         return 0;
4333     }
4334     return 1;
4335 }
4336
4337 static CHANSERV_FUNC(cmd_emote)
4338 {
4339     char *msg;
4340     assert(argc >= 2);
4341     if(channel)
4342     {
4343         /* CTCP is so annoying. */
4344         msg = unsplit_string(argv + 1, argc - 1, NULL);
4345         send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4346     }
4347     else if(GetUserH(argv[1]))
4348     {
4349         msg = unsplit_string(argv + 2, argc - 2, NULL);
4350         send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4351     }
4352     else
4353     {
4354         reply("MSG_NOT_TARGET_NAME");
4355         return 0;
4356     }
4357     return 1;
4358 }
4359
4360 struct channelList *
4361 chanserv_support_channels(void)
4362 {
4363     return &chanserv_conf.support_channels;
4364 }
4365
4366 static CHANSERV_FUNC(cmd_expire)
4367 {
4368     int channel_count = registered_channels;
4369     expire_channels(NULL);
4370     reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4371     return 1;
4372 }
4373
4374 static void
4375 chanserv_expire_suspension(void *data)
4376 {
4377     struct suspended *suspended = data;
4378     struct chanNode *channel;
4379     struct mod_chanmode change;
4380
4381     if(!suspended->expires || (now < suspended->expires))
4382         suspended->revoked = now;
4383     channel = suspended->cData->channel;
4384     suspended->cData->channel = channel;
4385     suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4386     mod_chanmode_init(&change);
4387     change.argc = 1;
4388     change.args[0].mode = MODE_CHANOP;
4389     change.args[0].member = AddChannelUser(chanserv, channel);
4390     mod_chanmode_announce(chanserv, channel, &change);
4391 }
4392
4393 static CHANSERV_FUNC(cmd_csuspend)
4394 {
4395     struct suspended *suspended;
4396     char reason[MAXLEN];
4397     time_t expiry, duration;
4398     struct userData *uData;
4399
4400     REQUIRE_PARAMS(3);
4401
4402     if(IsProtected(channel->channel_info))
4403     {
4404         reply("CSMSG_SUSPEND_NODELETE", channel->name);
4405         return 0;
4406     }
4407
4408     if(argv[1][0] == '!')
4409         argv[1]++;
4410     else if(IsSuspended(channel->channel_info))
4411     {
4412         reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4413         show_suspension_info(cmd, user, channel->channel_info->suspended);
4414         return 0;
4415     }
4416
4417     if(!strcmp(argv[1], "0"))
4418         expiry = 0;
4419     else if((duration = ParseInterval(argv[1])))
4420         expiry = now + duration;
4421     else
4422     {
4423         reply("MSG_INVALID_DURATION", argv[1]);
4424         return 0;
4425     }
4426
4427     unsplit_string(argv + 2, argc - 2, reason);
4428
4429     suspended = calloc(1, sizeof(*suspended));
4430     suspended->revoked = 0;
4431     suspended->issued = now;
4432     suspended->suspender = strdup(user->handle_info->handle);
4433     suspended->expires = expiry;
4434     suspended->reason = strdup(reason);
4435     suspended->cData = channel->channel_info;
4436     suspended->previous = suspended->cData->suspended;
4437     suspended->cData->suspended = suspended;
4438
4439     if(suspended->expires)
4440         timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4441
4442     if(IsSuspended(channel->channel_info))
4443     {
4444         suspended->previous->revoked = now;
4445         if(suspended->previous->expires)
4446             timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4447         sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4448         global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4449     }
4450     else
4451     {
4452         /* Mark all users in channel as absent. */
4453         for(uData = channel->channel_info->users; uData; uData = uData->next)
4454         {
4455             if(uData->present)
4456             {
4457                 uData->seen = now;
4458                 uData->present = 0;
4459             }
4460         }
4461
4462         /* Mark the channel as suspended, then part. */
4463         channel->channel_info->flags |= CHANNEL_SUSPENDED;
4464         DelChannelUser(chanserv, channel, suspended->reason, 0);
4465         reply("CSMSG_SUSPENDED", channel->name);
4466         sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4467         global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4468     }
4469     return 1;
4470 }
4471
4472 static CHANSERV_FUNC(cmd_cunsuspend)
4473 {
4474     struct suspended *suspended;
4475     char message[MAXLEN];
4476
4477     if(!IsSuspended(channel->channel_info))
4478     {
4479         reply("CSMSG_NOT_SUSPENDED", channel->name);
4480         return 0;
4481     }
4482
4483     suspended = channel->channel_info->suspended;
4484
4485     /* Expire the suspension and join ChanServ to the channel. */
4486     timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4487     chanserv_expire_suspension(suspended);
4488     reply("CSMSG_UNSUSPENDED", channel->name);
4489     sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4490     global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4491     return 1;
4492 }
4493
4494 typedef struct chanservSearch
4495 {
4496     char *name;
4497     char *registrar;
4498
4499     time_t unvisited;
4500     time_t registered;
4501
4502     unsigned long flags;
4503     unsigned int limit;
4504 } *search_t;
4505
4506 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4507
4508 static search_t
4509 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4510 {
4511     search_t search;
4512     unsigned int i;
4513
4514     search = malloc(sizeof(struct chanservSearch));
4515     memset(search, 0, sizeof(*search));
4516     search->limit = 25;
4517
4518     for(i = 0; i < argc; i++)
4519     {
4520         /* Assume all criteria require arguments. */
4521         if(i == (argc - 1))
4522         {
4523             send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4524             goto fail;
4525         }
4526
4527         if(!irccasecmp(argv[i], "name"))
4528             search->name = argv[++i];
4529         else if(!irccasecmp(argv[i], "registrar"))
4530             search->registrar = argv[++i];
4531         else if(!irccasecmp(argv[i], "unvisited"))
4532             search->unvisited = ParseInterval(argv[++i]);
4533         else if(!irccasecmp(argv[i], "registered"))
4534             search->registered = ParseInterval(argv[++i]);
4535         else if(!irccasecmp(argv[i], "flags"))
4536         {
4537             i++;
4538             if(!irccasecmp(argv[i], "nodelete"))
4539                 search->flags |= CHANNEL_NODELETE;
4540             else if(!irccasecmp(argv[i], "suspended"))
4541                 search->flags |= CHANNEL_SUSPENDED;
4542             else
4543             {
4544                 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4545                 goto fail;
4546             }
4547         }
4548         else if(!irccasecmp(argv[i], "limit"))
4549             search->limit = strtoul(argv[++i], NULL, 10);
4550         else
4551         {
4552             send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4553             goto fail;
4554         }
4555     }
4556
4557     if(search->name && !strcmp(search->name, "*"))
4558         search->name = 0;
4559     if(search->registrar && !strcmp(search->registrar, "*"))
4560         search->registrar = 0;
4561
4562     return search;
4563   fail:
4564     free(search);
4565     return NULL;
4566 }
4567
4568 static int
4569 chanserv_channel_match(struct chanData *channel, search_t search)
4570 {
4571     const char *name = channel->channel->name;
4572     if((search->name && !match_ircglob(name, search->name)) ||
4573        (search->registrar && !channel->registrar) ||
4574        (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4575        (search->unvisited && (now - channel->visited) < search->unvisited) ||
4576        (search->registered && (now - channel->registered) > search->registered) ||
4577        (search->flags && ((search->flags & channel->flags) != search->flags)))
4578         return 0;
4579
4580     return 1;
4581 }
4582
4583 static unsigned int
4584 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4585 {
4586     struct chanData *channel;
4587     unsigned int matches = 0;
4588
4589     for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4590     {
4591         if(!chanserv_channel_match(channel, search))
4592             continue;
4593         matches++;
4594         smf(channel, data);
4595     }
4596
4597     return matches;
4598 }
4599
4600 static void
4601 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4602 {
4603 }
4604
4605 static void
4606 search_print(struct chanData *channel, void *data)
4607 {
4608     send_message_type(4, data, chanserv, "%s", channel->channel->name);
4609 }
4610
4611 static CHANSERV_FUNC(cmd_search)
4612 {
4613     search_t search;
4614     unsigned int matches;
4615     channel_search_func action;
4616
4617     REQUIRE_PARAMS(3);
4618
4619     if(!irccasecmp(argv[1], "count"))
4620         action = search_count;
4621     else if(!irccasecmp(argv[1], "print"))
4622         action = search_print;
4623     else
4624     {
4625         reply("CSMSG_ACTION_INVALID", argv[1]);
4626         return 0;
4627     }
4628
4629     search = chanserv_search_create(user, argc - 2, argv + 2);
4630     if(!search)
4631         return 0;
4632
4633     if(action == search_count)
4634         search->limit = INT_MAX;
4635
4636     if(action == search_print)
4637         reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4638
4639     matches = chanserv_channel_search(search, action, user);
4640
4641     if(matches)
4642         reply("MSG_MATCH_COUNT", matches);
4643     else
4644         reply("MSG_NO_MATCHES");
4645
4646     free(search);
4647     return 1;
4648 }
4649
4650 static CHANSERV_FUNC(cmd_unvisited)
4651 {
4652     struct chanData *cData;
4653     time_t interval = chanserv_conf.channel_expire_delay;
4654     char buffer[INTERVALLEN];
4655     unsigned int limit = 25, matches = 0;
4656
4657     if(argc > 1)
4658     {
4659         interval = ParseInterval(argv[1]);
4660         if(argc > 2)
4661             limit = atoi(argv[2]);
4662     }
4663
4664     intervalString(buffer, interval);
4665     reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4666
4667     for(cData = channelList; cData && matches < limit; cData = cData->next)
4668     {
4669         if((now - cData->visited) < interval)
4670             continue;
4671
4672         intervalString(buffer, now - cData->visited);
4673         reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4674         matches++;
4675     }
4676
4677     return 1;
4678 }
4679
4680 static MODCMD_FUNC(chan_opt_defaulttopic)
4681 {
4682     if(argc > 1)
4683     {
4684         char *topic;
4685
4686         if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4687         {
4688             reply("CSMSG_TOPIC_LOCKED", channel->name);
4689             return 0;
4690         }
4691
4692         topic = unsplit_string(argv+1, argc-1, NULL);
4693
4694         free(channel->channel_info->topic);
4695         if(topic[0] == '*' && topic[1] == 0)
4696         {
4697             topic = channel->channel_info->topic = NULL;
4698         }
4699         else
4700         {
4701             topic = channel->channel_info->topic = strdup(topic);
4702             if(channel->channel_info->topic_mask
4703                && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4704                 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4705         }
4706         SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4707     }
4708
4709     if(channel->channel_info->topic)
4710         reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4711     else
4712         reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4713     return 1;
4714 }
4715
4716 static MODCMD_FUNC(chan_opt_topicmask)
4717 {
4718     if(argc > 1)
4719     {
4720         struct chanData *cData = channel->channel_info;
4721         char *mask;
4722
4723         if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4724         {
4725             reply("CSMSG_TOPIC_LOCKED", channel->name);
4726             return 0;
4727         }
4728
4729         mask = unsplit_string(argv+1, argc-1, NULL);
4730
4731         if(cData->topic_mask)
4732             free(cData->topic_mask);
4733         if(mask[0] == '*' && mask[1] == 0)
4734         {
4735             cData->topic_mask = 0;
4736         }
4737         else
4738         {
4739             cData->topic_mask = strdup(mask);
4740             if(!cData->topic)
4741                 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4742             else if(!match_ircglob(cData->topic, cData->topic_mask))
4743                 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4744         }
4745     }
4746
4747     if(channel->channel_info->topic_mask)
4748         reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4749     else
4750         reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4751     return 1;
4752 }
4753
4754 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4755 {
4756     if(argc > 1)
4757     {
4758         char *greeting = unsplit_string(argv+1, argc-1, NULL);
4759         char *previous;
4760
4761         previous = *data;
4762         if(greeting[0] == '*' && greeting[1] == 0)
4763             *data = NULL;
4764         else
4765         {
4766             unsigned int length = strlen(greeting);
4767             if(length > chanserv_conf.greeting_length)
4768             {
4769                 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4770                 return 0;
4771             }
4772             *data = strdup(greeting);
4773         }
4774         if(previous)
4775             free(previous);
4776     }
4777
4778     if(*data)
4779         reply(name, *data);
4780     else
4781         reply(name, user_find_message(user, "MSG_NONE"));
4782     return 1;
4783 }
4784
4785 static MODCMD_FUNC(chan_opt_greeting)
4786 {
4787     return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4788 }
4789
4790 static MODCMD_FUNC(chan_opt_usergreeting)
4791 {
4792     return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4793 }
4794
4795 static MODCMD_FUNC(chan_opt_modes)
4796 {
4797     struct mod_chanmode *new_modes;
4798     char modes[MODELEN];
4799
4800     if(argc > 1)
4801     {
4802         if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4803         {
4804             reply("CSMSG_NO_ACCESS");
4805             return 0;
4806         }
4807         if(argv[1][0] == '*' && argv[1][1] == 0)
4808         {
4809             memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4810         }
4811         else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE)))
4812         {
4813             reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4814             return 0;
4815         }
4816         else if(new_modes->argc > 1)
4817         {
4818             reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4819             mod_chanmode_free(new_modes);
4820             return 0;
4821         }
4822         else
4823         {
4824             channel->channel_info->modes = *new_modes;
4825             modcmd_chanmode_announce(new_modes);
4826             mod_chanmode_free(new_modes);
4827         }
4828     }
4829
4830     mod_chanmode_format(&channel->channel_info->modes, modes);
4831     if(modes[0])
4832         reply("CSMSG_SET_MODES", modes);
4833     else
4834         reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4835     return 1;
4836 }
4837
4838 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4839 static int
4840 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4841 {
4842     struct chanData *cData = channel->channel_info;
4843     int value;
4844
4845     if(argc > 1)
4846     {
4847         /* Set flag according to value. */
4848         if(enabled_string(argv[1]))
4849         {
4850             cData->flags |= mask;
4851             value = 1;
4852         }
4853         else if(disabled_string(argv[1]))
4854         {
4855             cData->flags &= ~mask;
4856             value = 0;
4857         }
4858         else
4859         {
4860             reply("MSG_INVALID_BINARY", argv[1]);
4861             return 0;
4862         }
4863     }
4864     else
4865     {
4866         /* Find current option value. */
4867         value = (cData->flags & mask) ? 1 : 0;
4868     }
4869
4870     if(value)
4871         reply(name, user_find_message(user, "MSG_ON"));
4872     else
4873         reply(name, user_find_message(user, "MSG_OFF"));
4874     return 1;
4875 }
4876
4877 static MODCMD_FUNC(chan_opt_nodelete)
4878 {
4879     if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4880     {
4881         reply("MSG_SETTING_PRIVILEGED", argv[0]);
4882         return 0;
4883     }
4884
4885     CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4886 }
4887
4888 static MODCMD_FUNC(chan_opt_dynlimit)
4889 {
4890     CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4891 }
4892
4893 static MODCMD_FUNC(chan_opt_defaults)
4894 {
4895     struct userData *uData;
4896     struct chanData *cData;
4897     const char *confirm;
4898     enum levelOption lvlOpt;
4899     enum charOption chOpt;
4900
4901     cData = channel->channel_info;
4902     uData = GetChannelUser(cData, user->handle_info);
4903     if(!uData || (uData->access < UL_OWNER))
4904     {
4905         reply("CSMSG_OWNER_DEFAULTS", channel->name);
4906         return 0;
4907     }
4908     confirm = make_confirmation_string(uData);
4909     if((argc < 2) || strcmp(argv[1], confirm))
4910     {
4911         reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
4912         return 0;
4913     }
4914     cData->flags = CHANNEL_DEFAULT_FLAGS;
4915     cData->modes = chanserv_conf.default_modes;
4916     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
4917         cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
4918     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
4919         cData->chOpts[chOpt] = charOptions[chOpt].default_value;
4920     reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
4921     return 1;
4922 }
4923
4924 static int
4925 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4926 {
4927     struct chanData *cData = channel->channel_info;
4928     struct userData *uData;
4929     unsigned short value;
4930
4931     if(argc > 1)
4932     {
4933         if(!check_user_level(channel, user, option, 1, 1))
4934         {
4935             reply("CSMSG_CANNOT_SET");
4936             return 0;
4937         }
4938         value = user_level_from_name(argv[1], UL_OWNER+1);
4939         if(!value && strcmp(argv[1], "0"))
4940         {
4941             reply("CSMSG_INVALID_ACCESS", argv[1]);
4942             return 0;
4943         }
4944         uData = GetChannelUser(cData, user->handle_info);
4945         if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
4946         {
4947             reply("CSMSG_BAD_SETLEVEL");
4948             return 0;
4949         }
4950         switch(option)
4951         {
4952         case lvlGiveVoice:
4953             if(value > cData->lvlOpts[lvlGiveOps])
4954             {
4955                 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
4956                 return 0;
4957             }
4958             break;
4959         case lvlGiveOps:
4960             if(value < cData->lvlOpts[lvlGiveVoice])
4961             {
4962                 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
4963                 return 0;
4964             }
4965             break;
4966         case lvlSetters:
4967             /* This test only applies to owners, since non-owners
4968              * trying to set an option to above their level get caught
4969              * by the CSMSG_BAD_SETLEVEL test above.
4970              */
4971             if(value > uData->access)
4972             {
4973                 reply("CSMSG_BAD_SETTERS");
4974                 return 0;
4975             }
4976             break;
4977         default:
4978             break;
4979         }
4980         cData->lvlOpts[option] = value;
4981     }
4982     reply(levelOptions[option].format_name, cData->lvlOpts[option]);
4983     return argc > 1;
4984 }
4985
4986 static MODCMD_FUNC(chan_opt_enfops)
4987 {
4988     return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
4989 }
4990
4991 static MODCMD_FUNC(chan_opt_giveops)
4992 {
4993     return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
4994 }
4995
4996 static MODCMD_FUNC(chan_opt_enfmodes)
4997 {
4998     return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
4999 }
5000
5001 static MODCMD_FUNC(chan_opt_enftopic)
5002 {
5003     return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5004 }
5005
5006 static MODCMD_FUNC(chan_opt_pubcmd)
5007 {
5008     return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5009 }
5010
5011 static MODCMD_FUNC(chan_opt_setters)
5012 {
5013     return channel_level_option(lvlSetters, CSFUNC_ARGS);
5014 }
5015
5016 static MODCMD_FUNC(chan_opt_ctcpusers)
5017 {
5018     return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5019 }
5020
5021 static MODCMD_FUNC(chan_opt_userinfo)
5022 {
5023     return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5024 }
5025
5026 static MODCMD_FUNC(chan_opt_givevoice)
5027 {
5028     return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5029 }
5030
5031 static MODCMD_FUNC(chan_opt_topicsnarf)
5032 {
5033     return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5034 }
5035
5036 static MODCMD_FUNC(chan_opt_inviteme)
5037 {
5038     return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5039 }
5040
5041 static int
5042 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5043 {
5044     struct chanData *cData = channel->channel_info;
5045     int count = charOptions[option].count, index;
5046
5047     if(argc > 1)
5048     {
5049         index = atoi(argv[1]);
5050
5051         if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5052         {
5053             reply("CSMSG_INVALID_NUMERIC", index);
5054             /* Show possible values. */
5055             for(index = 0; index < count; index++)
5056                 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5057             return 0;
5058         }
5059
5060         cData->chOpts[option] = charOptions[option].values[index].value;
5061     }
5062     else
5063     {
5064         /* Find current option value. */
5065       find_value:
5066         for(index = 0;
5067             (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5068             index++);
5069         if(index == count)
5070         {
5071             /* Somehow, the option value is corrupt; reset it to the default. */
5072             cData->chOpts[option] = charOptions[option].default_value;
5073             goto find_value;
5074         }
5075     }
5076
5077     reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5078     return 1;
5079 }
5080
5081 static MODCMD_FUNC(chan_opt_protect)
5082 {
5083     return channel_multiple_option(chProtect, CSFUNC_ARGS);
5084 }
5085
5086 static MODCMD_FUNC(chan_opt_toys)
5087 {
5088     return channel_multiple_option(chToys, CSFUNC_ARGS);
5089 }
5090
5091 static MODCMD_FUNC(chan_opt_ctcpreaction)
5092 {
5093     return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5094 }
5095
5096 static MODCMD_FUNC(chan_opt_topicrefresh)
5097 {
5098     return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5099 }
5100
5101 static struct svccmd_list set_shows_list;
5102
5103 static void
5104 handle_svccmd_unbind(struct svccmd *target) {
5105     unsigned int ii;
5106     for(ii=0; ii<set_shows_list.used; ++ii)
5107         if(target == set_shows_list.list[ii])
5108             set_shows_list.used = 0;
5109 }
5110
5111 static CHANSERV_FUNC(cmd_set)
5112 {
5113     struct svccmd *subcmd;
5114     char buf[MAXLEN];
5115     unsigned int ii;
5116
5117     /* Check if we need to (re-)initialize set_shows_list. */
5118     if(!set_shows_list.used)
5119     {
5120         if(!set_shows_list.size)
5121         {
5122             set_shows_list.size = chanserv_conf.set_shows->used;
5123             set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5124         }
5125         for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5126         {
5127             const char *name = chanserv_conf.set_shows->list[ii];
5128             sprintf(buf, "%s %s", argv[0], name);
5129             subcmd = dict_find(cmd->parent->commands, buf, NULL);
5130             if(!subcmd)
5131             {
5132                 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5133                 continue;
5134             }
5135             svccmd_list_append(&set_shows_list, subcmd);
5136         }
5137     }
5138
5139     if(argc < 2)
5140     {
5141         reply("CSMSG_CHANNEL_OPTIONS");
5142         for(ii = 0; ii < set_shows_list.used; ii++)
5143         {
5144             subcmd = set_shows_list.list[ii];
5145             subcmd->command->func(user, channel, 1, argv+1, subcmd);
5146         }
5147         return 1;
5148     }
5149
5150     sprintf(buf, "%s %s", argv[0], argv[1]);
5151     subcmd = dict_find(cmd->parent->commands, buf, NULL);
5152     if(!subcmd)
5153     {
5154         reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5155         return 0;
5156     }
5157     if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5158     {
5159         reply("CSMSG_NO_ACCESS");
5160         return 0;
5161     }
5162
5163     return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5164 }
5165
5166 static int
5167 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5168 {
5169     struct userData *uData;
5170
5171     uData = GetChannelAccess(channel->channel_info, user->handle_info);
5172     if(!uData)
5173     {
5174         reply("CSMSG_NOT_USER", channel->name);
5175         return 0;
5176     }
5177
5178     if(argc < 2)
5179     {
5180         /* Just show current option value. */
5181     }
5182     else if(enabled_string(argv[1]))
5183     {
5184         uData->flags |= mask;
5185     }
5186     else if(disabled_string(argv[1]))
5187     {
5188         uData->flags &= ~mask;
5189     }
5190     else
5191     {
5192         reply("MSG_INVALID_BINARY", argv[1]);
5193         return 0;
5194     }
5195
5196     reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5197     return 1;
5198 }
5199
5200 static MODCMD_FUNC(user_opt_noautoop)
5201 {
5202     struct userData *uData;
5203
5204     uData = GetChannelAccess(channel->channel_info, user->handle_info);
5205     if(!uData)
5206     {
5207         reply("CSMSG_NOT_USER", channel->name);
5208         return 0;
5209     }
5210     if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5211         return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5212     else
5213         return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5214 }
5215
5216 static MODCMD_FUNC(user_opt_autoinvite)
5217 {
5218     return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5219 }
5220
5221 static MODCMD_FUNC(user_opt_info)
5222 {
5223     struct userData *uData;
5224     char *infoline;
5225
5226     uData = GetChannelAccess(channel->channel_info, user->handle_info);
5227
5228     if(!uData)
5229     {
5230         /* If they got past the command restrictions (which require access)
5231          * but fail this test, we have some fool with security override on.
5232          */
5233         reply("CSMSG_NOT_USER", channel->name);
5234         return 0;
5235     }
5236
5237     if(argc > 1)
5238     {
5239         infoline = unsplit_string(argv + 1, argc - 1, NULL);
5240         if(uData->info)
5241             free(uData->info);
5242         if(infoline[0] == '*' && infoline[1] == 0)
5243             uData->info = NULL;
5244         else
5245             uData->info = strdup(infoline);
5246     }
5247     if(uData->info)
5248         reply("CSMSG_USET_INFO", uData->info);
5249     else
5250         reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5251     return 1;
5252 }
5253
5254 struct svccmd_list uset_shows_list;
5255
5256 static CHANSERV_FUNC(cmd_uset)
5257 {
5258     struct svccmd *subcmd;
5259     char buf[MAXLEN];
5260     unsigned int ii;
5261
5262     /* Check if we need to (re-)initialize uset_shows_list. */
5263     if(!uset_shows_list.used)
5264     {
5265         char *options[] =
5266         {
5267             "NoAutoOp", "AutoInvite", "Info"
5268         };
5269
5270         if(!uset_shows_list.size)
5271         {
5272             uset_shows_list.size = ArrayLength(options);
5273             uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5274         }
5275         for(ii = 0; ii < ArrayLength(options); ii++)
5276         {
5277             const char *name = options[ii];
5278             sprintf(buf, "%s %s", argv[0], name);
5279             subcmd = dict_find(cmd->parent->commands, buf, NULL);
5280             if(!subcmd)
5281             {
5282                 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5283                 continue;
5284             }
5285             svccmd_list_append(&uset_shows_list, subcmd);
5286         }
5287     }
5288
5289     if(argc < 2)
5290     {
5291         /* Do this so options are presented in a consistent order. */
5292         reply("CSMSG_USER_OPTIONS");
5293         for(ii = 0; ii < uset_shows_list.used; ii++)
5294             uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5295         return 1;
5296     }
5297
5298     sprintf(buf, "%s %s", argv[0], argv[1]);
5299     subcmd = dict_find(cmd->parent->commands, buf, NULL);
5300     if(!subcmd)
5301     {
5302         reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5303         return 0;
5304     }
5305
5306     return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5307 }
5308
5309 static CHANSERV_FUNC(cmd_giveownership)
5310 {
5311     struct handle_info *new_owner_hi;
5312     struct userData *new_owner, *curr_user;
5313     struct chanData *cData = channel->channel_info;
5314     struct do_not_register *dnr;
5315     unsigned int force;
5316     unsigned short co_access;
5317     char reason[MAXLEN];
5318
5319     REQUIRE_PARAMS(2);
5320     curr_user = GetChannelAccess(cData, user->handle_info);
5321     force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5322     if(!curr_user || (curr_user->access != UL_OWNER))
5323     {
5324         struct userData *owner = NULL;
5325         for(curr_user = channel->channel_info->users;
5326             curr_user;
5327             curr_user = curr_user->next)
5328         {
5329             if(curr_user->access != UL_OWNER)
5330                 continue;
5331             if(owner)
5332             {
5333                 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5334                 return 0;
5335             }
5336             owner = curr_user;
5337         }
5338         curr_user = owner;
5339     }
5340     if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5341         return 0;
5342     if(new_owner_hi == user->handle_info)
5343     {
5344         reply("CSMSG_NO_TRANSFER_SELF");
5345         return 0;
5346     }
5347     new_owner = GetChannelAccess(cData, new_owner_hi);
5348     if(!new_owner)
5349     {
5350         reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5351         return 0;
5352     }
5353     if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5354     {
5355         reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5356         return 0;
5357     }
5358     if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5359         if(!IsHelping(user))
5360             reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5361         else
5362             chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5363         return 0;
5364     }
5365     if(new_owner->access >= UL_COOWNER)
5366         co_access = new_owner->access;
5367     else
5368         co_access = UL_COOWNER;
5369     new_owner->access = UL_OWNER;
5370     if(curr_user)
5371         curr_user->access = co_access;
5372     reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5373     sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5374     global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5375     return 1;
5376 }
5377
5378 static CHANSERV_FUNC(cmd_suspend)
5379 {
5380     struct handle_info *hi;
5381     struct userData *self, *target;
5382
5383     REQUIRE_PARAMS(2);
5384     if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5385     self = GetChannelUser(channel->channel_info, user->handle_info);
5386     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5387     {
5388         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5389         return 0;
5390     }
5391     if(target->access >= self->access)
5392     {
5393         reply("MSG_USER_OUTRANKED", hi->handle);
5394         return 0;
5395     }
5396     if(target->flags & USER_SUSPENDED)
5397     {
5398         reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5399         return 0;
5400     }
5401     if(target->present)
5402     {
5403         target->present = 0;
5404         target->seen = now;
5405     }
5406     target->flags |= USER_SUSPENDED;
5407     reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5408     return 1;
5409 }
5410
5411 static CHANSERV_FUNC(cmd_unsuspend)
5412 {
5413     struct handle_info *hi;
5414     struct userData *self, *target;
5415
5416     REQUIRE_PARAMS(2);
5417     if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5418     self = GetChannelUser(channel->channel_info, user->handle_info);
5419     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5420     {
5421         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5422         return 0;
5423     }
5424     if(target->access >= self->access)
5425     {
5426         reply("MSG_USER_OUTRANKED", hi->handle);
5427         return 0;
5428     }
5429     if(!(target->flags & USER_SUSPENDED))
5430     {
5431         reply("CSMSG_NOT_SUSPENDED", hi->handle);
5432         return 0;
5433     }
5434     target->flags &= ~USER_SUSPENDED;
5435     reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5436     return 1;
5437 }
5438
5439 static MODCMD_FUNC(cmd_deleteme)
5440 {
5441     struct handle_info *hi;
5442     struct userData *target;
5443     const char *confirm_string;
5444     unsigned short access;
5445     char *channel_name;
5446
5447     hi = user->handle_info;
5448     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5449     {
5450         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5451         return 0;
5452     }
5453     if(target->access == UL_OWNER)
5454     {
5455         reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5456         return 0;
5457     }
5458     confirm_string = make_confirmation_string(target);
5459     if((argc < 2) || strcmp(argv[1], confirm_string))
5460     {
5461         reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5462         return 0;
5463     }
5464     access = target->access;
5465     channel_name = strdup(channel->name);
5466     del_channel_user(target, 1);
5467     reply("CSMSG_DELETED_YOU", access, channel_name);
5468     free(channel_name);
5469     return 1;
5470 }
5471
5472 static void
5473 chanserv_refresh_topics(UNUSED_ARG(void *data))
5474 {
5475     unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5476     struct chanData *cData;
5477     char opt;
5478
5479     for(cData = channelList; cData; cData = cData->next)
5480     {
5481         if(IsSuspended(cData))
5482             continue;
5483         opt = cData->chOpts[chTopicRefresh];
5484         if(opt == 'n')
5485             continue;
5486         if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5487             continue;
5488         if(cData->topic)
5489             SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5490         cData->last_refresh = refresh_num;
5491     }
5492     timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5493 }
5494
5495 static CHANSERV_FUNC(cmd_unf)
5496 {
5497     if(channel)
5498     {
5499         char response[MAXLEN];
5500         const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5501         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
5502         irc_privmsg(cmd->parent->bot, channel->name, response);
5503     }
5504     else
5505         reply("CSMSG_UNF_RESPONSE");
5506     return 1;
5507 }
5508
5509 static CHANSERV_FUNC(cmd_ping)
5510 {
5511     if(channel)
5512     {
5513         char response[MAXLEN];
5514         const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5515         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
5516         irc_privmsg(cmd->parent->bot, channel->name, response);
5517     }
5518     else
5519         reply("CSMSG_PING_RESPONSE");
5520     return 1;
5521 }
5522
5523 static CHANSERV_FUNC(cmd_wut)
5524 {
5525     if(channel)
5526     {
5527         char response[MAXLEN];
5528         const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5529         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
5530         irc_privmsg(cmd->parent->bot, channel->name, response);
5531     }
5532     else
5533         reply("CSMSG_WUT_RESPONSE");
5534     return 1;
5535 }
5536
5537 static CHANSERV_FUNC(cmd_8ball)
5538 {
5539     unsigned int i, j, accum;
5540     const char *resp;
5541
5542     REQUIRE_PARAMS(2);
5543     accum = 0;
5544     for(i=1; i<argc; i++)
5545         for(j=0; argv[i][j]; j++)
5546             accum = (accum << 5) - accum + toupper(argv[i][j]);
5547     resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5548     if(channel)
5549     {
5550         char response[MAXLEN];
5551         sprintf(response, "\ 2%s\ 2: %s", user->nick, resp);
5552         irc_privmsg(cmd->parent->bot, channel->name, response);
5553     }
5554     else
5555         send_message_type(4, user, cmd->parent->bot, "%s", resp);
5556     return 1;
5557 }
5558
5559 static CHANSERV_FUNC(cmd_d)
5560 {
5561     unsigned long sides, count, modifier, ii, total;
5562     char response[MAXLEN], *sep;
5563     const char *fmt;
5564
5565     REQUIRE_PARAMS(2);
5566     if((count = strtoul(argv[1], &sep, 10)) < 1)
5567         goto no_dice;
5568     if(sep[0] == 0)
5569     {
5570         if(count == 1)
5571             goto no_dice;
5572         sides = count;
5573         count = 1;
5574         modifier = 0;
5575     }
5576     else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5577             && (sides = strtoul(sep+1, &sep, 10)) > 1)
5578     {
5579         if(sep[0] == 0)
5580             modifier = 0;
5581         else if((sep[0] == '-') && isdigit(sep[1]))
5582             modifier = strtoul(sep, NULL, 10);
5583         else if((sep[0] == '+') && isdigit(sep[1]))
5584             modifier = strtoul(sep+1, NULL, 10);
5585         else
5586             goto no_dice;
5587     }
5588     else
5589     {
5590       no_dice:
5591         reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5592         return 0;
5593     }
5594     if(count > 10)
5595     {
5596         reply("CSMSG_BAD_DICE_COUNT", count, 10);
5597         return 0;
5598     }
5599     for(total = ii = 0; ii < count; ++ii)
5600         total += (rand() % sides) + 1;
5601     total += modifier;
5602
5603     if((count > 1) || modifier)
5604     {
5605         fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5606         sprintf(response, fmt, total, count, sides, modifier);
5607     }
5608     else
5609     {
5610         fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5611         sprintf(response, fmt, total, sides);
5612     }
5613     if(channel)
5614         send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5615     else
5616         send_message_type(4, user, cmd->parent->bot, "%s", response);
5617     return 1;
5618 }
5619
5620 static CHANSERV_FUNC(cmd_huggle)
5621 {
5622     /* CTCP must be via PRIVMSG, never notice */
5623     if(channel)
5624         send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5625     else
5626         send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5627     return 1;
5628 }
5629
5630 static void
5631 chanserv_adjust_limit(void *data)
5632 {
5633     struct mod_chanmode change;
5634     struct chanData *cData = data;
5635     struct chanNode *channel = cData->channel;
5636     unsigned int limit;
5637
5638     if(IsSuspended(cData))
5639         return;
5640
5641     cData->limitAdjusted = now;
5642     limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5643     if(cData->modes.modes_set & MODE_LIMIT)
5644     {
5645         if(limit > cData->modes.new_limit)
5646             limit = cData->modes.new_limit;
5647         else if(limit == cData->modes.new_limit)
5648             return;
5649     }
5650
5651     mod_chanmode_init(&change);
5652     change.modes_set = MODE_LIMIT;
5653     change.new_limit = limit;
5654     mod_chanmode_announce(chanserv, channel, &change);
5655 }
5656
5657 static void
5658 handle_new_channel(struct chanNode *channel)
5659 {
5660     struct chanData *cData;
5661
5662     if(!(cData = channel->channel_info))
5663         return;
5664
5665     if(cData->modes.modes_set || cData->modes.modes_clear)
5666         mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5667
5668     if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5669         SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5670 }
5671
5672 /* Welcome to my worst nightmare. Warning: Read (or modify)
5673    the code below at your own risk. */
5674 static int
5675 handle_join(struct modeNode *mNode)
5676 {
5677     struct mod_chanmode change;
5678     struct userNode *user = mNode->user;
5679     struct chanNode *channel = mNode->channel;
5680     struct chanData *cData;
5681     struct userData *uData = NULL;
5682     struct banData *bData;
5683     struct handle_info *handle;
5684     unsigned int modes = 0, info = 0;
5685     char *greeting;
5686
5687     if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5688         return 0;
5689
5690     cData = channel->channel_info;
5691     if(channel->members.used > cData->max)
5692         cData->max = channel->members.used;
5693
5694     /* Check for bans.  If they're joining through a ban, one of two
5695      * cases applies:
5696      *   1: Join during a netburst, by riding the break.  Kick them
5697      *      unless they have ops or voice in the channel.
5698      *   2: They're allowed to join through the ban (an invite in
5699      *   ircu2.10, or a +e on Hybrid, or something).
5700      * If they're not joining through a ban, and the banlist is not
5701      * full, see if they're on the banlist for the channel.  If so,
5702      * kickban them.
5703      */
5704     if(user->uplink->burst && !mNode->modes)
5705     {
5706         unsigned int ii;
5707         for(ii = 0; ii < channel->banlist.used; ii++)
5708         {
5709             if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5710             {
5711                 /* Riding a netburst.  Naughty. */
5712                 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5713                 return 1;
5714             }
5715         }
5716     }
5717
5718     mod_chanmode_init(&change);
5719     change.argc = 1;
5720     if(channel->banlist.used < MAXBANS)
5721     {
5722         /* Not joining through a ban. */
5723         for(bData = cData->bans;
5724                 bData && !user_matches_glob(user, bData->mask, 1);
5725                 bData = bData->next);
5726
5727         if(bData)
5728         {
5729             char kick_reason[MAXLEN];
5730             sprintf(kick_reason, "%s (%s)", bData->reason, bData->owner);
5731
5732             bData->triggered = now;
5733             if(bData != cData->bans)
5734             {
5735                 /* Shuffle the ban to the head of the list. */
5736                 if(bData->next)
5737                     bData->next->prev = bData->prev;
5738                 if(bData->prev)
5739                     bData->prev->next = bData->next;
5740
5741                 bData->prev = NULL;
5742                 bData->next = cData->bans;
5743
5744                 if(cData->bans)
5745                     cData->bans->prev = bData;
5746                 cData->bans = bData;
5747             }
5748
5749             change.args[0].mode = MODE_BAN;
5750             change.args[0].hostmask = bData->mask;
5751             mod_chanmode_announce(chanserv, channel, &change);
5752             KickChannelUser(user, channel, chanserv, kick_reason);
5753             return 1;
5754         }
5755     }
5756
5757     /* ChanServ will not modify the limits in join-flooded channels.
5758        It will also skip DynLimit processing when the user (or srvx)
5759        is bursting in, because there are likely more incoming. */
5760     if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5761        && !user->uplink->burst
5762        && !channel->join_flooded
5763        && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5764     {
5765         /* The user count has begun "bumping" into the channel limit,
5766            so set a timer to raise the limit a bit. Any previous
5767            timers are removed so three incoming users within the delay
5768            results in one limit change, not three. */
5769
5770         timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5771         timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5772     }
5773
5774     if(channel->join_flooded)
5775     {
5776         /* don't automatically give ops or voice during a join flood */
5777     }
5778     else if(cData->lvlOpts[lvlGiveOps] == 0)
5779         modes |= MODE_CHANOP;
5780     else if(cData->lvlOpts[lvlGiveVoice] == 0)
5781         modes |= MODE_VOICE;
5782
5783     greeting = cData->greeting;
5784     if(user->handle_info)
5785     {
5786         handle = user->handle_info;
5787
5788         if(IsHelper(user) && !IsHelping(user))
5789         {
5790             unsigned int ii;
5791             for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5792             {
5793                 if(channel == chanserv_conf.support_channels.list[ii])
5794                 {
5795                     HANDLE_SET_FLAG(user->handle_info, HELPING);
5796                     break;
5797                 }
5798             }
5799         }
5800
5801         uData = GetTrueChannelAccess(cData, handle);
5802         if(uData && !IsUserSuspended(uData))
5803         {
5804             /* Ops and above were handled by the above case. */
5805             if(IsUserAutoOp(uData))
5806             {
5807                 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5808                     modes |= MODE_CHANOP;
5809                 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5810                     modes |= MODE_VOICE;
5811             }
5812             if(uData->access >= UL_PRESENT)
5813                 cData->visited = now;
5814             if(cData->user_greeting)
5815                 greeting = cData->user_greeting;
5816             if(uData->info
5817                && (uData->access >= cData->lvlOpts[lvlUserInfo])
5818                && ((now - uData->seen) >= chanserv_conf.info_delay)
5819                && !uData->present)
5820                 info = 1;
5821             uData->seen = now;
5822             uData->present = 1;
5823         }
5824     }
5825     if(!user->uplink->burst)
5826     {
5827         if(modes)
5828         {
5829             if(modes & MODE_CHANOP)
5830                 modes &= ~MODE_VOICE;
5831             change.args[0].mode = modes;
5832             change.args[0].member = mNode;
5833             mod_chanmode_announce(chanserv, channel, &change);
5834         }
5835         if(greeting && !user->uplink->burst)
5836             send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5837         if(uData && info)
5838             send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5839     }
5840     return 0;
5841 }
5842
5843 static void
5844 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5845 {
5846     struct mod_chanmode change;
5847     struct userData *channel;
5848     unsigned int ii, jj;
5849
5850     if(!user->handle_info)
5851         return;
5852
5853     mod_chanmode_init(&change);
5854     change.argc = 1;
5855     for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5856     {
5857         struct chanNode *cn;
5858         struct modeNode *mn;
5859         if(IsUserSuspended(channel)
5860            || IsSuspended(channel->channel)
5861            || !(cn = channel->channel->channel))
5862             continue;
5863
5864         mn = GetUserMode(cn, user);
5865         if(!mn)
5866         {
5867             if(!IsUserSuspended(channel)
5868                && IsUserAutoInvite(channel)
5869                && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
5870                && (cn->modes & (MODE_KEY | MODE_INVITEONLY))
5871                && !self->burst)
5872                 irc_invite(chanserv, user, cn);
5873             continue;
5874         }
5875
5876         if(channel->access >= UL_PRESENT)
5877             channel->channel->visited = now;
5878
5879         if(IsUserAutoOp(channel))
5880         {
5881             if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5882                 change.args[0].mode = MODE_CHANOP;
5883             else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
5884                 change.args[0].mode = MODE_VOICE;
5885             else
5886                 change.args[0].mode = 0;
5887             change.args[0].member = mn;
5888             if(change.args[0].mode)
5889                 mod_chanmode_announce(chanserv, cn, &change);
5890         }
5891
5892         channel->seen = now;
5893         channel->present = 1;
5894     }
5895
5896     for(ii = 0; ii < user->channels.used; ++ii)
5897     {
5898         struct chanNode *channel = user->channels.list[ii]->channel;
5899         struct banData *ban;
5900
5901         if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
5902            || !channel->channel_info)
5903             continue;
5904         for(jj = 0; jj < channel->banlist.used; ++jj)
5905             if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
5906                 break;
5907         if(jj < channel->banlist.used)
5908             continue;
5909         for(ban = channel->channel_info->bans; ban; ban = ban->next)
5910         {
5911             char kick_reason[MAXLEN];
5912             if(!user_matches_glob(user, ban->mask, 1))
5913                 continue;
5914             change.args[0].mode = MODE_BAN;
5915             change.args[0].hostmask = ban->mask;
5916             mod_chanmode_announce(chanserv, channel, &change);
5917             sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
5918             KickChannelUser(user, channel, chanserv, kick_reason);
5919             ban->triggered = now;
5920             break;
5921         }
5922     }
5923
5924     if(IsSupportHelper(user))
5925     {
5926         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5927         {
5928             if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
5929             {
5930                 HANDLE_SET_FLAG(user->handle_info, HELPING);
5931                 break;
5932             }
5933         }
5934     }
5935 }
5936
5937 static void
5938 handle_part(struct userNode *user, struct chanNode *channel, UNUSED_ARG(const char *reason))
5939 {
5940     struct chanData *cData;
5941     struct userData *uData;
5942
5943     cData = channel->channel_info;
5944     if(!cData || IsSuspended(cData) || IsLocal(user))
5945         return;
5946
5947     if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !channel->join_flooded)
5948     {
5949         /* Allow for a bit of padding so that the limit doesn't
5950            track the user count exactly, which could get annoying. */
5951         if((channel->limit - channel->members.used) > chanserv_conf.adjust_threshold + 5)
5952         {
5953             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5954             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5955         }
5956     }
5957
5958     if((uData = GetTrueChannelAccess(cData, user->handle_info)))
5959     {
5960         scan_user_presence(uData, user);
5961         uData->seen = now;
5962     }
5963
5964     if(IsHelping(user) && IsSupportHelper(user))
5965     {
5966         unsigned int ii, jj;
5967         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5968         {
5969             for(jj = 0; jj < user->channels.used; ++jj)
5970                 if(user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
5971                     break;
5972             if(jj < user->channels.used)
5973                 break;
5974         }
5975         if(ii == chanserv_conf.support_channels.used)
5976             HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
5977     }
5978 }
5979
5980 static void
5981 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
5982 {
5983     struct userData *uData;
5984
5985     if(!channel->channel_info || !kicker || IsService(kicker)
5986        || (kicker == victim) || IsSuspended(channel->channel_info)
5987        || (kicker->handle_info && kicker->handle_info == victim->handle_info))
5988         return;
5989
5990     if(protect_user(victim, kicker, channel->channel_info))
5991     {
5992         const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
5993         KickChannelUser(kicker, channel, chanserv, reason);
5994     }
5995
5996     if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
5997         uData->seen = now;
5998 }
5999
6000 static int
6001 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6002 {
6003     struct chanData *cData;
6004
6005     if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6006         return 0;
6007
6008     cData = channel->channel_info;
6009     if(bad_topic(channel, user, channel->topic))
6010     {
6011         send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6012         if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6013             SetChannelTopic(channel, chanserv, old_topic, 1);
6014         else if(cData->topic)
6015             SetChannelTopic(channel, chanserv, cData->topic, 1);
6016         return 1;
6017     }
6018     /* With topicsnarf, grab the topic and save it as the default topic. */
6019     if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6020     {
6021         free(cData->topic);
6022         cData->topic = strdup(channel->topic);
6023     }
6024     return 0;
6025 }
6026
6027 static void
6028 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6029 {
6030     struct mod_chanmode *bounce = NULL;
6031     unsigned int bnc, ii;
6032     char deopped = 0;
6033
6034     if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6035         return;
6036
6037     if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6038        && mode_lock_violated(&channel->channel_info->modes, change))
6039     {
6040         char correct[MAXLEN];
6041         bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6042         mod_chanmode_format(&channel->channel_info->modes, correct);
6043         send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6044     }
6045     for(ii = bnc = 0; ii < change->argc; ++ii)
6046     {
6047         if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6048         {
6049             const struct userNode *victim = change->args[ii].member->user;
6050             if(!protect_user(victim, user, channel->channel_info))
6051                 continue;
6052             if(!bounce)
6053                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6054             if(!deopped)
6055             {
6056                 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6057                 bounce->args[bnc].member = GetUserMode(channel, user);
6058                 if(bounce->args[bnc].member)
6059                     bnc++;
6060                 deopped = 1;
6061             }
6062             bounce->args[bnc].mode = MODE_CHANOP;
6063             bounce->args[bnc].member = change->args[ii].member;
6064             bnc++;
6065             send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6066         }
6067         else if(change->args[ii].mode & MODE_CHANOP)
6068         {
6069             const struct userNode *victim = change->args[ii].member->user;
6070             if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6071                 continue;
6072             if(!bounce)
6073                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6074             bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6075             bounce->args[bnc].member = change->args[ii].member;
6076             bnc++;
6077         }
6078         else if(change->args[ii].mode & MODE_BAN)
6079         {
6080             const char *ban = change->args[ii].hostmask;
6081             if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6082                 continue;
6083             if(!bounce)
6084                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6085             bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6086             bounce->args[bnc].hostmask = ban;
6087             bnc++;
6088             send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6089         }
6090     }
6091     if(bounce)
6092     {
6093         if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6094             mod_chanmode_announce(chanserv, channel, bounce);
6095         mod_chanmode_free(bounce);
6096     }
6097 }
6098
6099 static void
6100 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6101 {
6102     struct chanNode *channel;
6103     struct banData *bData;
6104     struct mod_chanmode change;
6105     unsigned int ii, jj;
6106     char kick_reason[MAXLEN];
6107
6108     mod_chanmode_init(&change);
6109     change.argc = 1;
6110     change.args[0].mode = MODE_BAN;
6111     for(ii = 0; ii < user->channels.used; ++ii)
6112     {
6113         channel = user->channels.list[ii]->channel;
6114         /* Need not check for bans if they're opped or voiced. */
6115         if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6116             continue;
6117         /* Need not check for bans unless channel registration is active. */
6118         if(!channel->channel_info || IsSuspended(channel->channel_info))
6119             continue;
6120         /* Look for a matching ban already on the channel. */
6121         for(jj = 0; jj < channel->banlist.used; ++jj)
6122             if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6123                 break;
6124         /* Need not act if we found one. */
6125         if(jj < channel->banlist.used)
6126             continue;
6127         /* Look for a matching ban in this channel. */
6128         for(bData = channel->channel_info->bans; bData; bData = bData->next)
6129         {
6130             if(!user_matches_glob(user, bData->mask, 1))
6131                 continue;
6132             change.args[0].hostmask = bData->mask;
6133             mod_chanmode_announce(chanserv, channel, &change);
6134             sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6135             KickChannelUser(user, channel, chanserv, kick_reason);
6136             bData->triggered = now;
6137             break; /* we don't need to check any more bans in the channel */
6138         }
6139     }
6140 }
6141
6142 static void handle_rename(struct handle_info *handle, const char *old_handle)
6143 {
6144     struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6145
6146     if(dnr)
6147     {
6148         dict_remove2(handle_dnrs, old_handle, 1);
6149         safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6150         dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6151     }
6152 }
6153
6154 static void
6155 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6156 {
6157     struct userNode *h_user;
6158
6159     if(handle->channels)
6160     {
6161         for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6162             send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6163
6164         while(handle->channels)
6165             del_channel_user(handle->channels, 1);
6166     }
6167 }
6168
6169 static void
6170 handle_server_link(UNUSED_ARG(struct server *server))
6171 {
6172     struct chanData *cData;
6173
6174     for(cData = channelList; cData; cData = cData->next)
6175     {
6176         if(!IsSuspended(cData))
6177             cData->may_opchan = 1;
6178         if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6179            && !cData->channel->join_flooded
6180            && ((cData->channel->limit - cData->channel->members.used)
6181                < chanserv_conf.adjust_threshold))
6182         {
6183             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6184             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6185         }
6186     }
6187 }
6188
6189 static void
6190 chanserv_conf_read(void)
6191 {
6192     dict_t conf_node;
6193     const char *str;
6194     char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6195     struct mod_chanmode *change;
6196     struct string_list *strlist;
6197     struct chanNode *chan;
6198     unsigned int ii;
6199
6200     if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6201     {
6202         log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6203         return;
6204     }
6205     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6206         UnlockChannel(chanserv_conf.support_channels.list[ii]);
6207     chanserv_conf.support_channels.used = 0;
6208     if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6209     {
6210         for(ii = 0; ii < strlist->used; ++ii)
6211         {
6212             const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6213             if(!str2)
6214                 str2 = "+nt";
6215             chan = AddChannel(strlist->list[ii], now, str2, NULL);
6216             LockChannel(chan);
6217             channelList_append(&chanserv_conf.support_channels, chan);
6218         }
6219     }
6220     else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6221     {
6222         const char *str2;
6223         str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6224         if(!str2)
6225             str2 = "+nt";
6226         chan = AddChannel(str, now, str2, NULL);
6227         LockChannel(chan);
6228         channelList_append(&chanserv_conf.support_channels, chan);
6229     }
6230     str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6231     chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6232     str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6233     chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6234     str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6235     chanserv_conf.greeting_length = str ? atoi(str) : 120;
6236     str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6237     chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6238     str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6239     chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6240     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6241     chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6242     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6243     chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6244     str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6245     chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6246     str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6247     chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6248     str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6249     chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6250     str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6251     if(chanserv && str)
6252         NickChange(chanserv, str, 0);
6253     str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6254     chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6255     str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6256     chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6257     str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6258     chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6259     str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6260     chanserv_conf.max_owned = str ? atoi(str) : 5;
6261     str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6262     chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6263     str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6264     chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6265     str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6266     chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6267     str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6268     if(!str)
6269         str = "+nt";
6270     safestrncpy(mode_line, str, sizeof(mode_line));
6271     ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6272     if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6273     {
6274         chanserv_conf.default_modes = *change;
6275         mod_chanmode_free(change);
6276     }
6277     free_string_list(chanserv_conf.set_shows);
6278     strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6279     if(strlist)
6280         strlist = string_list_copy(strlist);
6281     else
6282     {
6283         static const char *list[] = {
6284             /* free form text */
6285             "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6286             /* options based on user level */
6287             "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6288             "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6289             /* multiple choice options */
6290             "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6291             /* binary options */
6292             "DynLimit", "NoDelete",
6293             /* delimiter */
6294             NULL
6295         };
6296         unsigned int ii;
6297         strlist = alloc_string_list(ArrayLength(list)-1);
6298         for(ii=0; list[ii]; ii++)
6299             string_list_append(strlist, strdup(list[ii]));
6300     }
6301     chanserv_conf.set_shows = strlist;
6302     /* We don't look things up now, in case the list refers to options
6303      * defined by modules initialized after this point.  Just mark the
6304      * function list as invalid, so it will be initialized.
6305      */
6306     set_shows_list.used = 0;
6307     free_string_list(chanserv_conf.eightball);
6308     strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6309     if(strlist)
6310     {
6311         strlist = string_list_copy(strlist);
6312     }
6313     else
6314     {
6315         strlist = alloc_string_list(4);
6316         string_list_append(strlist, strdup("Yes."));
6317         string_list_append(strlist, strdup("No."));
6318         string_list_append(strlist, strdup("Maybe so."));
6319     }
6320     chanserv_conf.eightball = strlist;
6321     free_string_list(chanserv_conf.old_ban_names);
6322     strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6323     if(strlist)
6324         strlist = string_list_copy(strlist);
6325     else
6326         strlist = alloc_string_list(2);
6327     chanserv_conf.old_ban_names = strlist;
6328 }
6329
6330 static void
6331 chanserv_note_type_read(const char *key, struct record_data *rd)
6332 {
6333     dict_t obj;
6334     struct note_type *ntype;
6335     const char *str;
6336
6337     if(!(obj = GET_RECORD_OBJECT(rd)))
6338     {
6339         log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6340         return;
6341     }
6342     if(!(ntype = chanserv_create_note_type(key)))
6343     {
6344         log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6345         return;
6346     }
6347
6348     /* Figure out set access */
6349     if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6350     {
6351         ntype->set_access_type = NOTE_SET_PRIVILEGED;
6352         ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6353     }
6354     else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6355     {
6356         ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6357         ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6358     }
6359     else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6360     {
6361         ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6362     }
6363     else
6364     {
6365         log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6366         ntype->set_access_type = NOTE_SET_PRIVILEGED;
6367         ntype->set_access.min_opserv = 0;
6368     }
6369
6370     /* Figure out visibility */
6371     if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6372         ntype->visible_type = NOTE_VIS_PRIVILEGED;
6373     else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6374         ntype->visible_type = NOTE_VIS_PRIVILEGED;
6375     else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6376         ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6377     else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6378         ntype->visible_type = NOTE_VIS_ALL;
6379     else
6380         ntype->visible_type = NOTE_VIS_PRIVILEGED;
6381
6382     str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6383     ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6384 }
6385
6386 static void
6387 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6388 {
6389     struct handle_info *handle;
6390     struct userData *uData;
6391     char *seen, *inf, *flags;
6392     time_t last_seen;
6393     unsigned short access;
6394
6395     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6396     {
6397         log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6398         return;
6399     }
6400
6401     access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6402     if(access > UL_OWNER)
6403     {
6404         log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6405         return;
6406     }
6407
6408     inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6409     seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6410     last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6411     flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6412     handle = get_handle_info(key);
6413     if(!handle)
6414     {
6415         log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6416         return;
6417     }
6418
6419     uData = add_channel_user(chan, handle, access, last_seen, inf);
6420     uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6421 }
6422
6423 static void
6424 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6425 {
6426     struct banData *bData;
6427     char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6428     time_t set_time, triggered_time, expires_time;
6429
6430     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6431     {
6432         log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6433         return;
6434     }
6435
6436     set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6437     triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6438     s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6439     s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6440     owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6441     reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6442
6443     set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6444     triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6445     if(s_expires)
6446         expires_time = (time_t)strtoul(s_expires, NULL, 0);
6447     else if(s_duration)
6448         expires_time = set_time + atoi(s_duration);
6449     else
6450         expires_time = 0;
6451
6452     if(expires_time && (expires_time < now))
6453         return;
6454
6455     bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6456 }
6457
6458 static struct suspended *
6459 chanserv_read_suspended(dict_t obj)
6460 {
6461     struct suspended *suspended = calloc(1, sizeof(*suspended));
6462     char *str;
6463     dict_t previous;
6464
6465     str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6466     suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6467     str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6468     suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6469     str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6470     suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6471     suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6472     suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6473     previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6474     suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6475     return suspended;
6476 }
6477
6478 static int
6479 chanserv_channel_read(const char *key, struct record_data *hir)
6480 {
6481     struct suspended *suspended;
6482     struct mod_chanmode *modes;
6483     struct chanNode *cNode;
6484     struct chanData *cData;
6485     struct dict *channel, *obj;
6486     char *str, *argv[10];
6487     dict_iterator_t it;
6488     unsigned int argc;
6489
6490     channel = hir->d.object;
6491
6492     str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6493     if(!str)
6494         str = "<unknown>";
6495     cNode = AddChannel(key, now, NULL, NULL);
6496     if(!cNode)
6497     {
6498         log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6499         return 0;
6500     }
6501     cData = register_channel(cNode, str);
6502     if(!cData)
6503     {
6504         log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6505         return 0;
6506     }
6507
6508     if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6509     {
6510         enum levelOption lvlOpt;
6511         enum charOption chOpt;
6512
6513         if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6514             cData->flags = atoi(str);
6515
6516         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6517         {
6518             str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6519             if(str)
6520                 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6521             else if(levelOptions[lvlOpt].old_flag)
6522             {
6523                 if(cData->flags & levelOptions[lvlOpt].old_flag)
6524                     cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6525                 else
6526                     cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6527             }
6528         }
6529
6530         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6531         {
6532             if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6533                 continue;
6534             cData->chOpts[chOpt] = str[0];
6535         }
6536     }
6537     else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6538     {
6539         enum levelOption lvlOpt;
6540         enum charOption chOpt;
6541         unsigned int count;
6542
6543         cData->flags = base64toint(str, 5);
6544         count = strlen(str += 5);
6545         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6546         {
6547             unsigned short lvl;
6548             if(levelOptions[lvlOpt].old_flag)
6549             {
6550                 if(cData->flags & levelOptions[lvlOpt].old_flag)
6551                     lvl = levelOptions[lvlOpt].flag_value;
6552                 else
6553                     lvl = levelOptions[lvlOpt].default_value;
6554             }
6555             else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6556             {
6557             case 'c': lvl = UL_COOWNER; break;
6558             case 'm': lvl = UL_MASTER; break;
6559             case 'n': lvl = UL_OWNER+1; break;
6560             case 'o': lvl = UL_OP; break;
6561             case 'p': lvl = UL_PEON; break;
6562             case 'w': lvl = UL_OWNER; break;
6563             default: lvl = 0; break;
6564             }
6565             cData->lvlOpts[lvlOpt] = lvl;
6566         }
6567         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6568             cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6569     }
6570    
6571     if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6572     {
6573         suspended = chanserv_read_suspended(obj);
6574         cData->suspended = suspended;
6575         suspended->cData = cData;
6576         /* We could use suspended->expires and suspended->revoked to
6577          * set the CHANNEL_SUSPENDED flag, but we don't. */
6578     }
6579     else if(cData->flags & CHANNEL_SUSPENDED)
6580     {
6581         suspended = calloc(1, sizeof(*suspended));
6582         suspended->issued = 0;
6583         suspended->revoked = 0;
6584         str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6585         suspended->expires = str ? atoi(str) : 0;
6586         suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6587         str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6588         suspended->reason = strdup(str ? str : "No reason");
6589         suspended->previous = NULL;
6590         cData->suspended = suspended;
6591         suspended->cData = cData;
6592     }
6593     else
6594         suspended = NULL;
6595
6596     if((cData->flags & CHANNEL_SUSPENDED)
6597        && suspended->expires
6598        && (suspended->expires <= now))
6599     {
6600         cData->flags &= ~CHANNEL_SUSPENDED;
6601     }
6602
6603     if(!(cData->flags & CHANNEL_SUSPENDED))
6604     {
6605         struct mod_chanmode change;
6606         mod_chanmode_init(&change);
6607         change.argc = 1;
6608         change.args[0].mode = MODE_CHANOP;
6609         change.args[0].member = AddChannelUser(chanserv, cNode);
6610         mod_chanmode_announce(chanserv, cNode, &change);
6611     }
6612     else if(suspended->expires > now)
6613     {
6614         timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6615     }
6616
6617     str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6618     cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6619     str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6620     cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6621     str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6622     cData->max = str ? atoi(str) : 0;
6623     str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6624     cData->greeting = str ? strdup(str) : NULL;
6625     str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6626     cData->user_greeting = str ? strdup(str) : NULL;
6627     str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6628     cData->topic_mask = str ? strdup(str) : NULL;
6629     str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6630     cData->topic = str ? strdup(str) : NULL;
6631
6632     if((str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6633        && (argc = split_line(str, 0, ArrayLength(argv), argv))
6634        && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6635         cData->modes = *modes;
6636         if(cData->modes.argc > 1)
6637             cData->modes.argc = 1;
6638         if(!IsSuspended(cData))
6639             mod_chanmode_announce(chanserv, cNode, &cData->modes);
6640         mod_chanmode_free(modes);
6641     }
6642
6643     obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6644     for(it = dict_first(obj); it; it = iter_next(it))
6645         user_read_helper(iter_key(it), iter_data(it), cData);
6646
6647     if(!cData->users && !IsProtected(cData))
6648     {
6649         log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6650         unregister_channel(cData, "has empty user list.");
6651         return 0;
6652     }
6653
6654     obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6655     for(it = dict_first(obj); it; it = iter_next(it))
6656         ban_read_helper(iter_key(it), iter_data(it), cData);
6657
6658     obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6659     for(it = dict_first(obj); it; it = iter_next(it))
6660     {
6661         struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6662         struct record_data *rd = iter_data(it);
6663         const char *note, *setter;
6664
6665         if(rd->type != RECDB_OBJECT)
6666         {
6667             log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6668         }
6669         else if(!ntype)
6670         {
6671             log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6672         }
6673         else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6674         {
6675             log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6676         }
6677         else
6678         {
6679             setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6680             if(!setter) setter = "<unknown>";
6681             chanserv_add_channel_note(cData, ntype, setter, note);
6682         }
6683     }
6684
6685     return 0;
6686 }
6687
6688 static void
6689 chanserv_dnr_read(const char *key, struct record_data *hir)
6690 {
6691     const char *setter, *reason, *str;
6692     struct do_not_register *dnr;
6693
6694     setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6695     if(!setter)
6696     {
6697         log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6698         return;
6699     }
6700     reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6701     if(!reason)
6702     {
6703         log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6704         return;
6705     }
6706     dnr = chanserv_add_dnr(key, setter, reason);
6707     if(!dnr)
6708         return;
6709     str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6710     if(str)
6711         dnr->set = atoi(str);
6712     else
6713         dnr->set = 0;
6714 }
6715
6716 static int
6717 chanserv_saxdb_read(struct dict *database)
6718 {
6719     struct dict *section;
6720     dict_iterator_t it;
6721
6722     if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6723         for(it = dict_first(section); it; it = iter_next(it))
6724             chanserv_note_type_read(iter_key(it), iter_data(it));
6725
6726     if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6727         for(it = dict_first(section); it; it = iter_next(it))
6728             chanserv_channel_read(iter_key(it), iter_data(it));
6729
6730     if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6731         for(it = dict_first(section); it; it = iter_next(it))
6732             chanserv_dnr_read(iter_key(it), iter_data(it));
6733
6734     return 0;
6735 }
6736
6737 static int
6738 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6739 {
6740     int high_present = 0;
6741     saxdb_start_record(ctx, KEY_USERS, 1);
6742     for(; uData; uData = uData->next)
6743     {
6744         if((uData->access >= UL_PRESENT) && uData->present)
6745             high_present = 1;
6746         saxdb_start_record(ctx, uData->handle->handle, 0);
6747         saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6748         saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6749         if(uData->flags)
6750             saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6751         if(uData->info)
6752             saxdb_write_string(ctx, KEY_INFO, uData->info);
6753         saxdb_end_record(ctx);
6754     }
6755     saxdb_end_record(ctx);
6756     return high_present;
6757 }
6758
6759 static void
6760 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6761 {
6762     if(!bData)
6763         return;
6764     saxdb_start_record(ctx, KEY_BANS, 1);
6765     for(; bData; bData = bData->next)
6766     {
6767         saxdb_start_record(ctx, bData->mask, 0);
6768         saxdb_write_int(ctx, KEY_SET, bData->set);
6769         if(bData->triggered)
6770             saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6771         if(bData->expires)
6772             saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6773         if(bData->owner[0])
6774             saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6775         if(bData->reason)
6776             saxdb_write_string(ctx, KEY_REASON, bData->reason);
6777         saxdb_end_record(ctx);
6778     }
6779     saxdb_end_record(ctx);
6780 }
6781
6782 static void
6783 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6784 {
6785     saxdb_start_record(ctx, name, 0);
6786     saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6787     saxdb_write_string(ctx, KEY_REASON, susp->reason);
6788     if(susp->issued)
6789         saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6790     if(susp->expires)
6791         saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6792     if(susp->revoked)
6793         saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6794     if(susp->previous)
6795         chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6796     saxdb_end_record(ctx);
6797 }
6798
6799 static void
6800 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6801 {
6802     char buf[MAXLEN];
6803     int high_present;
6804     enum levelOption lvlOpt;
6805     enum charOption chOpt;
6806
6807     saxdb_start_record(ctx, channel->channel->name, 1);
6808
6809     saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6810     saxdb_write_int(ctx, KEY_MAX, channel->max);
6811     if(channel->topic)
6812         saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6813     if(channel->registrar)
6814         saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6815     if(channel->greeting)
6816         saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6817     if(channel->user_greeting)
6818         saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6819     if(channel->topic_mask)
6820         saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6821     if(channel->suspended)
6822         chanserv_write_suspended(ctx, "suspended", channel->suspended);
6823
6824     saxdb_start_record(ctx, KEY_OPTIONS, 0);
6825     saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6826     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6827         saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6828     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6829     {
6830         buf[0] = channel->chOpts[chOpt];
6831         buf[1] = '\0';
6832         saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6833     }
6834     saxdb_end_record(ctx);
6835
6836     if(channel->modes.modes_set || channel->modes.modes_clear)
6837     {
6838         mod_chanmode_format(&channel->modes, buf);
6839         saxdb_write_string(ctx, KEY_MODES, buf);
6840     }
6841
6842     high_present = chanserv_write_users(ctx, channel->users);
6843     chanserv_write_bans(ctx, channel->bans);
6844
6845     if(dict_size(channel->notes))
6846     {
6847         dict_iterator_t it;
6848
6849         saxdb_start_record(ctx, KEY_NOTES, 1);
6850         for(it = dict_first(channel->notes); it; it = iter_next(it))
6851         {
6852             struct note *note = iter_data(it);
6853             saxdb_start_record(ctx, iter_key(it), 0);
6854             saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6855             saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6856             saxdb_end_record(ctx);
6857         }
6858         saxdb_end_record(ctx);
6859     }
6860
6861     saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6862     saxdb_end_record(ctx);
6863 }
6864
6865 static void
6866 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6867 {
6868     const char *str;
6869
6870     saxdb_start_record(ctx, ntype->name, 0);
6871     switch(ntype->set_access_type)
6872     {
6873     case NOTE_SET_CHANNEL_ACCESS:
6874         saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6875         break;
6876     case NOTE_SET_CHANNEL_SETTER:
6877         saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6878         break;
6879     case NOTE_SET_PRIVILEGED: default:
6880         saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6881         break;
6882     }
6883     switch(ntype->visible_type)
6884     {
6885     case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6886     case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6887     case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6888     }
6889     saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6890     saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6891     saxdb_end_record(ctx);
6892 }
6893
6894 static void
6895 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
6896 {
6897     struct do_not_register *dnr;
6898     dict_iterator_t it;
6899
6900     for(it = dict_first(dnrs); it; it = iter_next(it))
6901     {
6902         dnr = iter_data(it);
6903         saxdb_start_record(ctx, dnr->chan_name, 0);
6904         if(dnr->set)
6905             saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
6906         saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
6907         saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
6908         saxdb_end_record(ctx);
6909     }
6910 }
6911
6912 static int
6913 chanserv_saxdb_write(struct saxdb_context *ctx)
6914 {
6915     dict_iterator_t it;
6916     struct chanData *channel;
6917
6918     /* Notes */
6919     saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
6920     for(it = dict_first(note_types); it; it = iter_next(it))
6921         chanserv_write_note_type(ctx, iter_data(it));
6922     saxdb_end_record(ctx);
6923
6924     /* DNRs */
6925     saxdb_start_record(ctx, KEY_DNR, 1);
6926     write_dnrs_helper(ctx, handle_dnrs);
6927     write_dnrs_helper(ctx, plain_dnrs);
6928     write_dnrs_helper(ctx, mask_dnrs);
6929     saxdb_end_record(ctx);
6930
6931     /* Channels */
6932     saxdb_start_record(ctx, KEY_CHANNELS, 1);
6933     for(channel = channelList; channel; channel = channel->next)
6934         chanserv_write_channel(ctx, channel);
6935     saxdb_end_record(ctx);
6936
6937     return 0;
6938 }
6939
6940 static void
6941 chanserv_db_cleanup(void) {
6942     unsigned int ii;
6943     unreg_part_func(handle_part);
6944     while(channelList)
6945         unregister_channel(channelList, "terminating.");
6946     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6947         UnlockChannel(chanserv_conf.support_channels.list[ii]);
6948     free(chanserv_conf.support_channels.list);
6949     dict_delete(handle_dnrs);
6950     dict_delete(plain_dnrs);
6951     dict_delete(mask_dnrs);
6952     dict_delete(note_types);
6953     free_string_list(chanserv_conf.eightball);
6954     free_string_list(chanserv_conf.old_ban_names);
6955     free_string_list(chanserv_conf.set_shows);
6956     free(set_shows_list.list);
6957     free(uset_shows_list.list);
6958     while(helperList)
6959     {
6960         struct userData *helper = helperList;
6961         helperList = helperList->next;
6962         free(helper);
6963     }
6964 }
6965
6966 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
6967 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
6968 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
6969
6970 void
6971 init_chanserv(const char *nick)
6972 {
6973     CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
6974     conf_register_reload(chanserv_conf_read);
6975
6976     reg_server_link_func(handle_server_link);
6977
6978     reg_new_channel_func(handle_new_channel);
6979     reg_join_func(handle_join);
6980     reg_part_func(handle_part);
6981     reg_kick_func(handle_kick);
6982     reg_topic_func(handle_topic);
6983     reg_mode_change_func(handle_mode);
6984     reg_nick_change_func(handle_nick_change);
6985
6986     reg_auth_func(handle_auth);
6987     reg_handle_rename_func(handle_rename);
6988     reg_unreg_func(handle_unreg);
6989
6990     handle_dnrs = dict_new();
6991     dict_set_free_data(handle_dnrs, free);
6992     plain_dnrs = dict_new();
6993     dict_set_free_data(plain_dnrs, free);
6994     mask_dnrs = dict_new();
6995     dict_set_free_data(mask_dnrs, free);
6996
6997     reg_svccmd_unbind_func(handle_svccmd_unbind);
6998     chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
6999     DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7000     DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7001     DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7002     DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7003     DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7004     DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7005     DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7006     DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7007
7008     DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7009     DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7010
7011     DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7012     DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7013     DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7014     DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7015     DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7016
7017     DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7018     DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7019     DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7020     DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7021     DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7022
7023     DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7024     DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7025     DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7026     DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7027
7028     DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7029     DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7030     DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7031     DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7032     DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7033     DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7034     DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7035     DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7036
7037     DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7038     DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7039     DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7040     DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7041     DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7042     DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7043     DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7044     DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7045     DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7046     DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7047     DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7048     DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7049     DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7050     DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7051
7052     DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7053     DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7054     DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7055     DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7056     DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7057
7058     DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7059     DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7060
7061     DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7062     DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7063     DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7064     DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7065     DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7066     DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7067     DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7068     DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7069     DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7070     DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7071     DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7072
7073     DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7074     DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7075
7076     DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7077     DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7078     DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7079     DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7080
7081     DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7082     DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7083     DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7084     DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7085     DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7086
7087     DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7088     DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7089     DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7090     DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7091     DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7092     DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7093
7094     /* Channel options */
7095     DEFINE_CHANNEL_OPTION(defaulttopic);
7096     DEFINE_CHANNEL_OPTION(topicmask);
7097     DEFINE_CHANNEL_OPTION(greeting);
7098     DEFINE_CHANNEL_OPTION(usergreeting);
7099     DEFINE_CHANNEL_OPTION(modes);
7100     DEFINE_CHANNEL_OPTION(enfops);
7101     DEFINE_CHANNEL_OPTION(giveops);
7102     DEFINE_CHANNEL_OPTION(protect);
7103     DEFINE_CHANNEL_OPTION(enfmodes);
7104     DEFINE_CHANNEL_OPTION(enftopic);
7105     DEFINE_CHANNEL_OPTION(pubcmd);
7106     DEFINE_CHANNEL_OPTION(givevoice);
7107     DEFINE_CHANNEL_OPTION(userinfo);
7108     DEFINE_CHANNEL_OPTION(dynlimit);
7109     DEFINE_CHANNEL_OPTION(topicsnarf);
7110     DEFINE_CHANNEL_OPTION(nodelete);
7111     DEFINE_CHANNEL_OPTION(toys);
7112     DEFINE_CHANNEL_OPTION(setters);
7113     DEFINE_CHANNEL_OPTION(topicrefresh);
7114     DEFINE_CHANNEL_OPTION(ctcpusers);
7115     DEFINE_CHANNEL_OPTION(ctcpreaction);
7116     DEFINE_CHANNEL_OPTION(inviteme);
7117     modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7118
7119     /* Alias set topic to set defaulttopic for compatibility. */
7120     modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7121
7122     /* User options */
7123     DEFINE_USER_OPTION(noautoop);
7124     DEFINE_USER_OPTION(autoinvite);
7125     DEFINE_USER_OPTION(info);
7126
7127     /* Alias uset autovoice to uset autoop. */
7128     modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7129
7130     note_types = dict_new();
7131     dict_set_free_data(note_types, chanserv_deref_note_type);
7132     if(nick)
7133     {
7134         chanserv = AddService(nick, "Channel Services");
7135         service_register(chanserv, '!');
7136         reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7137     }
7138     saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7139
7140     if(chanserv_conf.channel_expire_frequency)
7141         timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7142
7143     if(chanserv_conf.refresh_period)
7144     {
7145         time_t next_refresh;
7146         next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7147         timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7148     }
7149     
7150     reg_exit_func(chanserv_db_cleanup);
7151     message_register_table(msgtab);
7152 }