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