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