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