added some nodelete overrides
[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) && !IsOper(user))
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) && !IsOper(user))
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(!uData->seen)
4171             ary[2] = "Never";
4172         else
4173             ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4174         ary[2] = strdup(ary[2]);
4175         if(IsUserSuspended(uData))
4176             ary[3] = "Suspended";
4177         else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4178             ary[3] = "Vacation";
4179         else if(HANDLE_FLAGGED(uData->handle, BOT))
4180             ary[3] = "Bot";
4181         else
4182             ary[3] = "Normal";
4183     }
4184     send_list(&lData);
4185     for(matches = 1; matches < lData.table.length; ++matches)
4186     {
4187         free((char*)lData.table.contents[matches][2]);
4188         free(lData.table.contents[matches]);
4189     }
4190     free(lData.table.contents[0]);
4191     free(lData.table.contents);
4192     reply("CSMSG_TOTAL_USERS",(lData.table.length - 1),channel->name);
4193     return 1;
4194 }
4195
4196 static CHANSERV_FUNC(cmd_users)
4197 {
4198     return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4199 }
4200
4201 static CHANSERV_FUNC(cmd_wlist)
4202 {
4203     return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4204 }
4205
4206 static CHANSERV_FUNC(cmd_clist)
4207 {
4208     return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4209 }
4210
4211 static CHANSERV_FUNC(cmd_mlist)
4212 {
4213     return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4214 }
4215
4216 static CHANSERV_FUNC(cmd_olist)
4217 {
4218     return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4219 }
4220
4221 static CHANSERV_FUNC(cmd_plist)
4222 {
4223     return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4224 }
4225
4226 static CHANSERV_FUNC(cmd_bans)
4227 {
4228     struct userNode *search_u = NULL;
4229     struct helpfile_table tbl;
4230     unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4231     char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4232     const char *msg_never, *triggered, *expires;
4233     struct banData *ban, **bans;
4234
4235     if(argc < 2)
4236         search = NULL;
4237     else if(strchr(search = argv[1], '!'))
4238     {
4239         search = argv[1];
4240         search_wilds = search[strcspn(search, "?*")];
4241     }
4242     else if(!(search_u = GetUserH(search)))
4243         reply("MSG_NICK_UNKNOWN", search);
4244
4245     bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4246
4247     for(ban = channel->channel_info->bans; ban; ban = ban->next)
4248     {
4249         if(search_u)
4250         {
4251             if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4252                 continue;
4253         }
4254         else if(search)
4255         {
4256             if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4257                 continue;
4258         }
4259         bans[matches++] = ban;
4260         if(ban->expires)
4261             timed = 1;
4262     }
4263
4264     tbl.length = matches + 1;
4265     tbl.width = 4 + timed;
4266     tbl.flags = 0;
4267     tbl.flags = TABLE_NO_FREE;
4268     tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4269     tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4270     tbl.contents[0][0] = "Mask";
4271     tbl.contents[0][1] = "Set By";
4272     tbl.contents[0][2] = "Triggered";
4273     if(timed)
4274     {
4275         tbl.contents[0][3] = "Expires";
4276         tbl.contents[0][4] = "Reason";
4277     }
4278     else
4279         tbl.contents[0][3] = "Reason";
4280     if(!matches)
4281     {
4282         table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4283         reply("MSG_NONE");
4284         free(tbl.contents[0]);
4285         free(tbl.contents);
4286         return 0;
4287     }
4288
4289     msg_never = user_find_message(user, "MSG_NEVER");
4290     for(ii = 0; ii < matches; )
4291     {
4292         ban = bans[ii];
4293
4294         if(!timed)
4295             expires = "";
4296         else if(ban->expires)
4297             expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4298         else
4299             expires = msg_never;
4300
4301         if(ban->triggered)
4302             triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4303         else
4304             triggered = msg_never;
4305
4306         tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4307         tbl.contents[ii][0] = ban->mask;
4308         tbl.contents[ii][1] = ban->owner;
4309         tbl.contents[ii][2] = strdup(triggered);
4310         if(timed)
4311         {
4312             tbl.contents[ii][3] = strdup(expires);
4313             tbl.contents[ii][4] = ban->reason;
4314         }
4315         else
4316             tbl.contents[ii][3] = ban->reason;
4317     }
4318     table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4319     reply("MSG_MATCH_COUNT", matches);
4320     for(ii = 1; ii < tbl.length; ++ii)
4321     {
4322         free((char*)tbl.contents[ii][2]);
4323         if(timed)
4324             free((char*)tbl.contents[ii][3]);
4325         free(tbl.contents[ii]);
4326     }
4327     free(tbl.contents[0]);
4328     free(tbl.contents);
4329     return 1;
4330 }
4331
4332 static int
4333 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4334 {
4335     struct chanData *cData = channel->channel_info;
4336     if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4337         return 0;
4338     if(cData->topic_mask) 
4339     {
4340         if(cData->flags & CHANNEL_ADVTOPIC) 
4341         {
4342             //this implementation is a little bit dirty but i haven't found a better, faster solution, yet...
4343             char topicmask[TOPICLEN];
4344             int skipnum, topicpos = 0;
4345             char *ptr = cData->topic_mask;
4346             for(;*ptr;ptr++) { //replace all the %[0-9]* variables with *
4347                 switch(*ptr) {
4348                 case '%':
4349                     for(skipnum = 0; isdigit(ptr[1]) && skipnum < 3; ptr++, skipnum++) {} //skip up to 3 numbers
4350                     if(skipnum)
4351                         topicmask[topicpos++] = '*';
4352                     else
4353                         topicmask[topicpos++] = *ptr;
4354                     break;
4355                 default:
4356                     topicmask[topicpos++] = *ptr;
4357                     break;
4358                 }
4359             }
4360             topicmask[topicpos] = 0;
4361             return !match_ircglob(new_topic, topicmask);
4362         }
4363         else
4364             return !match_ircglob(new_topic, cData->topic_mask);
4365     }
4366     else if(cData->topic)
4367         return irccasecmp(new_topic, cData->topic);
4368     else
4369         return 0;
4370 }
4371
4372 static CHANSERV_FUNC(cmd_topic)
4373 {
4374     struct chanData *cData;
4375     char *topic;
4376
4377     cData = channel->channel_info;
4378     if(argc < 2)
4379     {
4380         if(cData->topic)
4381         {
4382             SetChannelTopic(channel, chanserv, cData->topic, 1);
4383             reply("CSMSG_TOPIC_SET", cData->topic);
4384             return 1;
4385         }
4386
4387         reply("CSMSG_NO_TOPIC", channel->name);
4388         return 0;
4389     }
4390
4391     topic = unsplit_string(argv + 1, argc - 1, NULL);
4392     /* If they say "!topic *", use an empty topic. */
4393     if((topic[0] == '*') && (topic[1] == 0))
4394         topic[0] = 0;
4395     if(bad_topic(channel, user, topic))
4396     {
4397         char *topic_mask = cData->topic_mask;
4398         if(topic_mask)
4399         {
4400             char new_topic[TOPICLEN+1], tchar;
4401             int pos=0, starpos=-1, dpos=0, len;
4402
4403             if(cData->flags & CHANNEL_ADVTOPIC) 
4404             {
4405                 //first check if there is a leading 'modifier id'
4406                 int advtopic_index = 0;
4407                 char numbuf[4];
4408                 int numpos;
4409                 for(; topic[pos]; pos++) 
4410                 {
4411                     if(topic[pos] == ' ') 
4412                     {
4413                         //leading number found, cut off and store value in advtopic_index
4414                         topic[pos] = 0;
4415                         advtopic_index = atoi(topic) - 1; //no zerobase
4416                         topic = &topic[pos+1];
4417                         /* If they say "!topic 2 *", unset advtopic id 2. */
4418                         if((topic[0] == '*') && (topic[1] == 0))
4419                             topic[0] = 0;
4420                     }
4421                     if(!isdigit(topic[pos]))
4422                         break;
4423                 }
4424                 if(advtopic_index < 0 || advtopic_index >= MAXADVTOPICENTRIES)
4425                 {
4426                     //invalid id!
4427                     reply("CSMSG_ADVTOPIC_INVALID_ID", advtopic_index+1);
4428                     return 0;
4429                 }
4430                 if(cData->advtopic[advtopic_index])
4431                     free(cData->advtopic[advtopic_index]);
4432                 cData->advtopic[advtopic_index] = (topic[0] ? strdup(topic) : NULL);
4433                 char *ptr = topic_mask;
4434                 while(*ptr && (dpos <= TOPICLEN))
4435                 {
4436                     switch(*ptr)
4437                     {
4438                     case '%':
4439                         ptr++;
4440                         for(numpos = 0; isdigit(*ptr) && numpos < 3; ptr++) {
4441                             numbuf[numpos++] = *ptr;
4442                         }
4443                         numbuf[numpos] = 0;
4444                         if(!numpos || (advtopic_index = atoi(numbuf)) <= 0 || advtopic_index > MAXADVTOPICENTRIES) {
4445                             ptr -= numpos+1;
4446                             new_topic[dpos++] = *ptr; //is % again
4447                             break;
4448                         }
4449                         advtopic_index--; //no zero base
4450                         if(!cData->advtopic[advtopic_index])
4451                             break; //just leave it empty
4452                         len = strlen(cData->advtopic[advtopic_index]);
4453                         if((dpos + len) > TOPICLEN)
4454                             len = TOPICLEN + 1 - dpos;
4455                         memcpy(new_topic+dpos, cData->advtopic[advtopic_index], len);
4456                         dpos += len;
4457                         break;
4458                     case '\\': 
4459                         ptr++; /* and fall through */
4460                         if(!*ptr) break;
4461                     default:
4462                         new_topic[dpos++] = *ptr;
4463                         ptr++;
4464                         break;
4465                     }
4466                 }
4467             } else {
4468                 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4469                 {
4470                     switch(tchar)
4471                     {
4472                     case '*':
4473                         if(starpos != -1)
4474                             goto bad_mask;
4475                         len = strlen(topic);
4476                         if((dpos + len) > TOPICLEN)
4477                             len = TOPICLEN + 1 - dpos;
4478                         memcpy(new_topic+dpos, topic, len);
4479                         dpos += len;
4480                         starpos = pos;
4481                         break;
4482                     case '\\': tchar = topic_mask[pos++]; /* and fall through */
4483                     default: new_topic[dpos++] = tchar; break;
4484                     }
4485                 }
4486                 if((dpos > TOPICLEN) || tchar)
4487                 {
4488                 bad_mask:
4489                     reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4490                     reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4491                     return 0;
4492                 }
4493             }
4494             new_topic[dpos] = 0;
4495             SetChannelTopic(channel, chanserv, new_topic, 1);
4496         } else {
4497             reply("CSMSG_TOPIC_LOCKED", channel->name);
4498             return 0;
4499         }
4500     }
4501     else
4502         SetChannelTopic(channel, chanserv, topic, 1);
4503
4504     if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4505     {
4506         /* Grab the topic and save it as the default topic. */
4507         free(cData->topic);
4508         cData->topic = strdup(channel->topic);
4509     }
4510
4511     return 1;
4512 }
4513
4514 static CHANSERV_FUNC(cmd_mode)
4515 {
4516     struct userData *uData;
4517     struct mod_chanmode *change;
4518     short base_oplevel;
4519     char fmt[MAXLEN];
4520
4521     if(argc < 2)
4522     {
4523         change = &channel->channel_info->modes;
4524         if(change->modes_set || change->modes_clear) {
4525             modcmd_chanmode_announce(change);
4526             reply("CSMSG_DEFAULTED_MODES", channel->name);
4527         } else
4528             reply("CSMSG_NO_MODES", channel->name);
4529         return 1;
4530     }
4531
4532     uData = GetChannelUser(channel->channel_info, user->handle_info);
4533     if (!uData)
4534         base_oplevel = MAXOPLEVEL;
4535     else if (uData->access >= UL_OWNER)
4536         base_oplevel = 1;
4537     else
4538         base_oplevel = 1 + UL_OWNER - uData->access;
4539     change = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4540     if(!change)
4541     {
4542         reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4543         return 0;
4544     }
4545
4546     if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4547        && mode_lock_violated(&channel->channel_info->modes, change))
4548     {
4549         char modes[MAXLEN];
4550         mod_chanmode_format(&channel->channel_info->modes, modes);
4551         reply("CSMSG_MODE_LOCKED", modes, channel->name);
4552         return 0;
4553     }
4554
4555     modcmd_chanmode_announce(change);
4556     mod_chanmode_format(change, fmt);
4557     mod_chanmode_free(change);
4558     reply("CSMSG_MODES_SET", fmt);
4559     return 1;
4560 }
4561
4562 static void
4563 chanserv_del_invite_mark(void *data)
4564 {
4565         struct ChanUser *chanuser = data;
4566         struct chanNode *channel = chanuser->chan;
4567         unsigned int i;
4568         if(!channel) return;
4569         for(i = 0; i < channel->invited.used; i++)
4570     {
4571         if(channel->invited.list[i] == chanuser->user) {
4572                         userList_remove(&channel->invited, chanuser->user);
4573                 }
4574         }
4575         free(chanuser);
4576 }
4577
4578 static CHANSERV_FUNC(cmd_invite)
4579 {
4580     struct userNode *invite;
4581     struct ChanUser *chanuser;
4582     unsigned int i;
4583
4584     if(argc > 1)
4585     {
4586         if(!(invite = GetUserH(argv[1])))
4587         {
4588             reply("MSG_NICK_UNKNOWN", argv[1]);
4589             return 0;
4590         }
4591     }
4592     else
4593         invite = user;
4594
4595     if(GetUserMode(channel, invite))
4596     {
4597         reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4598         return 0;
4599     }
4600     
4601     for(i = 0; i < channel->invited.used; i++)
4602     {
4603         if(channel->invited.list[i] == invite) {
4604             reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4605             return 0;
4606         }
4607     }
4608
4609     if(user != invite)
4610     {
4611         if(argc > 2)
4612         {
4613             char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4614             send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4615         }
4616         else
4617             send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4618     }
4619     irc_invite(chanserv, invite, channel);
4620     if(argc > 1)
4621         reply("CSMSG_INVITED_USER", argv[1], channel->name);
4622
4623     userList_append(&channel->invited, invite);
4624     chanuser = calloc(1, sizeof(*chanuser));
4625     chanuser->user=invite;
4626     chanuser->chan=channel;
4627     timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4628
4629     return 1;
4630 }
4631
4632 static CHANSERV_FUNC(cmd_inviteme)
4633 {
4634     if(GetUserMode(channel, user))
4635     {
4636         reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4637         return 0;
4638     }
4639     if(channel->channel_info
4640        && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4641     {
4642         reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4643         return 0;
4644     }
4645     irc_invite(cmd->parent->bot, user, channel);
4646     return 1;
4647 }
4648
4649 static CHANSERV_FUNC(cmd_invitemeall)
4650 {
4651     struct handle_info *target = user->handle_info;
4652     struct userData *uData;
4653
4654     if(!target->channels)
4655     {
4656         reply("CSMSG_SQUAT_ACCESS", target->handle);
4657         return 1;
4658     }
4659         
4660     for(uData = target->channels; uData; uData = uData->u_next)
4661     {
4662         struct chanData *cData = uData->channel;
4663         if(uData->access >= cData->lvlOpts[lvlInviteMe])
4664                 {
4665             irc_invite(cmd->parent->bot, user, cData->channel);
4666         }
4667     }
4668     return 1;
4669 }
4670
4671 static void
4672 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4673 {
4674     unsigned int combo;
4675     char buf1[INTERVALLEN], buf2[INTERVALLEN];
4676
4677     /* We display things based on two dimensions:
4678      * - Issue time: present or absent
4679      * - Expiration: revoked, expired, expires in future, or indefinite expiration
4680      * (in order of precedence, so something both expired and revoked
4681      * only counts as revoked)
4682      */
4683     combo = (suspended->issued ? 4 : 0)
4684         + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4685     switch(combo) {
4686     case 0: /* no issue time, indefinite expiration */
4687         reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4688         break;
4689     case 1: /* no issue time, expires in future */
4690         intervalString(buf1, suspended->expires-now, user->handle_info);
4691         reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4692         break;
4693     case 2: /* no issue time, expired */
4694         intervalString(buf1, now-suspended->expires, user->handle_info);
4695         reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4696         break;
4697     case 3: /* no issue time, revoked */
4698         intervalString(buf1, now-suspended->revoked, user->handle_info);
4699         reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4700         break;
4701     case 4: /* issue time set, indefinite expiration */
4702         intervalString(buf1, now-suspended->issued, user->handle_info);
4703         reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4704         break;
4705     case 5: /* issue time set, expires in future */
4706         intervalString(buf1, now-suspended->issued, user->handle_info);
4707         intervalString(buf2, suspended->expires-now, user->handle_info);
4708         reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4709         break;
4710     case 6: /* issue time set, expired */
4711         intervalString(buf1, now-suspended->issued, user->handle_info);
4712         intervalString(buf2, now-suspended->expires, user->handle_info);
4713         reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4714         break;
4715     case 7: /* issue time set, revoked */
4716         intervalString(buf1, now-suspended->issued, user->handle_info);
4717         intervalString(buf2, now-suspended->revoked, user->handle_info);
4718         reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4719         break;
4720     default:
4721         log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4722         return;
4723     }
4724 }
4725
4726 static void
4727 show_giveownership_info(struct svccmd *cmd, struct userNode *user, struct giveownership *giveownership)
4728 {
4729     char buf[MAXLEN];
4730     const char *fmt = "%a %b %d %H:%M %Y";
4731     strftime(buf, sizeof(buf), fmt, localtime(&giveownership->issued));
4732
4733     if(giveownership->staff_issuer)
4734     {
4735         if(giveownership->reason)
4736             reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership->old_owner,
4737                   giveownership->target, giveownership->target_access,
4738                   giveownership->staff_issuer, buf, giveownership->reason);
4739         else
4740             reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership->old_owner,
4741                   giveownership->target, giveownership->target_access,
4742                   giveownership->staff_issuer, buf);
4743     }
4744     else
4745     {
4746         reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership->old_owner, giveownership->target, giveownership->target_access, buf);
4747     }
4748 }
4749
4750 static CHANSERV_FUNC(cmd_info)
4751 {
4752     char modes[MAXLEN], buffer[INTERVALLEN];
4753     struct userData *uData, *owner;
4754     struct chanData *cData;
4755     struct do_not_register *dnr;
4756     struct note *note;
4757     dict_iterator_t it;
4758     int privileged;
4759
4760     cData = channel->channel_info;
4761     reply("CSMSG_CHANNEL_INFO", channel->name);
4762
4763     uData = GetChannelUser(cData, user->handle_info);
4764     if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4765     {
4766         mod_chanmode_format(&cData->modes, modes);
4767         reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4768         reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4769     }
4770
4771     for(it = dict_first(cData->notes); it; it = iter_next(it))
4772     {
4773         int padding;
4774
4775         note = iter_data(it);
4776         if(!note_type_visible_to_user(cData, note->type, user))
4777             continue;
4778
4779         padding = PADLEN - 1 - strlen(iter_key(it));
4780         reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4781     }
4782
4783     if(cData->max_time) {
4784         reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4785     } else {
4786         reply("CSMSG_CHANNEL_MAX", cData->max);
4787     }
4788     for(owner = cData->users; owner; owner = owner->next)
4789         if(owner->access == UL_OWNER)
4790             reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4791     reply("CSMSG_CHANNEL_USERS", cData->userCount);
4792     reply("CSMSG_CHANNEL_BANS", cData->banCount);
4793     reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4794
4795     privileged = IsStaff(user);
4796     if(privileged)
4797         reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4798     if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4799         reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4800
4801     if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4802         chanserv_show_dnrs(user, cmd, channel->name, NULL);
4803
4804     if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4805     {
4806         struct suspended *suspended;
4807         reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4808         for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4809             show_suspension_info(cmd, user, suspended);
4810     }
4811     else if(IsSuspended(cData))
4812     {
4813         reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4814         show_suspension_info(cmd, user, cData->suspended);
4815     }
4816     
4817     if(cData->giveownership && ((uData && (uData->access >= UL_COOWNER)) || IsStaff(user)))
4818     {
4819         struct giveownership *giveownership;
4820         reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel->name);
4821         for(giveownership = cData->giveownership; giveownership; giveownership = giveownership->previous)
4822             show_giveownership_info(cmd, user, giveownership);
4823     }
4824     return 1;
4825 }
4826
4827 static CHANSERV_FUNC(cmd_netinfo)
4828 {
4829     extern unsigned long boot_time;
4830     extern unsigned long burst_length;
4831     char interval[INTERVALLEN];
4832
4833     reply("CSMSG_NETWORK_INFO");
4834     reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4835     reply("CSMSG_NETWORK_USERS", dict_size(clients));
4836     reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4837     reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4838     reply("CSMSG_NETWORK_BANS", banCount);
4839     reply("CSMSG_NETWORK_CHANUSERS", userCount);
4840     reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4841     reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4842     return 1;
4843 }
4844
4845 static void
4846 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4847 {
4848     struct helpfile_table table;
4849     unsigned int nn;
4850     struct userNode *user;
4851     char *nick;
4852
4853     table.length = 0;
4854     table.width = 1;
4855     table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4856     table.contents = alloca(list->used*sizeof(*table.contents));
4857     for(nn=0; nn<list->used; nn++)
4858     {
4859         user = list->list[nn];
4860         if(user->modes & skip_flags)
4861             continue;
4862         if(IsBot(user))
4863             continue;
4864                 if(IsInvi(user))
4865                     continue;
4866         table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4867         if(IsAway(user))
4868         {
4869             nick = alloca(strlen(user->nick)+3);
4870             sprintf(nick, "(%s)", user->nick);
4871         }
4872         else
4873             nick = user->nick;
4874         table.contents[table.length][0] = nick;
4875         table.length++;
4876     }
4877     table_send(chanserv, to->nick, 0, NULL, table);
4878 }
4879
4880 static CHANSERV_FUNC(cmd_ircops)
4881 {
4882     reply("CSMSG_STAFF_OPERS");
4883     send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4884     return 1;
4885 }
4886
4887 static CHANSERV_FUNC(cmd_helpers)
4888 {
4889     reply("CSMSG_STAFF_HELPERS");
4890     send_staff_list(user, &curr_helpers, FLAGS_OPER);
4891     return 1;
4892 }
4893
4894 static CHANSERV_FUNC(cmd_staff)
4895 {
4896     reply("CSMSG_NETWORK_STAFF");
4897     cmd_ircops(CSFUNC_ARGS);
4898     cmd_helpers(CSFUNC_ARGS);
4899     return 1;
4900 }
4901
4902 static CHANSERV_FUNC(cmd_peek)
4903 {
4904     struct modeNode *mn;
4905     char modes[MODELEN];
4906     unsigned int n;
4907     struct helpfile_table table;
4908     int opcount = 0, voicecount = 0, srvcount = 0;
4909
4910     irc_make_chanmode(channel, modes);
4911
4912     reply("CSMSG_PEEK_INFO", channel->name);
4913     reply("CSMSG_PEEK_TOPIC", channel->topic);
4914     reply("CSMSG_PEEK_MODES", modes);
4915
4916     table.length = 0;
4917     table.width = 1;
4918     table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4919     table.contents = alloca(channel->members.used*sizeof(*table.contents));
4920     for(n = 0; n < channel->members.used; n++)
4921     {
4922         mn = channel->members.list[n];
4923         if(IsLocal(mn->user))
4924             srvcount++;
4925         else if(mn->modes & MODE_CHANOP)
4926             opcount++;
4927         else if(mn->modes & MODE_VOICE)
4928             voicecount++;
4929
4930         if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4931             continue;
4932         table.contents[table.length] = alloca(sizeof(**table.contents));
4933         table.contents[table.length][0] = mn->user->nick;
4934         table.length++;
4935     }
4936
4937     reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4938           (channel->members.used - opcount - voicecount - srvcount));
4939
4940     if(table.length)
4941     {
4942         reply("CSMSG_PEEK_OPS");
4943         table_send(chanserv, user->nick, 0, NULL, table);
4944     }
4945     else
4946         reply("CSMSG_PEEK_NO_OPS");
4947     return 1;
4948 }
4949
4950 static MODCMD_FUNC(cmd_wipeinfo)
4951 {
4952     struct handle_info *victim;
4953     struct userData *ud, *actor, *real_actor;
4954     unsigned int override = 0;
4955
4956     REQUIRE_PARAMS(2);
4957     actor = GetChannelUser(channel->channel_info, user->handle_info);
4958     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4959     if(!(victim = modcmd_get_handle_info(user, argv[1])))
4960         return 0;
4961     if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4962     {
4963         reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4964         return 0;
4965     }
4966     if((ud->access >= actor->access) && (ud != actor))
4967     {
4968         reply("MSG_USER_OUTRANKED", victim->handle);
4969         return 0;
4970     }
4971     if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4972         override = CMD_LOG_OVERRIDE;
4973     if(ud->info)
4974         free(ud->info);
4975     ud->info = NULL;
4976     reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4977     return 1 | override;
4978 }
4979
4980 static CHANSERV_FUNC(cmd_resync)
4981 {
4982     struct mod_chanmode *changes;
4983     struct chanData *cData = channel->channel_info;
4984     unsigned int ii, used;
4985
4986     changes = mod_chanmode_alloc(channel->members.used * 2);
4987     for(ii = used = 0; ii < channel->members.used; ++ii)
4988     {
4989         struct modeNode *mn = channel->members.list[ii];
4990         struct userData *uData;
4991
4992         if(IsService(mn->user))
4993             continue;
4994
4995         uData = GetChannelAccess(cData, mn->user->handle_info);
4996         if(!cData->lvlOpts[lvlGiveOps]
4997            || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4998         {
4999             if(!(mn->modes & MODE_CHANOP))
5000             {
5001                 if(!uData || IsUserAutoOp(uData)) 
5002                 {
5003                     changes->args[used].mode = MODE_CHANOP;
5004                     changes->args[used++].u.member = mn;
5005                     if(!(mn->modes & MODE_VOICE))
5006                     {
5007                         changes->args[used].mode = MODE_VOICE;
5008                         changes->args[used++].u.member = mn;
5009                     }
5010                 }
5011             }
5012         }
5013         else if(!cData->lvlOpts[lvlGiveVoice]
5014                 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
5015         {
5016             if(mn->modes & MODE_CHANOP)
5017             {
5018                 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
5019                 changes->args[used++].u.member = mn;
5020             }
5021             if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
5022             {
5023                 changes->args[used].mode = MODE_VOICE;
5024                 changes->args[used++].u.member = mn;
5025             }
5026         }
5027         else
5028         {
5029             if(mn->modes)
5030             {
5031                 changes->args[used].mode = MODE_REMOVE | mn->modes;
5032                 changes->args[used++].u.member = mn;
5033             }
5034         }
5035     }
5036     changes->argc = used;
5037     modcmd_chanmode_announce(changes);
5038     mod_chanmode_free(changes);
5039     reply("CSMSG_RESYNCED_USERS", channel->name);
5040     return 1;
5041 }
5042
5043 static CHANSERV_FUNC(cmd_seen)
5044 {
5045     struct userData *uData;
5046     struct handle_info *handle;
5047     char seen[INTERVALLEN];
5048
5049     REQUIRE_PARAMS(2);
5050
5051     if(!irccasecmp(argv[1], chanserv->nick))
5052     {
5053         reply("CSMSG_IS_CHANSERV");
5054         return 1;
5055     }
5056
5057     if(!(handle = get_handle_info(argv[1])))
5058     {
5059         reply("MSG_HANDLE_UNKNOWN", argv[1]);
5060         return 0;
5061     }
5062
5063     if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
5064     {
5065         reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
5066         return 0;
5067     }
5068
5069     if(uData->present)
5070         reply("CSMSG_USER_PRESENT", handle->handle);
5071     else if(uData->seen)
5072         reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
5073     else
5074         reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
5075
5076     if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
5077         reply("CSMSG_USER_VACATION", handle->handle);
5078
5079     return 1;
5080 }
5081
5082 static MODCMD_FUNC(cmd_names)
5083 {
5084     struct userNode *targ;
5085     struct userData *targData;
5086     unsigned int ii, pos;
5087     char buf[400];
5088
5089     for(ii=pos=0; ii<channel->members.used; ++ii)
5090     {
5091         targ = channel->members.list[ii]->user;
5092         targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
5093         if(!targData)
5094             continue;
5095         if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
5096         {
5097             buf[pos] = 0;
5098             reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5099             pos = 0;
5100         }
5101         buf[pos++] = ' ';
5102         if(IsUserSuspended(targData))
5103             buf[pos++] = 's';
5104         pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
5105     }
5106     buf[pos] = 0;
5107     reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5108     reply("CSMSG_END_NAMES", channel->name);
5109     return 1;
5110 }
5111
5112 static int
5113 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
5114 {
5115     switch(ntype->visible_type)
5116     {
5117     case NOTE_VIS_ALL: return 1;
5118     case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
5119     case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
5120     }
5121 }
5122
5123 static int
5124 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
5125 {
5126     struct userData *uData;
5127
5128     switch(ntype->set_access_type)
5129     {
5130     case NOTE_SET_CHANNEL_ACCESS:
5131         if(!user->handle_info)
5132             return 0;
5133         if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
5134             return 0;
5135         return uData->access >= ntype->set_access.min_ulevel;
5136     case NOTE_SET_CHANNEL_SETTER:
5137         return check_user_level(channel, user, lvlSetters, 1, 0);
5138     case NOTE_SET_PRIVILEGED: default:
5139         return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
5140     }
5141 }
5142
5143 static CHANSERV_FUNC(cmd_note)
5144 {
5145     struct chanData *cData;
5146     struct note *note;
5147     struct note_type *ntype;
5148
5149     cData = channel->channel_info;
5150     if(!cData)
5151     {
5152         reply("CSMSG_NOT_REGISTERED", channel->name);
5153         return 0;
5154     }
5155
5156     /* If no arguments, show all visible notes for the channel. */
5157     if(argc < 2)
5158     {
5159         dict_iterator_t it;
5160         unsigned int count;
5161
5162         for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5163         {
5164             note = iter_data(it);
5165             if(!note_type_visible_to_user(cData, note->type, user))
5166                 continue;
5167             if(!count++)
5168                 reply("CSMSG_NOTELIST_HEADER", channel->name);
5169             reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5170         }
5171         if(count)
5172             reply("CSMSG_NOTELIST_END", channel->name);
5173         else
5174             reply("CSMSG_NOTELIST_EMPTY", channel->name);
5175     }
5176     /* If one argument, show the named note. */
5177     else if(argc == 2)
5178     {
5179         if((note = dict_find(cData->notes, argv[1], NULL))
5180            && note_type_visible_to_user(cData, note->type, user))
5181         {
5182             reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5183         }
5184         else if((ntype = dict_find(note_types, argv[1], NULL))
5185                 && note_type_visible_to_user(NULL, ntype, user))
5186         {
5187             reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5188             return 0;
5189         }
5190         else
5191         {
5192             reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5193             return 0;
5194         }
5195     }
5196     /* Assume they're trying to set a note. */
5197     else
5198     {
5199         char *note_text;
5200         ntype = dict_find(note_types, argv[1], NULL);
5201         if(!ntype)
5202         {
5203             reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5204             return 0;
5205         }
5206         else if(note_type_settable_by_user(channel, ntype, user))
5207         {
5208             note_text = unsplit_string(argv+2, argc-2, NULL);
5209             if((note = dict_find(cData->notes, argv[1], NULL)))
5210                 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5211             chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5212             reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5213
5214             if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5215             {
5216                 /* The note is viewable to staff only, so return 0
5217                    to keep the invocation from getting logged (or
5218                    regular users can see it in !events). */
5219                 return 0;
5220             }
5221         }
5222         else
5223         {
5224             reply("CSMSG_NO_ACCESS");
5225             return 0;
5226         }
5227     }
5228     return 1;
5229 }
5230
5231 static CHANSERV_FUNC(cmd_delnote)
5232 {
5233     struct note *note;
5234
5235     REQUIRE_PARAMS(2);
5236     if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5237        || !note_type_settable_by_user(channel, note->type, user))
5238     {
5239         reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5240         return 0;
5241     }
5242     dict_remove(channel->channel_info->notes, note->type->name);
5243     reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5244     return 1;
5245 }
5246
5247 static CHANSERV_FUNC(cmd_events)
5248 {
5249     struct logSearch discrim;
5250     struct logReport report;
5251     unsigned int matches, limit;
5252
5253     limit = (argc > 1) ? atoi(argv[1]) : 10;
5254     if(limit < 1 || limit > 200)
5255         limit = 10;
5256
5257     memset(&discrim, 0, sizeof(discrim));
5258     discrim.masks.bot = chanserv;
5259     discrim.masks.channel_name = channel->name;
5260     if(argc > 2)
5261         discrim.masks.command = argv[2];
5262     discrim.limit = limit;
5263     discrim.max_time = INT_MAX;
5264     discrim.severities = 1 << LOG_COMMAND;
5265     report.reporter = chanserv;
5266     report.user = user;
5267     reply("CSMSG_EVENT_SEARCH_RESULTS");
5268     matches = log_entry_search(&discrim, log_report_entry, &report);
5269     if(matches)
5270         reply("MSG_MATCH_COUNT", matches);
5271     else
5272         reply("MSG_NO_MATCHES");
5273     return 1;
5274 }
5275
5276 static CHANSERV_FUNC(cmd_say)
5277 {
5278     char *msg;
5279     if(channel)
5280     {
5281         REQUIRE_PARAMS(2);
5282         msg = unsplit_string(argv + 1, argc - 1, NULL);
5283         send_channel_message(channel, cmd->parent->bot, "%s", msg);
5284     }
5285     else if(*argv[1] == '*' && argv[1][1] != '\0')
5286     {
5287         struct handle_info *hi;
5288         struct userNode *authed;
5289
5290         REQUIRE_PARAMS(3);
5291         msg = unsplit_string(argv + 2, argc - 2, NULL);
5292
5293         if (!(hi = get_handle_info(argv[1] + 1)))
5294         {
5295             reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5296             return 0;
5297         }
5298
5299         for (authed = hi->users; authed; authed = authed->next_authed)
5300             send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5301     }
5302     else if(GetUserH(argv[1]))
5303     {
5304         REQUIRE_PARAMS(3);
5305         msg = unsplit_string(argv + 2, argc - 2, NULL);
5306         send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5307     }
5308     else
5309     {
5310         reply("MSG_NOT_TARGET_NAME");
5311         return 0;
5312     }
5313     return 1;
5314 }
5315
5316 static CHANSERV_FUNC(cmd_emote)
5317 {
5318     char *msg;
5319     assert(argc >= 2);
5320     if(channel)
5321     {
5322         /* CTCP is so annoying. */
5323         msg = unsplit_string(argv + 1, argc - 1, NULL);
5324         send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5325     }
5326     else if(*argv[1] == '*' && argv[1][1] != '\0')
5327     {
5328         struct handle_info *hi;
5329         struct userNode *authed;
5330
5331         REQUIRE_PARAMS(3);
5332         msg = unsplit_string(argv + 2, argc - 2, NULL);
5333
5334         if (!(hi = get_handle_info(argv[1] + 1)))
5335         {
5336             reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5337             return 0;
5338         }
5339
5340         for (authed = hi->users; authed; authed = authed->next_authed)
5341             send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5342     }
5343     else if(GetUserH(argv[1]))
5344     {
5345         msg = unsplit_string(argv + 2, argc - 2, NULL);
5346         send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5347     }
5348     else
5349     {
5350         reply("MSG_NOT_TARGET_NAME");
5351         return 0;
5352     }
5353     return 1;
5354 }
5355
5356 struct channelList *
5357 chanserv_support_channels(void)
5358 {
5359     return &chanserv_conf.support_channels;
5360 }
5361
5362 static CHANSERV_FUNC(cmd_expire)
5363 {
5364     int channel_count = registered_channels;
5365     expire_channels(chanserv);
5366     reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5367     return 1;
5368 }
5369
5370 static void
5371 chanserv_expire_suspension(void *data)
5372 {
5373     struct suspended *suspended = data;
5374     struct chanNode *channel;
5375     unsigned int ii;
5376
5377     /* Update the channel registration data structure. */
5378     if(!suspended->expires || (now < suspended->expires))
5379         suspended->revoked = now;
5380     channel = suspended->cData->channel;
5381     suspended->cData->channel = channel;
5382     suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5383
5384     /* If appropriate, re-join ChanServ to the channel. */
5385     if(!IsOffChannel(suspended->cData))
5386     {
5387         spamserv_cs_suspend(channel, 0, 0, NULL);
5388         ss_cs_join_channel(channel, 1);
5389     }
5390
5391     /* Mark everyone currently in the channel as present. */
5392     for(ii = 0; ii < channel->members.used; ++ii)
5393     {
5394         struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5395         if(uData)
5396         {
5397             uData->present = 1;
5398             uData->seen = now;
5399         }
5400     }
5401 }
5402
5403 static CHANSERV_FUNC(cmd_csuspend)
5404 {
5405     struct suspended *suspended;
5406     char reason[MAXLEN];
5407     unsigned long expiry, duration;
5408     struct userData *uData;
5409
5410     REQUIRE_PARAMS(3);
5411
5412     if(IsProtected(channel->channel_info) && !IsOper(user))
5413     {
5414         reply("CSMSG_SUSPEND_NODELETE", channel->name);
5415         return 0;
5416     }
5417
5418     if(argv[1][0] == '!')
5419         argv[1]++;
5420     else if(IsSuspended(channel->channel_info))
5421     {
5422         reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5423         show_suspension_info(cmd, user, channel->channel_info->suspended);
5424         return 0;
5425     }
5426
5427     if(!strcmp(argv[1], "0"))
5428         expiry = 0;
5429     else if((duration = ParseInterval(argv[1])))
5430         expiry = now + duration;
5431     else
5432     {
5433         reply("MSG_INVALID_DURATION", argv[1]);
5434         return 0;
5435     }
5436
5437     unsplit_string(argv + 2, argc - 2, reason);
5438
5439     suspended = calloc(1, sizeof(*suspended));
5440     suspended->revoked = 0;
5441     suspended->issued = now;
5442     suspended->suspender = strdup(user->handle_info->handle);
5443     suspended->expires = expiry;
5444     suspended->reason = strdup(reason);
5445     suspended->cData = channel->channel_info;
5446     suspended->previous = suspended->cData->suspended;
5447     suspended->cData->suspended = suspended;
5448
5449     if(suspended->expires)
5450         timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5451
5452     if(IsSuspended(channel->channel_info))
5453     {
5454         suspended->previous->revoked = now;
5455         if(suspended->previous->expires)
5456             timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5457         sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5458         global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5459     }
5460     else
5461     {
5462         /* Mark all users in channel as absent. */
5463         for(uData = channel->channel_info->users; uData; uData = uData->next)
5464         {
5465             if(uData->present)
5466             {
5467                 uData->seen = now;
5468                 uData->present = 0;
5469             }
5470         }
5471
5472         /* Mark the channel as suspended, then part. */
5473         channel->channel_info->flags |= CHANNEL_SUSPENDED;
5474         spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5475         DelChannelUser(chanserv, channel, suspended->reason, 0);
5476         reply("CSMSG_SUSPENDED", channel->name);
5477         sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5478         global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5479     }
5480     return 1;
5481 }
5482
5483 static CHANSERV_FUNC(cmd_cunsuspend)
5484 {
5485     struct suspended *suspended;
5486     char message[MAXLEN];
5487
5488     if(!IsSuspended(channel->channel_info))
5489     {
5490         reply("CSMSG_NOT_SUSPENDED", channel->name);
5491         return 0;
5492     }
5493
5494     suspended = channel->channel_info->suspended;
5495
5496     /* Expire the suspension and join ChanServ to the channel. */
5497     timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5498     chanserv_expire_suspension(suspended);
5499     reply("CSMSG_UNSUSPENDED", channel->name);
5500     sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5501     global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5502     return 1;
5503 }
5504
5505 typedef struct chanservSearch
5506 {
5507     char *name;
5508     char *registrar;
5509
5510     unsigned long unvisited;
5511     unsigned long registered;
5512
5513     unsigned long flags;
5514     unsigned int limit;
5515 } *search_t;
5516
5517 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5518
5519 static search_t
5520 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5521 {
5522     search_t search;
5523     unsigned int i;
5524
5525     search = malloc(sizeof(struct chanservSearch));
5526     memset(search, 0, sizeof(*search));
5527     search->limit = 25;
5528
5529     for(i = 0; i < argc; i++)
5530     {
5531         /* Assume all criteria require arguments. */
5532         if(i == (argc - 1))
5533         {
5534             send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5535             goto fail;
5536         }
5537
5538         if(!irccasecmp(argv[i], "name"))
5539             search->name = argv[++i];
5540         else if(!irccasecmp(argv[i], "registrar"))
5541             search->registrar = argv[++i];
5542         else if(!irccasecmp(argv[i], "unvisited"))
5543             search->unvisited = ParseInterval(argv[++i]);
5544         else if(!irccasecmp(argv[i], "registered"))
5545             search->registered = ParseInterval(argv[++i]);
5546         else if(!irccasecmp(argv[i], "flags"))
5547         {
5548             i++;
5549             if(!irccasecmp(argv[i], "nodelete"))
5550                 search->flags |= CHANNEL_NODELETE;
5551             else if(!irccasecmp(argv[i], "suspended"))
5552                 search->flags |= CHANNEL_SUSPENDED;
5553             else if(!irccasecmp(argv[i], "unreviewed"))
5554                 search->flags |= CHANNEL_UNREVIEWED;
5555             else
5556             {
5557                 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5558                 goto fail;
5559             }
5560         }
5561         else if(!irccasecmp(argv[i], "limit"))
5562             search->limit = strtoul(argv[++i], NULL, 10);
5563         else
5564         {
5565             send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5566             goto fail;
5567         }
5568     }
5569
5570     if(search->name && !strcmp(search->name, "*"))
5571         search->name = 0;
5572     if(search->registrar && !strcmp(search->registrar, "*"))
5573         search->registrar = 0;
5574
5575     return search;
5576   fail:
5577     free(search);
5578     return NULL;
5579 }
5580
5581 static int
5582 chanserv_channel_match(struct chanData *channel, search_t search)
5583 {
5584     const char *name = channel->channel->name;
5585     if((search->name && !match_ircglob(name, search->name)) ||
5586        (search->registrar && !channel->registrar) ||
5587        (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5588        (search->unvisited && (now - channel->visited) < search->unvisited) ||
5589        (search->registered && (now - channel->registered) > search->registered) ||
5590        (search->flags && ((search->flags & channel->flags) != search->flags)))
5591         return 0;
5592
5593     return 1;
5594 }
5595
5596 static unsigned int
5597 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5598 {
5599     struct chanData *channel;
5600     unsigned int matches = 0;
5601
5602     for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5603     {
5604         if(!chanserv_channel_match(channel, search))
5605             continue;
5606         matches++;
5607         smf(channel, data);
5608     }
5609
5610     return matches;
5611 }
5612
5613 static void
5614 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5615 {
5616 }
5617
5618 static void
5619 search_print(struct chanData *channel, void *data)
5620 {
5621     send_message_type(4, data, chanserv, "%s", channel->channel->name);
5622 }
5623
5624 static CHANSERV_FUNC(cmd_search)
5625 {
5626     search_t search;
5627     unsigned int matches;
5628     channel_search_func action;
5629
5630     REQUIRE_PARAMS(3);
5631
5632     if(!irccasecmp(argv[1], "count"))
5633         action = search_count;
5634     else if(!irccasecmp(argv[1], "print"))
5635         action = search_print;
5636     else
5637     {
5638         reply("CSMSG_ACTION_INVALID", argv[1]);
5639         return 0;
5640     }
5641
5642     search = chanserv_search_create(user, argc - 2, argv + 2);
5643     if(!search)
5644         return 0;
5645
5646     if(action == search_count)
5647         search->limit = INT_MAX;
5648
5649     if(action == search_print)
5650         reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5651
5652     matches = chanserv_channel_search(search, action, user);
5653
5654     if(matches)
5655         reply("MSG_MATCH_COUNT", matches);
5656     else
5657         reply("MSG_NO_MATCHES");
5658
5659     free(search);
5660     return 1;
5661 }
5662
5663 static CHANSERV_FUNC(cmd_unvisited)
5664 {
5665     struct chanData *cData;
5666     unsigned long interval = chanserv_conf.channel_expire_delay;
5667     char buffer[INTERVALLEN];
5668     unsigned int limit = 25, matches = 0;
5669
5670     if(argc > 1)
5671     {
5672         interval = ParseInterval(argv[1]);
5673         if(argc > 2)
5674             limit = atoi(argv[2]);
5675     }
5676
5677     intervalString(buffer, interval, user->handle_info);
5678     reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5679
5680     for(cData = channelList; cData && matches < limit; cData = cData->next)
5681     {
5682         if((now - cData->visited) < interval)
5683             continue;
5684
5685         intervalString(buffer, now - cData->visited, user->handle_info);
5686         reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5687         matches++;
5688     }
5689
5690     return 1;
5691 }
5692
5693 static MODCMD_FUNC(chan_opt_defaulttopic)
5694 {
5695     if(argc > 1)
5696     {
5697         char *topic;
5698
5699         if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5700         {
5701             reply("CSMSG_TOPIC_LOCKED", channel->name);
5702             return 0;
5703         }
5704
5705         topic = unsplit_string(argv+1, argc-1, NULL);
5706
5707         free(channel->channel_info->topic);
5708         if(topic[0] == '*' && topic[1] == 0)
5709         {
5710             topic = channel->channel_info->topic = NULL;
5711         }
5712         else
5713         {
5714             topic = channel->channel_info->topic = strdup(topic);
5715             if(channel->channel_info->topic_mask
5716                && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5717                 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5718         }
5719         SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5720     }
5721
5722     if(channel->channel_info->topic)
5723         reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5724     else
5725         reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5726     return 1;
5727 }
5728
5729 static MODCMD_FUNC(chan_opt_topicmask)
5730 {
5731     if(argc > 1)
5732     {
5733         struct chanData *cData = channel->channel_info;
5734         char *mask;
5735
5736         if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5737         {
5738             reply("CSMSG_TOPIC_LOCKED", channel->name);
5739             return 0;
5740         }
5741
5742         mask = unsplit_string(argv+1, argc-1, NULL);
5743
5744         if(cData->topic_mask)
5745             free(cData->topic_mask);
5746         if(mask[0] == '*' && mask[1] == 0)
5747         {
5748             cData->topic_mask = 0;
5749         }
5750         else
5751         {
5752             cData->topic_mask = strdup(mask);
5753             if(!cData->topic)
5754                 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5755             else if(!match_ircglob(cData->topic, cData->topic_mask))
5756                 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5757         }
5758     }
5759
5760     if(channel->channel_info->topic_mask)
5761         reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5762     else
5763         reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5764     return 1;
5765 }
5766
5767 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5768 {
5769     if(argc > 1)
5770     {
5771         char *greeting = unsplit_string(argv+1, argc-1, NULL);
5772         char *previous;
5773
5774         previous = *data;
5775         if(greeting[0] == '*' && greeting[1] == 0)
5776             *data = NULL;
5777         else
5778         {
5779             unsigned int length = strlen(greeting);
5780             if(length > chanserv_conf.greeting_length)
5781             {
5782                 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5783                 return 0;
5784             }
5785             *data = strdup(greeting);
5786         }
5787         if(previous)
5788             free(previous);
5789     }
5790
5791     if(*data)
5792         reply(name, *data);
5793     else
5794         reply(name, user_find_message(user, "MSG_NONE"));
5795     return 1;
5796 }
5797
5798 static MODCMD_FUNC(chan_opt_greeting)
5799 {
5800     return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5801 }
5802
5803 static MODCMD_FUNC(chan_opt_usergreeting)
5804 {
5805     return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5806 }
5807
5808 static MODCMD_FUNC(chan_opt_modes)
5809 {
5810     struct mod_chanmode *new_modes;
5811     char modes[MAXLEN];
5812
5813     if(argc > 1)
5814     {
5815         if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5816         {
5817             reply("CSMSG_NO_ACCESS");
5818             return 0;
5819         }
5820         if(argv[1][0] == '*' && argv[1][1] == 0)
5821         {
5822             memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5823         }
5824         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)))
5825         {
5826             reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5827             return 0;
5828         }
5829         else if(new_modes->argc > 1)
5830         {
5831             reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5832             mod_chanmode_free(new_modes);
5833             return 0;
5834         }
5835         else
5836         {
5837             channel->channel_info->modes = *new_modes;
5838             modcmd_chanmode_announce(new_modes);
5839             mod_chanmode_free(new_modes);
5840         }
5841     }
5842
5843     mod_chanmode_format(&channel->channel_info->modes, modes);
5844     if(modes[0])
5845         reply("CSMSG_SET_MODES", modes);
5846     else
5847         reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5848     return 1;
5849 }
5850
5851 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5852 static int
5853 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5854 {
5855     struct chanData *cData = channel->channel_info;
5856     int value;
5857
5858     if(argc > 1)
5859     {
5860         /* Set flag according to value. */
5861         if(enabled_string(argv[1]))
5862         {
5863             cData->flags |= mask;
5864             value = 1;
5865         }
5866         else if(disabled_string(argv[1]))
5867         {
5868             cData->flags &= ~mask;
5869             value = 0;
5870         }
5871         else
5872         {
5873             reply("MSG_INVALID_BINARY", argv[1]);
5874             return 0;
5875         }
5876     }
5877     else
5878     {
5879         /* Find current option value. */
5880         value = (cData->flags & mask) ? 1 : 0;
5881     }
5882
5883     if(value)
5884         reply(name, user_find_message(user, "MSG_ON"));
5885     else
5886         reply(name, user_find_message(user, "MSG_OFF"));
5887     return 1;
5888 }
5889
5890 static MODCMD_FUNC(chan_opt_nodelete)
5891 {
5892     if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5893     {
5894         reply("MSG_SETTING_PRIVILEGED", argv[0]);
5895         return 0;
5896     }
5897
5898     CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5899 }
5900
5901 static MODCMD_FUNC(chan_opt_dynlimit)
5902 {
5903     CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5904 }
5905
5906 static MODCMD_FUNC(chan_opt_advtopic)
5907 {
5908     CHANNEL_BINARY_OPTION("CSMSG_SET_ADVTOPIC", CHANNEL_ADVTOPIC);
5909 }
5910
5911 static MODCMD_FUNC(chan_opt_offchannel)
5912 {
5913     struct chanData *cData = channel->channel_info;
5914     int value;
5915
5916     if(argc > 1)
5917     {
5918         /* Set flag according to value. */
5919         if(enabled_string(argv[1]))
5920         {
5921             if(!IsOffChannel(cData))
5922                 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5923             cData->flags |= CHANNEL_OFFCHANNEL;
5924             value = 1;
5925         }
5926         else if(disabled_string(argv[1]))
5927         {
5928             if(IsOffChannel(cData))
5929             {
5930                 struct mod_chanmode change;
5931                 mod_chanmode_init(&change);
5932                 change.argc = 1;
5933                 change.args[0].mode = MODE_CHANOP;
5934                 change.args[0].u.member = AddChannelUser(chanserv, channel);
5935                 mod_chanmode_announce(chanserv, channel, &change);
5936             }
5937             cData->flags &= ~CHANNEL_OFFCHANNEL;
5938             value = 0;
5939         }
5940         else
5941         {
5942             reply("MSG_INVALID_BINARY", argv[1]);
5943             return 0;
5944         }
5945     }
5946     else
5947     {
5948         /* Find current option value. */
5949         value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5950     }
5951
5952     if(value)
5953         reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5954     else
5955         reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5956     return 1;
5957 }
5958
5959 static MODCMD_FUNC(chan_opt_unreviewed)
5960 {
5961     struct chanData *cData = channel->channel_info;
5962     int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5963
5964     if(argc > 1)
5965     {
5966         int new_value;
5967
5968         /* The two directions can have different ACLs. */
5969         if(enabled_string(argv[1]))
5970             new_value = 1;
5971         else if(disabled_string(argv[1]))
5972             new_value = 0;
5973         else
5974         {
5975             reply("MSG_INVALID_BINARY", argv[1]);
5976             return 0;
5977         }
5978
5979         if (new_value != value)
5980         {
5981             struct svccmd *subcmd;
5982             char subcmd_name[32];
5983
5984             snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5985             subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5986             if(!subcmd)
5987             {
5988                 reply("MSG_COMMAND_DISABLED", subcmd_name);
5989                 return 0;
5990             }
5991             else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5992                 return 0;
5993
5994             if (new_value)
5995                 cData->flags |= CHANNEL_UNREVIEWED;
5996             else
5997             {
5998                 free(cData->registrar);
5999                 cData->registrar = strdup(user->handle_info->handle);
6000                 cData->flags &= ~CHANNEL_UNREVIEWED;
6001             }
6002             value = new_value;
6003         }
6004     }
6005
6006     if(value)
6007         reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
6008     else
6009         reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
6010     return 1;
6011 }
6012
6013 static MODCMD_FUNC(chan_opt_defaults)
6014 {
6015     struct userData *uData;
6016     struct chanData *cData;
6017     const char *confirm;
6018     enum levelOption lvlOpt;
6019     enum charOption chOpt;
6020
6021     cData = channel->channel_info;
6022     uData = GetChannelUser(cData, user->handle_info);
6023     if(!uData || (uData->access < UL_OWNER))
6024     {
6025         reply("CSMSG_OWNER_DEFAULTS", channel->name);
6026         return 0;
6027     }
6028     confirm = make_confirmation_string(uData);
6029     if((argc < 2) || strcmp(argv[1], confirm))
6030     {
6031         reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
6032         return 0;
6033     }
6034     cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
6035         | (cData->flags & CHANNEL_PRESERVED_FLAGS);
6036     cData->modes = chanserv_conf.default_modes;
6037     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6038         cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6039     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6040         cData->chOpts[chOpt] = charOptions[chOpt].default_value;
6041     reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
6042     return 1;
6043 }
6044
6045 static int
6046 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6047 {
6048     struct chanData *cData = channel->channel_info;
6049     struct userData *uData;
6050     unsigned short value;
6051
6052     if(argc > 1)
6053     {
6054         if(!check_user_level(channel, user, option, 1, 1))
6055         {
6056             reply("CSMSG_CANNOT_SET");
6057             return 0;
6058         }
6059         value = user_level_from_name(argv[1], UL_OWNER+1);
6060         if(!value && strcmp(argv[1], "0"))
6061         {
6062             reply("CSMSG_INVALID_ACCESS", argv[1]);
6063             return 0;
6064         }
6065         uData = GetChannelUser(cData, user->handle_info);
6066         if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
6067         {
6068             reply("CSMSG_BAD_SETLEVEL");
6069             return 0;
6070         }
6071         switch(option)
6072         {
6073         case lvlGiveVoice:
6074             if(value > cData->lvlOpts[lvlGiveOps])
6075             {
6076                 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
6077                 return 0;
6078             }
6079             break;
6080         case lvlGiveOps:
6081             if(value < cData->lvlOpts[lvlGiveVoice])
6082             {
6083                 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
6084                 return 0;
6085             }
6086             break;
6087         case lvlSetters:
6088             /* This test only applies to owners, since non-owners
6089              * trying to set an option to above their level get caught
6090              * by the CSMSG_BAD_SETLEVEL test above.
6091              */
6092             if(value > uData->access)
6093             {
6094                 reply("CSMSG_BAD_SETTERS");
6095                 return 0;
6096             }
6097             break;
6098         default:
6099             break;
6100         }
6101         cData->lvlOpts[option] = value;
6102     }
6103     reply(levelOptions[option].format_name, cData->lvlOpts[option]);
6104     return argc > 1;
6105 }
6106
6107 static MODCMD_FUNC(chan_opt_enfops)
6108 {
6109     return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
6110 }
6111
6112 static MODCMD_FUNC(chan_opt_giveops)
6113 {
6114     return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
6115 }
6116
6117 static MODCMD_FUNC(chan_opt_enfmodes)
6118 {
6119     return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
6120 }
6121
6122 static MODCMD_FUNC(chan_opt_enftopic)
6123 {
6124     return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
6125 }
6126
6127 static MODCMD_FUNC(chan_opt_pubcmd)
6128 {
6129     return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
6130 }
6131
6132 static MODCMD_FUNC(chan_opt_setters)
6133 {
6134     return channel_level_option(lvlSetters, CSFUNC_ARGS);
6135 }
6136
6137 static MODCMD_FUNC(chan_opt_ctcpusers)
6138 {
6139     return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
6140 }
6141
6142 static MODCMD_FUNC(chan_opt_userinfo)
6143 {
6144     return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
6145 }
6146
6147 static MODCMD_FUNC(chan_opt_givevoice)
6148 {
6149     return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
6150 }
6151
6152 static MODCMD_FUNC(chan_opt_topicsnarf)
6153 {
6154     return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6155 }
6156
6157 static MODCMD_FUNC(chan_opt_vote)
6158 {
6159     return channel_level_option(lvlVote, CSFUNC_ARGS);
6160 }
6161
6162 static MODCMD_FUNC(chan_opt_inviteme)
6163 {
6164     return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6165 }
6166
6167 static int
6168 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6169 {
6170     struct chanData *cData = channel->channel_info;
6171     int count = charOptions[option].count, idx;
6172
6173     if(argc > 1)
6174     {
6175         idx = atoi(argv[1]);
6176
6177         if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6178         {
6179             reply("CSMSG_INVALID_NUMERIC", idx);
6180             /* Show possible values. */
6181             for(idx = 0; idx < count; idx++)
6182                 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6183             return 0;
6184         }
6185
6186         cData->chOpts[option] = charOptions[option].values[idx].value;
6187     }
6188     else
6189     {
6190         /* Find current option value. */
6191       find_value:
6192         for(idx = 0;
6193             (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6194             idx++);
6195         if(idx == count)
6196         {
6197             /* Somehow, the option value is corrupt; reset it to the default. */
6198             cData->chOpts[option] = charOptions[option].default_value;
6199             goto find_value;
6200         }
6201     }
6202
6203     reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6204     return 1;
6205 }
6206
6207 static MODCMD_FUNC(chan_opt_protect)
6208 {
6209     return channel_multiple_option(chProtect, CSFUNC_ARGS);
6210 }
6211
6212 static MODCMD_FUNC(chan_opt_toys)
6213 {
6214     return channel_multiple_option(chToys, CSFUNC_ARGS);
6215 }
6216
6217 static MODCMD_FUNC(chan_opt_ctcpreaction)
6218 {
6219     return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6220 }
6221
6222 static MODCMD_FUNC(chan_opt_topicrefresh)
6223 {
6224     return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6225 }
6226
6227 static struct svccmd_list set_shows_list;
6228
6229 static void
6230 handle_svccmd_unbind(struct svccmd *target) {
6231     unsigned int ii;
6232     for(ii=0; ii<set_shows_list.used; ++ii)
6233         if(target == set_shows_list.list[ii])
6234             set_shows_list.used = 0;
6235 }
6236
6237 static CHANSERV_FUNC(cmd_set)
6238 {
6239     struct svccmd *subcmd;
6240     char buf[MAXLEN];
6241     unsigned int ii;
6242
6243     /* Check if we need to (re-)initialize set_shows_list. */
6244     if(!set_shows_list.used)
6245     {
6246         if(!set_shows_list.size)
6247         {
6248             set_shows_list.size = chanserv_conf.set_shows->used;
6249             set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6250         }
6251         for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6252         {
6253             const char *name = chanserv_conf.set_shows->list[ii];
6254             sprintf(buf, "%s %s", argv[0], name);
6255             subcmd = dict_find(cmd->parent->commands, buf, NULL);
6256             if(!subcmd)
6257             {
6258                 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6259                 continue;
6260             }
6261             svccmd_list_append(&set_shows_list, subcmd);
6262         }
6263     }
6264
6265     if(argc < 2)
6266     {
6267         reply("CSMSG_CHANNEL_OPTIONS");
6268         for(ii = 0; ii < set_shows_list.used; ii++)
6269         {
6270             subcmd = set_shows_list.list[ii];
6271             subcmd->command->func(user, channel, 1, argv+1, subcmd);
6272         }
6273         return 1;
6274     }
6275
6276     sprintf(buf, "%s %s", argv[0], argv[1]);
6277     subcmd = dict_find(cmd->parent->commands, buf, NULL);
6278     if(!subcmd)
6279     {
6280         reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6281         return 0;
6282     }
6283     if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6284     {
6285         reply("CSMSG_NO_ACCESS");
6286         return 0;
6287     }
6288
6289     argv[0] = "";
6290     argv[1] = buf;
6291     return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6292 }
6293
6294 static int
6295 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6296 {
6297     struct userData *uData;
6298
6299     uData = GetChannelAccess(channel->channel_info, user->handle_info);
6300     if(!uData)
6301     {
6302         reply("CSMSG_NOT_USER", channel->name);
6303         return 0;
6304     }
6305
6306     if(argc < 2)
6307     {
6308         /* Just show current option value. */
6309     }
6310     else if(enabled_string(argv[1]))
6311     {
6312         uData->flags |= mask;
6313     }
6314     else if(disabled_string(argv[1]))
6315     {
6316         uData->flags &= ~mask;
6317     }
6318     else
6319     {
6320         reply("MSG_INVALID_BINARY", argv[1]);
6321         return 0;
6322     }
6323
6324     reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6325     return 1;
6326 }
6327
6328 static MODCMD_FUNC(user_opt_noautoop)
6329 {
6330     struct userData *uData;
6331
6332     uData = GetChannelAccess(channel->channel_info, user->handle_info);
6333     if(!uData)
6334     {
6335         reply("CSMSG_NOT_USER", channel->name);
6336         return 0;
6337     }
6338     if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6339         return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6340     else
6341         return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6342 }
6343
6344 static MODCMD_FUNC(user_opt_autoinvite)
6345 {
6346     if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6347     {
6348         reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6349     }
6350     return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6351 }
6352
6353 static MODCMD_FUNC(user_opt_info)
6354 {
6355     struct userData *uData;
6356     char *infoline;
6357
6358     uData = GetChannelAccess(channel->channel_info, user->handle_info);
6359
6360     if(!uData)
6361     {
6362         /* If they got past the command restrictions (which require access)
6363          * but fail this test, we have some fool with security override on.
6364          */
6365         reply("CSMSG_NOT_USER", channel->name);
6366         return 0;
6367     }
6368
6369     if(argc > 1)
6370     {
6371         size_t bp;
6372         infoline = unsplit_string(argv + 1, argc - 1, NULL);
6373         if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6374         {
6375             reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6376             return 0;
6377         }
6378         bp = strcspn(infoline, "\001");
6379         if(infoline[bp])
6380         {
6381             reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6382             return 0;
6383         }
6384         if(uData->info)
6385             free(uData->info);
6386         if(infoline[0] == '*' && infoline[1] == 0)
6387             uData->info = NULL;
6388         else
6389             uData->info = strdup(infoline);
6390     }
6391     if(uData->info)
6392         reply("CSMSG_USET_INFO", uData->info);
6393     else
6394         reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6395     return 1;
6396 }
6397
6398 struct svccmd_list uset_shows_list;
6399
6400 static CHANSERV_FUNC(cmd_uset)
6401 {
6402     struct svccmd *subcmd;
6403     char buf[MAXLEN];
6404     unsigned int ii;
6405
6406     /* Check if we need to (re-)initialize uset_shows_list. */
6407     if(!uset_shows_list.used)
6408     {
6409         char *options[] =
6410         {
6411             "NoAutoOp", "AutoInvite", "Info"
6412         };
6413
6414         if(!uset_shows_list.size)
6415         {
6416             uset_shows_list.size = ArrayLength(options);
6417             uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6418         }
6419         for(ii = 0; ii < ArrayLength(options); ii++)
6420         {
6421             const char *name = options[ii];
6422             sprintf(buf, "%s %s", argv[0], name);
6423             subcmd = dict_find(cmd->parent->commands, buf, NULL);
6424             if(!subcmd)
6425             {
6426                 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6427                 continue;
6428             }
6429             svccmd_list_append(&uset_shows_list, subcmd);
6430         }
6431     }
6432
6433     if(argc < 2)
6434     {
6435         /* Do this so options are presented in a consistent order. */
6436         reply("CSMSG_USER_OPTIONS");
6437         for(ii = 0; ii < uset_shows_list.used; ii++)
6438             uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6439         return 1;
6440     }
6441
6442     sprintf(buf, "%s %s", argv[0], argv[1]);
6443     subcmd = dict_find(cmd->parent->commands, buf, NULL);
6444     if(!subcmd)
6445     {
6446         reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6447         return 0;
6448     }
6449
6450     return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6451 }
6452
6453 static CHANSERV_FUNC(cmd_giveownership)
6454 {
6455     struct handle_info *new_owner_hi;
6456     struct userData *new_owner;
6457     struct userData *curr_user;
6458     struct userData *invoker;
6459     struct chanData *cData = channel->channel_info;
6460     struct do_not_register *dnr;
6461     const char *confirm;
6462     struct giveownership *giveownership;
6463     unsigned int force, override;
6464     unsigned short co_access, new_owner_old_access;
6465     char reason[MAXLEN], transfer_reason[MAXLEN];
6466
6467     REQUIRE_PARAMS(2);
6468     curr_user = GetChannelAccess(cData, user->handle_info);
6469     force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6470
6471     struct userData *uData = _GetChannelUser(channel->channel_info, user->handle_info, 1, 0);
6472     override = ((cmd->effective_flags & MODCMD_REQUIRE_CHANUSER)
6473                 && (uData->access > 500)
6474                 && (!(uData = _GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
6475                     || uData->access < 500));
6476
6477     if(!curr_user || (curr_user->access != UL_OWNER))
6478     {
6479         struct userData *owner = NULL;
6480         for(curr_user = channel->channel_info->users;
6481             curr_user;
6482             curr_user = curr_user->next)
6483         {
6484             if(curr_user->access != UL_OWNER)
6485                 continue;
6486             if(owner)
6487             {
6488                 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6489                 return 0;
6490             }
6491             owner = curr_user;
6492         }
6493         curr_user = owner;
6494     }
6495     else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6496     {
6497         char delay[INTERVALLEN];
6498         intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6499         reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6500         return 0;
6501     }
6502     if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6503         return 0;
6504     if(new_owner_hi == user->handle_info)
6505     {
6506         reply("CSMSG_NO_TRANSFER_SELF");
6507         return 0;
6508     }
6509     new_owner = GetChannelAccess(cData, new_owner_hi);
6510     if(!new_owner)
6511     {
6512         if(force)
6513         {
6514             new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6515         }
6516         else
6517         {
6518             reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6519             return 0;
6520         }
6521     }
6522     if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6523     {
6524         reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6525         return 0;
6526     }
6527     if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6528         if(!IsHelping(user))
6529             reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6530         else
6531             chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6532         return 0;
6533     }
6534     invoker = GetChannelUser(cData, user->handle_info);
6535     if(invoker->access <= UL_OWNER)
6536     {
6537         confirm = make_confirmation_string(curr_user);
6538         if((argc < 3) || strcmp(argv[2], confirm))
6539         {
6540             reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6541             return 0;
6542         }
6543     }
6544     new_owner_old_access = new_owner->access;
6545     if(new_owner->access >= UL_COOWNER)
6546         co_access = new_owner->access;
6547     else
6548         co_access = UL_COOWNER;
6549     new_owner->access = UL_OWNER;
6550     if(curr_user)
6551         curr_user->access = co_access;
6552     cData->ownerTransfer = now;
6553     giveownership = calloc(1, sizeof(*giveownership));
6554     giveownership->issued = now;
6555     giveownership->old_owner = curr_user->handle->handle;
6556     giveownership->target = new_owner_hi->handle;
6557     giveownership->target_access = new_owner_old_access;
6558     if(override)
6559     {
6560         if(argc > (2 + force))
6561         {
6562             unsplit_string(argv + 2 + force, argc - 2 - force, transfer_reason);
6563             giveownership->reason = strdup(transfer_reason);
6564         }
6565         giveownership->staff_issuer = strdup(user->handle_info->handle);
6566     }
6567
6568     giveownership->previous = channel->channel_info->giveownership;
6569     channel->channel_info->giveownership = giveownership;
6570     reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6571     sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6572     global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6573     return 1;
6574 }
6575
6576 static CHANSERV_FUNC(cmd_suspend)
6577 {
6578     struct handle_info *hi;
6579     struct userData *actor, *real_actor, *target;
6580     unsigned int override = 0;
6581
6582     REQUIRE_PARAMS(2);
6583     if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6584     actor = GetChannelUser(channel->channel_info, user->handle_info);
6585     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6586     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6587     {
6588         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6589         return 0;
6590     }
6591     if(target->access >= actor->access)
6592     {
6593         reply("MSG_USER_OUTRANKED", hi->handle);
6594         return 0;
6595     }
6596     if(target->flags & USER_SUSPENDED)
6597     {
6598         reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6599         return 0;
6600     }
6601     if(target->present)
6602     {
6603         target->present = 0;
6604         target->seen = now;
6605     }
6606     if(!real_actor || target->access >= real_actor->access)
6607         override = CMD_LOG_OVERRIDE;
6608     target->flags |= USER_SUSPENDED;
6609     reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6610     return 1 | override;
6611 }
6612
6613 static CHANSERV_FUNC(cmd_unsuspend)
6614 {
6615     struct handle_info *hi;
6616     struct userData *actor, *real_actor, *target;
6617     unsigned int override = 0;
6618
6619     REQUIRE_PARAMS(2);
6620     if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6621     actor = GetChannelUser(channel->channel_info, user->handle_info);
6622     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6623     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6624     {
6625         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6626         return 0;
6627     }
6628     if(target->access >= actor->access)
6629     {
6630         reply("MSG_USER_OUTRANKED", hi->handle);
6631         return 0;
6632     }
6633     if(!(target->flags & USER_SUSPENDED))
6634     {
6635         reply("CSMSG_NOT_SUSPENDED", hi->handle);
6636         return 0;
6637     }
6638     if(!real_actor || target->access >= real_actor->access)
6639         override = CMD_LOG_OVERRIDE;
6640     target->flags &= ~USER_SUSPENDED;
6641     scan_user_presence(target, NULL);
6642     reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6643     return 1 | override;
6644 }
6645
6646 static MODCMD_FUNC(cmd_deleteme)
6647 {
6648     struct handle_info *hi;
6649     struct userData *target;
6650     const char *confirm_string;
6651     unsigned short access_level;
6652     char *channel_name;
6653
6654     hi = user->handle_info;
6655     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6656     {
6657         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6658         return 0;
6659     }
6660     if(target->access == UL_OWNER)
6661     {
6662         reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6663         return 0;
6664     }
6665     confirm_string = make_confirmation_string(target);
6666     if((argc < 2) || strcmp(argv[1], confirm_string))
6667     {
6668         reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6669         return 0;
6670     }
6671     access_level = target->access;
6672     channel_name = strdup(channel->name);
6673     del_channel_user(target, 1);
6674     reply("CSMSG_DELETED_YOU", access_level, channel_name);
6675     free(channel_name);
6676     return 1;
6677 }
6678
6679 static CHANSERV_FUNC(cmd_addvote)
6680 {
6681     struct chanData *cData = channel->channel_info;
6682     struct userData *uData, *target;
6683     struct handle_info *hi;
6684     if (!cData) return 0;
6685     REQUIRE_PARAMS(2);
6686     hi = user->handle_info;
6687     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6688     {
6689         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6690         return 0;
6691     }
6692     if(target->access < 300) {
6693         reply("CSMSG_NO_ACCESS");
6694         return 0;
6695     }
6696     if (cData->vote) {
6697         reply("CSMSG_ADDVOTE_FULL");
6698         return 0;
6699     }
6700     char *msg;
6701     msg = unsplit_string(argv + 1, argc - 1, NULL);
6702     cData->vote = strdup(msg);
6703     cData->vote_start=0;
6704     dict_delete(cData->vote_options);
6705     cData->vote_options = dict_new();
6706     dict_set_free_data(cData->vote_options, free_vote_options);
6707     for(uData = channel->channel_info->users; uData; uData = uData->next)
6708     {
6709         uData->voted = 0;
6710         uData->votefor = 0;
6711     }
6712     reply("CSMSG_ADDVOTE_DONE");
6713     return 1;
6714 }
6715
6716 static CHANSERV_FUNC(cmd_delvote)
6717 {
6718     struct chanData *cData = channel->channel_info;
6719     struct userData *target;
6720     struct handle_info *hi;
6721     if (!cData) return 0;
6722     hi = user->handle_info;
6723     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6724     {
6725         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6726         return 0;
6727     }
6728     if(target->access < 300) {
6729         reply("CSMSG_NO_ACCESS");
6730         return 0;
6731     }
6732     if (!cData->vote) {
6733         reply("CSMSG_NO_VOTE");
6734         return 0;
6735     }
6736     free(cData->vote);
6737     cData->vote = NULL;
6738     reply("CSMSG_DELVOTE_DONE");
6739     return 1;
6740 }
6741
6742 static CHANSERV_FUNC(cmd_addoption)
6743 {
6744     struct chanData *cData = channel->channel_info;
6745     struct userData *target;
6746     struct handle_info *hi;
6747     if (!cData) return 0;
6748     REQUIRE_PARAMS(2);
6749     hi = user->handle_info;
6750     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6751     {
6752         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6753         return 0;
6754     }
6755     if(target->access < 300) {
6756         reply("CSMSG_NO_ACCESS");
6757         return 0;
6758     }
6759     if (!cData->vote) {
6760         reply("CSMSG_NO_VOTE");
6761         return 0;
6762     }
6763     
6764     char *msg;
6765     
6766     msg = unsplit_string(argv + 1, argc - 1, NULL);
6767     
6768     dict_iterator_t it;
6769     unsigned int lastid = 1;
6770     for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6771         struct vote_option *cvOpt = iter_data(it);
6772         if(cvOpt->option_id > lastid)
6773             lastid = cvOpt->option_id;
6774     }
6775     struct vote_option *vOpt;
6776     vOpt = calloc(1, sizeof(*vOpt));
6777     vOpt->name = strdup(msg);
6778     vOpt->option_id = (lastid + 1);
6779     char str[50];
6780     sprintf(str,"%i",(lastid + 1));
6781     vOpt->option_str = strdup(str);
6782     vOpt->voted = 0;
6783     dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6784     
6785     reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6786     return 1;
6787 }
6788
6789 static CHANSERV_FUNC(cmd_deloption)
6790 {
6791     struct chanData *cData = channel->channel_info;
6792     struct userData *uData, *target;
6793     struct handle_info *hi;
6794     if (!cData) return 0;
6795     REQUIRE_PARAMS(2);
6796     hi = user->handle_info;
6797     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6798     {
6799         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6800         return 0;
6801     }
6802     if(target->access < 300) {
6803         reply("CSMSG_NO_ACCESS");
6804         return 0;
6805     }
6806     if (!cData->vote) {
6807         reply("CSMSG_NO_VOTE");
6808         return 0;
6809     }
6810     if(cData->vote_start) {
6811         if(dict_size(cData->vote_options) < 3) {
6812             reply("CSMSG_VOTE_NEED_OPTIONS");
6813             return 0;
6814         }
6815     }
6816     
6817     int find_id = atoi(argv[1]);
6818     int ii = 0;
6819     unsigned int found = 0;
6820     dict_iterator_t it;
6821     
6822     for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6823         ii++;
6824         if (find_id == ii) {
6825             struct vote_option *vOpt = iter_data(it);
6826             found = vOpt->option_id;
6827             char str[50];
6828             sprintf(str,"%i",vOpt->option_id);
6829             dict_remove(cData->vote_options, str);
6830         }
6831     }
6832     
6833     if(found > 0) {
6834         for(uData = channel->channel_info->users; uData; uData = uData->next) {
6835             if(uData->votefor == found) {
6836                 uData->voted = 0;
6837                 uData->votefor = 0;
6838             }
6839         }
6840         reply("CSMSG_DELOPTION_DONE");
6841         return 1;
6842     } else {
6843         reply("CSMSG_DELOPTION_NONE");
6844         return 0;
6845     }
6846 }
6847
6848 static CHANSERV_FUNC(cmd_vote)
6849 {
6850     struct chanData *cData = channel->channel_info;
6851     struct userData *target;
6852     struct handle_info *hi;
6853     unsigned int votedfor = 0;
6854     char *votedfor_str = NULL;
6855     
6856     if (!cData || !cData->vote) {
6857         reply("CSMSG_NO_VOTE");
6858         return 0;
6859     }
6860     if(argc > 1 && cData->vote_start) {
6861         hi = user->handle_info;
6862         if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6863         {
6864             reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6865             return 0;
6866         }
6867         if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6868             reply("CSMSG_NO_ACCESS");
6869             return 0;
6870         }
6871         if(target->voted) {
6872             reply("CSMSG_VOTE_VOTED");
6873             return 0;
6874         }
6875         int find_id = atoi(argv[1]);
6876         int ii = 0;
6877         dict_iterator_t it;
6878         for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6879             ii++;
6880             if (find_id == ii) {
6881                 struct vote_option *vOpt = iter_data(it);
6882                 vOpt->voted++;
6883                 target->voted = 1;
6884                 target->votefor = vOpt->option_id;
6885                 votedfor = vOpt->option_id;
6886                 votedfor_str = vOpt->name;
6887             }
6888         }
6889         if(votedfor == 0) {
6890             reply("CSMSG_VOTE_INVALID");
6891             return 0;
6892         }
6893     }
6894     if (!cData->vote_start) {
6895         reply("CSMSG_VOTE_NOT_STARTED");
6896     }
6897     reply("CSMSG_VOTE_QUESTION",cData->vote);
6898     
6899     unsigned int voteid = 0;
6900     dict_iterator_t it;
6901     
6902     for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6903         struct vote_option *vOpt = iter_data(it);
6904         voteid++;
6905         reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6906     }
6907     if(argc > 1 && cData->vote_start && votedfor_str) {
6908         reply("CSMSG_VOTE_DONE",votedfor_str);
6909     }
6910     return 1;
6911 }
6912
6913 static CHANSERV_FUNC(cmd_startvote)
6914 {
6915     struct chanData *cData = channel->channel_info;
6916     struct userData *target;
6917     struct handle_info *hi;
6918     if (!cData) return 0;
6919     hi = user->handle_info;
6920     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6921     {
6922         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6923         return 0;
6924     }
6925     if(target->access < 300) {
6926         reply("CSMSG_NO_ACCESS");
6927         return 0;
6928     }
6929     if (!cData->vote) {
6930         reply("CSMSG_NO_VOTE");
6931         return 0;
6932     }
6933     if(cData->vote_start) {
6934         reply("CSMSG_STARTVOTE_RUNNING");
6935         return 0;
6936     }
6937     if(dict_size(cData->vote_options) < 2) {
6938         reply("CSMSG_VOTE_NEED_OPTIONS");
6939         return 0;
6940     }
6941     cData->vote_start = 1;
6942     char response[MAXLEN];
6943     sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6944     irc_privmsg(cmd->parent->bot, channel->name, response);
6945     sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6946     irc_privmsg(cmd->parent->bot, channel->name, response);
6947     unsigned int voteid = 0;
6948     dict_iterator_t it;
6949     for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6950         struct vote_option *vOpt = iter_data(it);
6951         voteid++;
6952         sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6953         irc_privmsg(cmd->parent->bot, channel->name, response);
6954     }
6955     sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6956     irc_privmsg(cmd->parent->bot, channel->name, response);
6957     sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6958     irc_privmsg(cmd->parent->bot, channel->name, response);
6959     return 1;
6960 }
6961
6962 static CHANSERV_FUNC(cmd_endvote)
6963 {
6964     struct chanData *cData = channel->channel_info;
6965     struct userData *target;
6966     struct handle_info *hi;
6967     if (!cData) return 0;
6968     hi = user->handle_info;
6969     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6970     {
6971         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6972         return 0;
6973     }
6974     if(target->access < 300) {
6975         reply("CSMSG_NO_ACCESS");
6976         return 0;
6977     }
6978     if (!cData->vote) {
6979         reply("CSMSG_NO_VOTE");
6980         return 0;
6981     }
6982     if(!cData->vote_start) {
6983         reply("CSMSG_ENDVOTE_STOPPED");
6984         return 0;
6985     }
6986     cData->vote_start = 0;
6987     reply("CSMSG_ENDVOTE_DONE");
6988     return 1;
6989 }
6990
6991 static CHANSERV_FUNC(cmd_voteresults)
6992 {
6993     struct chanData *cData = channel->channel_info;
6994     struct userData *target;
6995     struct handle_info *hi;
6996     if (!cData) return 0;
6997     if (!cData->vote) {
6998         reply("CSMSG_NO_VOTE");
6999         return 0;
7000     }
7001     if (argc > 1 && !irccasecmp(argv[1], "*")) {
7002         hi = user->handle_info;
7003         if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
7004         {
7005             reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
7006             return 0;
7007         }
7008         if(target->access < 300) {
7009             reply("CSMSG_NO_ACCESS");
7010             return 0;
7011         }
7012         char response[MAXLEN];
7013         sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
7014         irc_privmsg(cmd->parent->bot, channel->name, response);
7015         unsigned int voteid = 0;
7016         dict_iterator_t it;
7017         for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
7018             struct vote_option *vOpt = iter_data(it);
7019             voteid++;
7020             sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
7021             irc_privmsg(cmd->parent->bot, channel->name, response);
7022         }
7023     } else {
7024         reply("CSMSG_VOTE_QUESTION",cData->vote);
7025         unsigned int voteid = 0;
7026        dict_iterator_t it;
7027         for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
7028             struct vote_option *vOpt = iter_data(it);
7029             voteid++;
7030             reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
7031         }
7032     }
7033     return 1;
7034 }
7035
7036 static void
7037 chanserv_refresh_topics(UNUSED_ARG(void *data))
7038 {
7039     unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
7040     struct chanData *cData;
7041     char opt;
7042
7043     for(cData = channelList; cData; cData = cData->next)
7044     {
7045         if(IsSuspended(cData))
7046             continue;
7047         opt = cData->chOpts[chTopicRefresh];
7048         if(opt == 'n')
7049             continue;
7050         if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
7051             continue;
7052         if(cData->topic)
7053             SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
7054         cData->last_refresh = refresh_num;
7055     }
7056     timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
7057 }
7058
7059 static CHANSERV_FUNC(cmd_unf)
7060 {
7061     if(channel)
7062     {
7063         char response[MAXLEN];
7064         const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
7065         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
7066         irc_privmsg(cmd->parent->bot, channel->name, response);
7067     }
7068     else
7069         reply("CSMSG_UNF_RESPONSE");
7070     return 1;
7071 }
7072
7073 static CHANSERV_FUNC(cmd_ping)
7074 {
7075     if(channel)
7076     {
7077         char response[MAXLEN];
7078         const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
7079         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
7080         irc_privmsg(cmd->parent->bot, channel->name, response);
7081     }
7082     else
7083         reply("CSMSG_PING_RESPONSE");
7084     return 1;
7085 }
7086
7087 static CHANSERV_FUNC(cmd_wut)
7088 {
7089     if(channel)
7090     {
7091         char response[MAXLEN];
7092         const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
7093         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
7094         irc_privmsg(cmd->parent->bot, channel->name, response);
7095     }
7096     else
7097         reply("CSMSG_WUT_RESPONSE");
7098     return 1;
7099 }
7100
7101 static CHANSERV_FUNC(cmd_8ball)
7102 {
7103     unsigned int i, j, accum;
7104     const char *resp;
7105
7106     REQUIRE_PARAMS(2);
7107     accum = 0;
7108     for(i=1; i<argc; i++)
7109         for(j=0; argv[i][j]; j++)
7110             accum = (accum << 5) - accum + toupper(argv[i][j]);
7111     resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
7112     if(channel)
7113     {
7114         char response[MAXLEN];
7115         sprintf(response, "\ 2%s\ 2: %s", user->nick, resp);
7116         irc_privmsg(cmd->parent->bot, channel->name, response);
7117     }
7118     else
7119         send_message_type(4, user, cmd->parent->bot, "%s", resp);
7120     return 1;
7121 }
7122
7123 static CHANSERV_FUNC(cmd_d)
7124 {
7125     unsigned long sides, count, modifier, ii, total;
7126     char response[MAXLEN], *sep;
7127     const char *fmt;
7128
7129     REQUIRE_PARAMS(2);
7130     if((count = strtoul(argv[1], &sep, 10)) < 1)
7131         goto no_dice;
7132     if(sep[0] == 0)
7133     {
7134         if(count == 1)
7135             goto no_dice;
7136         sides = count;
7137         count = 1;
7138         modifier = 0;
7139     }
7140     else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
7141             && (sides = strtoul(sep+1, &sep, 10)) > 1)
7142     {
7143         if(sep[0] == 0)
7144             modifier = 0;
7145         else if((sep[0] == '-') && isdigit(sep[1]))
7146             modifier = strtoul(sep, NULL, 10);
7147         else if((sep[0] == '+') && isdigit(sep[1]))
7148             modifier = strtoul(sep+1, NULL, 10);
7149         else
7150             goto no_dice;
7151     }
7152     else
7153     {
7154       no_dice:
7155         reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
7156         return 0;
7157     }
7158     if(count > 10)
7159     {
7160         reply("CSMSG_BAD_DICE_COUNT", count, 10);
7161         return 0;
7162     }
7163     for(total = ii = 0; ii < count; ++ii)
7164         total += (rand() % sides) + 1;
7165     total += modifier;
7166
7167     if((count > 1) || modifier)
7168     {
7169         fmt = user_find_message(user, "CSMSG_DICE_ROLL");
7170         sprintf(response, fmt, total, count, sides, modifier);
7171     }
7172     else
7173     {
7174         fmt = user_find_message(user, "CSMSG_DIE_ROLL");
7175         sprintf(response, fmt, total, sides);
7176     }
7177     if(channel)
7178         send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7179     else
7180         send_message_type(4, user, cmd->parent->bot, "%s", response);
7181     return 1;
7182 }
7183
7184 static CHANSERV_FUNC(cmd_huggle)
7185 {
7186     /* CTCP must be via PRIVMSG, never notice */
7187     if(channel)
7188         send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7189     else
7190         send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7191     return 1;
7192 }
7193
7194 static void
7195 chanserv_adjust_limit(void *data)
7196 {
7197     struct mod_chanmode change;
7198     struct chanData *cData = data;
7199     struct chanNode *channel = cData->channel;
7200     unsigned int limit;
7201
7202     if(IsSuspended(cData))
7203         return;
7204
7205     cData->limitAdjusted = now;
7206     limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7207     if(cData->modes.modes_set & MODE_LIMIT)
7208     {
7209         if(limit > cData->modes.new_limit)
7210             limit = cData->modes.new_limit;
7211         else if(limit == cData->modes.new_limit)
7212             return;
7213     }
7214
7215     mod_chanmode_init(&change);
7216     change.modes_set = MODE_LIMIT;
7217     change.new_limit = limit;
7218     mod_chanmode_announce(chanserv, channel, &change);
7219 }
7220
7221 static void
7222 handle_new_channel(struct chanNode *channel)
7223 {
7224     struct chanData *cData;
7225
7226     if(!(cData = channel->channel_info))
7227         return;
7228
7229     if(cData->modes.modes_set || cData->modes.modes_clear)
7230         mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7231
7232     if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7233         SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7234 }
7235
7236 void handle_new_channel_created(char *chan, struct userNode *user) {
7237     if(user->handle_info && chanserv_conf.new_channel_authed) {
7238         send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7239     } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7240         send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7241     }
7242     if(chanserv_conf.new_channel_msg)
7243         send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7244 }
7245
7246 /* Welcome to my worst nightmare. Warning: Read (or modify)
7247    the code below at your own risk. */
7248 static int
7249 handle_join(struct modeNode *mNode)
7250 {
7251     struct mod_chanmode change;
7252     struct userNode *user = mNode->user;
7253     struct chanNode *channel = mNode->channel;
7254     struct chanData *cData;
7255     struct userData *uData = NULL;
7256     struct banData *bData;
7257     struct handle_info *handle;
7258     unsigned int modes = 0, info = 0;
7259     char *greeting;
7260     unsigned int i = 0;
7261
7262     if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7263         return 0;
7264
7265     cData = channel->channel_info;
7266     if(channel->members.used > cData->max) {
7267         cData->max = channel->members.used;
7268         cData->max_time = now;
7269     }
7270
7271     for(i = 0; i < channel->invited.used; i++)
7272     {
7273         if(channel->invited.list[i] == user) {
7274             userList_remove(&channel->invited, user);
7275         }
7276     }
7277
7278     /* Check for bans.  If they're joining through a ban, one of two
7279      * cases applies:
7280      *   1: Join during a netburst, by riding the break.  Kick them
7281      *      unless they have ops or voice in the channel.
7282      *   2: They're allowed to join through the ban (an invite in
7283      *   ircu2.10, or a +e on Hybrid, or something).
7284      * If they're not joining through a ban, and the banlist is not
7285      * full, see if they're on the banlist for the channel.  If so,
7286      * kickban them.
7287      */
7288     if(user->uplink->burst && !mNode->modes)
7289     {
7290         unsigned int ii;
7291         for(ii = 0; ii < channel->banlist.used; ii++)
7292         {
7293             if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7294             {
7295                 /* Riding a netburst.  Naughty. */
7296                 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7297                 return 1;
7298             }
7299         }
7300     }
7301
7302     mod_chanmode_init(&change);
7303     change.argc = 1;
7304     if(channel->banlist.used < MAXBANS)
7305     {
7306         /* Not joining through a ban. */
7307         for(bData = cData->bans;
7308             bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7309             bData = bData->next);
7310
7311         if(bData)
7312         {
7313             char kick_reason[MAXLEN];
7314             sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7315
7316             bData->triggered = now;
7317             if(bData != cData->bans)
7318             {
7319                 /* Shuffle the ban to the head of the list. */
7320                 if(bData->next)
7321                     bData->next->prev = bData->prev;
7322                 if(bData->prev)
7323                     bData->prev->next = bData->next;
7324
7325                 bData->prev = NULL;
7326                 bData->next = cData->bans;
7327
7328                 if(cData->bans)
7329                     cData->bans->prev = bData;
7330                 cData->bans = bData;
7331             }
7332
7333             change.args[0].mode = MODE_BAN;
7334             change.args[0].u.hostmask = bData->mask;
7335             mod_chanmode_announce(chanserv, channel, &change);
7336             KickChannelUser(user, channel, chanserv, kick_reason);
7337             return 1;
7338         }
7339     }
7340
7341     /* ChanServ will not modify the limits in join-flooded channels,
7342        or when there are enough slots left below the limit. */
7343     if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7344        && !channel->join_flooded
7345        && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7346     {
7347         /* The user count has begun "bumping" into the channel limit,
7348            so set a timer to raise the limit a bit. Any previous
7349            timers are removed so three incoming users within the delay
7350            results in one limit change, not three. */
7351
7352         timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7353         timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7354     }
7355
7356     if(channel->join_flooded)
7357     {
7358         /* don't automatically give ops or voice during a join flood */
7359     }
7360     else if(cData->lvlOpts[lvlGiveOps] == 0)
7361         modes |= MODE_CHANOP;
7362     else if(cData->lvlOpts[lvlGiveVoice] == 0)
7363         modes |= MODE_VOICE;
7364
7365     greeting = cData->greeting;
7366     if(user->handle_info)
7367     {
7368         handle = user->handle_info;
7369
7370         if(IsHelper(user) && !IsHelping(user))
7371         {
7372             unsigned int ii;
7373             for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7374             {
7375                 if(channel == chanserv_conf.support_channels.list[ii])
7376                 {
7377                     HANDLE_SET_FLAG(user->handle_info, HELPING);
7378                     break;
7379                 }
7380             }
7381         }
7382
7383         uData = GetTrueChannelAccess(cData, handle);
7384         if(uData && !IsUserSuspended(uData))
7385         {
7386             /* Ops and above were handled by the above case. */
7387             if(IsUserAutoOp(uData))
7388             {
7389                 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7390                     modes |= MODE_CHANOP;
7391                 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7392                     modes |= MODE_VOICE;
7393             }
7394             if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7395                 cData->visited = now;
7396             if(cData->user_greeting)
7397                 greeting = cData->user_greeting;
7398             if(uData->info
7399                && (uData->access >= cData->lvlOpts[lvlUserInfo])
7400                && ((now - uData->seen) >= chanserv_conf.info_delay)
7401                && !uData->present)
7402                 info = 1;
7403             uData->seen = now;
7404             uData->present = 1;
7405         }
7406     }
7407
7408     /* If user joining normally (not during burst), apply op or voice,
7409      * and send greeting/userinfo as appropriate.
7410      */
7411     if(!user->uplink->burst)
7412     {
7413         if(modes)
7414         {
7415             if(modes & MODE_CHANOP)
7416                 modes &= ~MODE_VOICE;
7417             change.args[0].mode = modes;
7418             change.args[0].u.member = mNode;
7419             mod_chanmode_announce(chanserv, channel, &change);
7420         }
7421         if(greeting)
7422             send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7423         if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7424             send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7425     }
7426     return 0;
7427 }
7428
7429 static void
7430 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7431 {
7432     struct mod_chanmode change;
7433     struct userData *channel;
7434     unsigned int ii, jj;
7435
7436     if(!user->handle_info)
7437         return;
7438
7439     mod_chanmode_init(&change);
7440     change.argc = 1;
7441     for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7442     {
7443         struct chanNode *cn;
7444         struct modeNode *mn;
7445         if(IsUserSuspended(channel)
7446            || IsSuspended(channel->channel)
7447            || !(cn = channel->channel->channel))
7448             continue;
7449
7450         mn = GetUserMode(cn, user);
7451         if(!mn)
7452         {
7453             if(!IsUserSuspended(channel)
7454                && IsUserAutoInvite(channel)
7455                && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7456                && !self->burst
7457                && !user->uplink->burst)
7458                 irc_invite(chanserv, user, cn);
7459             continue;
7460         }
7461
7462         if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7463             channel->channel->visited = now;
7464
7465         if(IsUserAutoOp(channel))
7466         {
7467             if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7468                 change.args[0].mode = MODE_CHANOP;
7469             else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7470                 change.args[0].mode = MODE_VOICE;
7471             else
7472                 change.args[0].mode = 0;
7473             change.args[0].u.member = mn;
7474             if(change.args[0].mode)
7475                 mod_chanmode_announce(chanserv, cn, &change);
7476         }
7477
7478         channel->seen = now;
7479         channel->present = 1;
7480     }
7481
7482     for(ii = 0; ii < user->channels.used; ++ii)
7483     {
7484         struct chanNode *chan = user->channels.list[ii]->channel;
7485         struct banData *ban;
7486
7487         if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7488            || !chan->channel_info
7489            || IsSuspended(chan->channel_info))
7490             continue;
7491         for(jj = 0; jj < chan->banlist.used; ++jj)
7492             if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7493                 break;
7494         if(jj < chan->banlist.used)
7495             continue;
7496         for(ban = chan->channel_info->bans; ban; ban = ban->next)
7497         {
7498             char kick_reason[MAXLEN];
7499             if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7500                 continue;
7501             change.args[0].mode = MODE_BAN;
7502             change.args[0].u.hostmask = ban->mask;
7503             mod_chanmode_announce(chanserv, chan, &change);
7504             sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7505             KickChannelUser(user, chan, chanserv, kick_reason);
7506             ban->triggered = now;
7507             break;
7508         }
7509     }
7510
7511     if(IsSupportHelper(user))
7512     {
7513         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7514         {
7515             if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7516             {
7517                 HANDLE_SET_FLAG(user->handle_info, HELPING);
7518                 break;
7519             }
7520         }
7521     }
7522 }
7523
7524 static void
7525 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7526 {
7527     struct chanData *cData;
7528     struct userData *uData;
7529
7530     cData = mn->channel->channel_info;
7531     if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7532         return;
7533
7534     if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7535     {
7536         /* Allow for a bit of padding so that the limit doesn't
7537            track the user count exactly, which could get annoying. */
7538         if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7539         {
7540             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7541             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7542         }
7543     }
7544
7545     if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7546     {
7547         scan_user_presence(uData, mn->user);
7548         uData->seen = now;
7549         if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7550             cData->visited = now;
7551     }
7552
7553     if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7554     {
7555         unsigned int ii;
7556         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7557             struct chanNode *channel;
7558             struct userNode *exclude;
7559             /* When looking at the channel that is being /part'ed, we
7560              * have to skip over the client that is leaving.  For
7561              * other channels, we must not do that.
7562              */
7563             channel = chanserv_conf.support_channels.list[ii];
7564             exclude = (channel == mn->channel) ? mn->user : NULL;
7565             if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7566                 break;
7567         }
7568         if(ii == chanserv_conf.support_channels.used)
7569             HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7570     }
7571 }
7572
7573 static void
7574 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7575 {
7576     struct userData *uData;
7577
7578     if(!channel->channel_info || !kicker || IsService(kicker)
7579        || (kicker == victim) || IsSuspended(channel->channel_info)
7580        || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7581         return;
7582
7583     if(protect_user(victim, kicker, channel->channel_info))
7584     {
7585         const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7586         KickChannelUser(kicker, channel, chanserv, reason);
7587     }
7588
7589     if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7590         uData->seen = now;
7591 }
7592
7593 static int
7594 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7595 {
7596     struct chanData *cData;
7597
7598     if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7599         return 0;
7600
7601     cData = channel->channel_info;
7602     if(bad_topic(channel, user, channel->topic))
7603     {
7604         send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7605         if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7606             SetChannelTopic(channel, chanserv, old_topic, 1);
7607         else if(cData->topic)
7608             SetChannelTopic(channel, chanserv, cData->topic, 1);
7609         return 1;
7610     }
7611     /* With topicsnarf, grab the topic and save it as the default topic. */
7612     if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7613     {
7614         free(cData->topic);
7615         cData->topic = strdup(channel->topic);
7616     }
7617     return 0;
7618 }
7619
7620 static void
7621 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7622 {
7623     struct mod_chanmode *bounce = NULL;
7624     unsigned int bnc, ii;
7625     char deopped = 0;
7626
7627     if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7628         return;
7629
7630     if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7631        && mode_lock_violated(&channel->channel_info->modes, change))
7632     {
7633         char correct[MAXLEN];
7634         bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7635         mod_chanmode_format(&channel->channel_info->modes, correct);
7636         send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7637     }
7638     for(ii = bnc = 0; ii < change->argc; ++ii)
7639     {
7640         if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7641         {
7642             const struct userNode *victim = change->args[ii].u.member->user;
7643             if(!protect_user(victim, user, channel->channel_info))
7644                 continue;
7645             if(!bounce)
7646                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7647             if(!deopped)
7648             {
7649                 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7650                 bounce->args[bnc].u.member = GetUserMode(channel, user);
7651                 if(bounce->args[bnc].u.member)
7652                     bnc++;
7653                 deopped = 1;
7654             }
7655             bounce->args[bnc].mode = MODE_CHANOP;
7656             bounce->args[bnc].u.member = change->args[ii].u.member;
7657             bnc++;
7658             send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7659         }
7660         else if(change->args[ii].mode & MODE_CHANOP)
7661         {
7662             const struct userNode *victim = change->args[ii].u.member->user;
7663             if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7664                 continue;
7665             if(!bounce)
7666                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7667             bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7668             bounce->args[bnc].u.member = change->args[ii].u.member;
7669             bnc++;
7670         }
7671         else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7672         {
7673             const char *ban = change->args[ii].u.hostmask;
7674             if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7675                 continue;
7676             if(!bounce)
7677                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7678             bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7679             bounce->args[bnc].u.hostmask = strdup(ban);
7680             bnc++;
7681             send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7682         }
7683     }
7684     if(bounce)
7685     {
7686         if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7687             mod_chanmode_announce(chanserv, channel, bounce);
7688         for(ii = 0; ii < change->argc; ++ii)
7689             if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7690                 free((char*)bounce->args[ii].u.hostmask);
7691         mod_chanmode_free(bounce);
7692     }
7693 }
7694
7695 static void
7696 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7697 {
7698     struct chanNode *channel;
7699     struct banData *bData;
7700     struct mod_chanmode change;
7701     unsigned int ii, jj;
7702     char kick_reason[MAXLEN];
7703
7704     mod_chanmode_init(&change);
7705     change.argc = 1;
7706     change.args[0].mode = MODE_BAN;
7707     for(ii = 0; ii < user->channels.used; ++ii)
7708     {
7709         channel = user->channels.list[ii]->channel;
7710         /* Need not check for bans if they're opped or voiced. */
7711         if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7712             continue;
7713         /* Need not check for bans unless channel registration is active. */
7714         if(!channel->channel_info || IsSuspended(channel->channel_info))
7715             continue;
7716         /* Look for a matching ban already on the channel. */
7717         for(jj = 0; jj < channel->banlist.used; ++jj)
7718             if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7719                 break;
7720         /* Need not act if we found one. */
7721         if(jj < channel->banlist.used)
7722             continue;
7723         /* Look for a matching ban in this channel. */
7724         for(bData = channel->channel_info->bans; bData; bData = bData->next)
7725         {
7726             if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7727                 continue;
7728             change.args[0].u.hostmask = bData->mask;
7729             mod_chanmode_announce(chanserv, channel, &change);
7730             sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7731             KickChannelUser(user, channel, chanserv, kick_reason);
7732             bData->triggered = now;
7733             break; /* we don't need to check any more bans in the channel */
7734         }
7735     }
7736 }
7737
7738 static void handle_rename(struct handle_info *handle, const char *old_handle)
7739 {
7740     struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7741
7742     if(dnr)
7743     {
7744         dict_remove2(handle_dnrs, old_handle, 1);
7745         safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7746         dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7747     }
7748 }
7749
7750 static void
7751 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7752 {
7753     struct userNode *h_user;
7754
7755     if(handle->channels)
7756     {
7757         for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7758             send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7759
7760         while(handle->channels)
7761             del_channel_user(handle->channels, 1);
7762     }
7763 }
7764
7765 static void
7766 handle_server_link(UNUSED_ARG(struct server *server))
7767 {
7768     struct chanData *cData;
7769
7770     for(cData = channelList; cData; cData = cData->next)
7771     {
7772         if(!IsSuspended(cData))
7773             cData->may_opchan = 1;
7774         if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7775            && !cData->channel->join_flooded
7776            && ((cData->channel->limit - cData->channel->members.used)
7777                < chanserv_conf.adjust_threshold))
7778         {
7779             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7780             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7781         }
7782     }
7783 }
7784
7785 static void
7786 chanserv_conf_read(void)
7787 {
7788     dict_t conf_node;
7789     const char *str;
7790     char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7791     struct mod_chanmode *change;
7792     struct string_list *strlist;
7793     struct chanNode *chan;
7794     unsigned int ii;
7795
7796     if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7797     {
7798         log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7799         return;
7800     }
7801     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7802         UnlockChannel(chanserv_conf.support_channels.list[ii]);
7803     chanserv_conf.support_channels.used = 0;
7804     if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7805     {
7806         for(ii = 0; ii < strlist->used; ++ii)
7807         {
7808             const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7809             if(!str2)
7810                 str2 = "+nt";
7811             chan = AddChannel(strlist->list[ii], now, str2, NULL);
7812             LockChannel(chan);
7813             channelList_append(&chanserv_conf.support_channels, chan);
7814         }
7815     }
7816     else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7817     {
7818         const char *str2;
7819         str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7820         if(!str2)
7821             str2 = "+nt";
7822         chan = AddChannel(str, now, str2, NULL);
7823         LockChannel(chan);
7824         channelList_append(&chanserv_conf.support_channels, chan);
7825     }
7826     str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7827     chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7828     str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7829     chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7830     str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7831     chanserv_conf.greeting_length = str ? atoi(str) : 200;
7832     str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7833     chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7834     str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7835     chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7836     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7837     chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7838     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7839     chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7840     str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7841     chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7842     str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7843     chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7844     str = database_get_data(conf_node, KEY_REVOKE_MODE_A, RECDB_QSTRING);
7845     chanserv_conf.revoke_mode_a = str ? atoi(str) : 1;
7846     str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7847     chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7848     str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7849     chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7850     str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7851     chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7852     str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7853     chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7854     str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7855     if(chanserv && str)
7856         NickChange(chanserv, str, 0);
7857     str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7858     chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7859     str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7860     chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7861     str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7862     chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7863     str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7864     chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7865     str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7866     chanserv_conf.max_owned = str ? atoi(str) : 5;
7867     str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7868     chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7869     str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7870     chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7871     str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7872     chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7873     str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7874     chanserv_conf.new_channel_authed = (str && *str) ? str : NULL;
7875     str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7876     chanserv_conf.new_channel_unauthed = (str && *str) ? str : NULL;
7877     str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7878     chanserv_conf.new_channel_msg = (str && *str) ? str : NULL;
7879     str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7880     if(!str)
7881         str = "+nt";
7882     safestrncpy(mode_line, str, sizeof(mode_line));
7883     ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7884     if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7885        && (change->argc < 2))
7886     {
7887         chanserv_conf.default_modes = *change;
7888         mod_chanmode_free(change);
7889     }
7890     free_string_list(chanserv_conf.set_shows);
7891     strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7892     if(strlist)
7893         strlist = string_list_copy(strlist);
7894     else
7895     {
7896         static const char *list[] = {
7897             /* free form text */
7898             "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7899             /* options based on user level */
7900             "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7901             "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7902             /* multiple choice options */
7903             "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7904             /* binary options */
7905             "DynLimit", "NoDelete", "expire", "Vote",
7906             /* delimiter */
7907             NULL
7908         };
7909         strlist = alloc_string_list(ArrayLength(list)-1);
7910         for(ii=0; list[ii]; ii++)
7911             string_list_append(strlist, strdup(list[ii]));
7912     }
7913     chanserv_conf.set_shows = strlist;
7914     /* We don't look things up now, in case the list refers to options
7915      * defined by modules initialized after this point.  Just mark the
7916      * function list as invalid, so it will be initialized.
7917      */
7918     set_shows_list.used = 0;
7919     free_string_list(chanserv_conf.eightball);
7920     strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7921     if(strlist)
7922     {
7923         strlist = string_list_copy(strlist);
7924     }
7925     else
7926     {
7927         strlist = alloc_string_list(4);
7928         string_list_append(strlist, strdup("Yes."));
7929         string_list_append(strlist, strdup("No."));
7930         string_list_append(strlist, strdup("Maybe so."));
7931     }
7932     chanserv_conf.eightball = strlist;
7933     free_string_list(chanserv_conf.old_ban_names);
7934     strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7935     if(strlist)
7936         strlist = string_list_copy(strlist);
7937     else
7938         strlist = alloc_string_list(2);
7939     chanserv_conf.old_ban_names = strlist;
7940     str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7941     off_channel = str ? atoi(str) : 0;
7942 }
7943
7944 static void
7945 chanserv_note_type_read(const char *key, struct record_data *rd)
7946 {
7947     dict_t obj;
7948     struct note_type *ntype;
7949     const char *str;
7950
7951     if(!(obj = GET_RECORD_OBJECT(rd)))
7952     {
7953         log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7954         return;
7955     }
7956     if(!(ntype = chanserv_create_note_type(key)))
7957     {
7958         log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7959         return;
7960     }
7961
7962     /* Figure out set access */
7963     if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7964     {
7965         ntype->set_access_type = NOTE_SET_PRIVILEGED;
7966         ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7967     }
7968     else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7969     {
7970         ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7971         ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7972     }
7973     else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7974     {
7975         ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7976     }
7977     else
7978     {
7979         log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7980         ntype->set_access_type = NOTE_SET_PRIVILEGED;
7981         ntype->set_access.min_opserv = 0;
7982     }
7983
7984     /* Figure out visibility */
7985     if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7986         ntype->visible_type = NOTE_VIS_PRIVILEGED;
7987     else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7988         ntype->visible_type = NOTE_VIS_PRIVILEGED;
7989     else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7990         ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7991     else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7992         ntype->visible_type = NOTE_VIS_ALL;
7993     else
7994         ntype->visible_type = NOTE_VIS_PRIVILEGED;
7995
7996     str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7997     ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7998 }
7999
8000 static void
8001 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8002 {
8003     struct vote_option *vOpt;
8004     char *str;
8005     
8006     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8007     {
8008         log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
8009         return;
8010     }
8011     
8012     vOpt = calloc(1, sizeof(*vOpt));
8013     vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
8014     str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
8015     vOpt->voted = str ? atoi(str) : 0;
8016     vOpt->option_id = str ? atoi(key) : 0;
8017     vOpt->option_str = strdup(key);
8018     dict_insert(chan->vote_options,vOpt->option_str,vOpt);
8019 }
8020
8021 static void
8022 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8023 {
8024     struct handle_info *handle;
8025     struct userData *uData;
8026     char *seen, *inf, *flags, *voted, *votefor;
8027     unsigned long last_seen;
8028     unsigned short access_level;
8029
8030     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8031     {
8032         log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
8033         return;
8034     }
8035
8036     access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
8037     if(access_level > UL_OWNER)
8038     {
8039         log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
8040         return;
8041     }
8042
8043     inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
8044     seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
8045     last_seen = seen ? strtoul(seen, NULL, 0) : now;
8046     flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
8047     voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
8048     votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
8049     handle = get_handle_info(key);
8050     if(!handle)
8051     {
8052         log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
8053         return;
8054     }
8055
8056     uData = add_channel_user(chan, handle, access_level, last_seen, inf);
8057     uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
8058     if(chan->vote) {
8059         uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
8060         uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
8061     } else {
8062         uData->voted = 0;
8063         uData->votefor = 0;
8064     }
8065 }
8066
8067 static void
8068 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8069 {
8070     char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
8071     unsigned long set_time, triggered_time, expires_time;
8072
8073     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8074     {
8075         log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
8076         return;
8077     }
8078
8079     set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
8080     triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
8081     s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
8082     s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
8083     owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
8084     reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
8085     if (!reason || !owner)
8086         return;
8087
8088     set_time = set ? strtoul(set, NULL, 0) : now;
8089     triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
8090     if(s_expires)
8091         expires_time = strtoul(s_expires, NULL, 0);
8092     else if(s_duration)
8093         expires_time = set_time + atoi(s_duration);
8094     else
8095         expires_time = 0;
8096
8097     if(!reason || (expires_time && (expires_time < now)))
8098         return;
8099
8100     add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
8101 }
8102
8103 static struct suspended *
8104 chanserv_read_suspended(dict_t obj)
8105 {
8106     struct suspended *suspended = calloc(1, sizeof(*suspended));
8107     char *str;
8108     dict_t previous;
8109
8110     str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
8111     suspended->expires = str ? strtoul(str, NULL, 0) : 0;
8112     str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
8113     suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
8114     str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8115     suspended->issued = str ? strtoul(str, NULL, 0) : 0;
8116     suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
8117     suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
8118     previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8119     suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
8120     return suspended;
8121 }
8122
8123 static struct giveownership *
8124 chanserv_read_giveownership(dict_t obj)
8125 {
8126     struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
8127     char *str;
8128     dict_t previous;
8129
8130     str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
8131     giveownership->staff_issuer = str ? strdup(str) : NULL;
8132
8133     giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
8134
8135     giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
8136     giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
8137
8138     str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
8139     giveownership->reason = str ? strdup(str) : NULL;
8140     str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8141     giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
8142
8143     previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8144     giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
8145     return giveownership;
8146 }
8147
8148 static int
8149 chanserv_channel_read(const char *key, struct record_data *hir)
8150 {
8151     struct suspended *suspended;
8152     struct giveownership *giveownership;
8153     struct mod_chanmode *modes;
8154     struct chanNode *cNode;
8155     struct chanData *cData;
8156     struct dict *channel, *obj;
8157     char *str, *argv[10];
8158     dict_iterator_t it;
8159     unsigned int argc;
8160
8161     channel = hir->d.object;
8162
8163     str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
8164     if(!str)
8165         str = "<unknown>";
8166     cNode = AddChannel(key, now, NULL, NULL);
8167     if(!cNode)
8168     {
8169         log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
8170         return 0;
8171     }
8172     cData = register_channel(cNode, str);
8173     if(!cData)
8174     {
8175         log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
8176         return 0;
8177     }
8178
8179     if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
8180     {
8181         enum levelOption lvlOpt;
8182         enum charOption chOpt;
8183
8184         if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
8185             cData->flags = atoi(str);
8186
8187         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8188         {
8189             str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
8190             if(str)
8191                 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
8192             else if(levelOptions[lvlOpt].old_flag)
8193             {
8194                 if(cData->flags & levelOptions[lvlOpt].old_flag)
8195                     cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
8196                 else
8197                     cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
8198             }
8199         }
8200
8201         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8202         {
8203             if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8204                 continue;
8205             cData->chOpts[chOpt] = str[0];
8206         }
8207     }
8208     else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8209     {
8210         enum levelOption lvlOpt;
8211         enum charOption chOpt;
8212         unsigned int count;
8213
8214         cData->flags = base64toint(str, 5);
8215         count = strlen(str += 5);
8216         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8217         {
8218             unsigned short lvl;
8219             if(levelOptions[lvlOpt].old_flag)
8220             {
8221                 if(cData->flags & levelOptions[lvlOpt].old_flag)
8222                     lvl = levelOptions[lvlOpt].flag_value;
8223                 else
8224                     lvl = levelOptions[lvlOpt].default_value;
8225             }
8226             else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8227             {
8228             case 'c': lvl = UL_COOWNER; break;
8229             case 'm': lvl = UL_MASTER; break;
8230             case 'n': lvl = UL_OWNER+1; break;
8231             case 'o': lvl = UL_OP; break;
8232             case 'p': lvl = UL_PEON; break;
8233             case 'w': lvl = UL_OWNER; break;
8234             default: lvl = 0; break;
8235             }
8236             cData->lvlOpts[lvlOpt] = lvl;
8237         }
8238         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8239             cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8240     }
8241
8242     if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8243     {
8244         cData->expiry = atoi(str);
8245         if(cData->expiry > 0) {
8246             if(cData->expiry > now) {
8247                 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8248             } else {
8249                 timeq_add(1, chanserv_expire_channel, cData);
8250             }
8251         }
8252     } else {
8253         cData->expiry = 0;
8254     }
8255
8256     if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8257     {
8258         suspended = chanserv_read_suspended(obj);
8259         cData->suspended = suspended;
8260         suspended->cData = cData;
8261         /* We could use suspended->expires and suspended->revoked to
8262          * set the CHANNEL_SUSPENDED flag, but we don't. */
8263     }
8264     else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8265     {
8266         suspended = calloc(1, sizeof(*suspended));
8267         suspended->issued = 0;
8268         suspended->revoked = 0;
8269         suspended->suspender = strdup(str);
8270         str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8271         suspended->expires = str ? atoi(str) : 0;
8272         str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8273         suspended->reason = strdup(str ? str : "No reason");
8274         suspended->previous = NULL;
8275         cData->suspended = suspended;
8276         suspended->cData = cData;
8277     }
8278     else
8279     {
8280         cData->flags &= ~CHANNEL_SUSPENDED;
8281         suspended = NULL; /* to squelch a warning */
8282     }
8283
8284     if(IsSuspended(cData)) {
8285         if(suspended->expires > now)
8286             timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8287         else if(suspended->expires)
8288             cData->flags &= ~CHANNEL_SUSPENDED;
8289     }
8290
8291     if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
8292     {
8293         giveownership = chanserv_read_giveownership(obj);
8294         cData->giveownership = giveownership;
8295     }
8296
8297     if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8298         struct mod_chanmode change;
8299         mod_chanmode_init(&change);
8300         change.argc = 1;
8301         change.args[0].mode = MODE_CHANOP;
8302         change.args[0].u.member = AddChannelUser(chanserv, cNode);
8303         mod_chanmode_announce(chanserv, cNode, &change);
8304     }
8305
8306     str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8307     cData->registered = str ? strtoul(str, NULL, 0) : now;
8308     str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8309     cData->visited = str ? strtoul(str, NULL, 0) : now;
8310     str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8311     cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8312     str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8313     cData->max = str ? atoi(str) : 0;
8314     str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8315     cData->max_time = str ? atoi(str) : 0;
8316     str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8317     cData->greeting = str ? strdup(str) : NULL;
8318     str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8319     cData->user_greeting = str ? strdup(str) : NULL;
8320     str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8321     cData->topic_mask = str ? strdup(str) : NULL;
8322     str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8323     cData->topic = str ? strdup(str) : NULL;
8324
8325     str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8326     if(str) {
8327         cData->vote = str ? strdup(str) : NULL;
8328         dict_delete(cData->vote_options);
8329         cData->vote_options = dict_new();
8330         dict_set_free_data(cData->vote_options, free_vote_options);
8331         str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8332         cData->vote_start = str ? atoi(str) : 0;
8333         obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8334         for(it = dict_first(obj); it; it = iter_next(it)) {
8335             vote_option_read_helper(iter_key(it), iter_data(it), cData);
8336         }
8337     }
8338
8339     obj = database_get_data(channel, KEY_ADVTOPIC_ENTRIES, RECDB_OBJECT);
8340     for(it = dict_first(obj); it; it = iter_next(it))
8341     {
8342         struct record_data *rd = iter_data(it);
8343         if(rd->type != RECDB_QSTRING) continue;
8344         int advtopic_index = atoi(iter_key(it));
8345         if(advtopic_index < 0 || advtopic_index >= MAXADVTOPICENTRIES) continue;
8346         cData->advtopic[advtopic_index] = (rd ? strdup(rd->d.qstring) : NULL);
8347     }
8348
8349     if(!IsSuspended(cData)
8350        && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8351        && (argc = split_line(str, 0, ArrayLength(argv), argv))
8352        && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8353         cData->modes = *modes;
8354         if(off_channel > 0)
8355           cData->modes.modes_set |= MODE_REGISTERED;
8356         if(cData->modes.argc > 1)
8357             cData->modes.argc = 1;
8358         mod_chanmode_announce(chanserv, cNode, &cData->modes);
8359         mod_chanmode_free(modes);
8360     }
8361
8362     obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8363     for(it = dict_first(obj); it; it = iter_next(it))
8364         user_read_helper(iter_key(it), iter_data(it), cData);
8365
8366     if(!cData->users && !IsProtected(cData))
8367     {
8368         log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8369         unregister_channel(cData, "has empty user list.");
8370         return 0;
8371     }
8372
8373     obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8374     for(it = dict_first(obj); it; it = iter_next(it))
8375         ban_read_helper(iter_key(it), iter_data(it), cData);
8376
8377     obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8378     for(it = dict_first(obj); it; it = iter_next(it))
8379     {
8380         struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8381         struct record_data *rd = iter_data(it);
8382         const char *note, *setter;
8383
8384         if(rd->type != RECDB_OBJECT)
8385         {
8386             log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8387         }
8388         else if(!ntype)
8389         {
8390             log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8391         }
8392         else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8393         {
8394             log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8395         }
8396         else
8397         {
8398             setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8399             if(!setter) setter = "<unknown>";
8400             chanserv_add_channel_note(cData, ntype, setter, note);
8401         }
8402     }
8403
8404     return 0;
8405 }
8406
8407 static void
8408 chanserv_dnr_read(const char *key, struct record_data *hir)
8409 {
8410     const char *setter, *reason, *str;
8411     struct do_not_register *dnr;
8412     unsigned long expiry;
8413
8414     setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8415     if(!setter)
8416     {
8417         log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8418         return;
8419     }
8420     reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8421     if(!reason)
8422     {
8423         log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8424         return;
8425     }
8426     str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8427     expiry = str ? strtoul(str, NULL, 0) : 0;
8428     if(expiry && expiry <= now)
8429         return;
8430     dnr = chanserv_add_dnr(key, setter, expiry, reason);
8431     if(!dnr)
8432         return;
8433     str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8434     if(str)
8435         dnr->set = atoi(str);
8436     else
8437         dnr->set = 0;
8438 }
8439
8440 static int
8441 chanserv_saxdb_read(struct dict *database)
8442 {
8443     struct dict *section;
8444     dict_iterator_t it;
8445
8446     if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8447         for(it = dict_first(section); it; it = iter_next(it))
8448             chanserv_note_type_read(iter_key(it), iter_data(it));
8449
8450     if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8451         for(it = dict_first(section); it; it = iter_next(it))
8452             chanserv_channel_read(iter_key(it), iter_data(it));
8453
8454     if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8455         for(it = dict_first(section); it; it = iter_next(it))
8456             chanserv_dnr_read(iter_key(it), iter_data(it));
8457
8458     return 0;
8459 }
8460
8461 static int
8462 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8463 {
8464     int high_present = 0;
8465     saxdb_start_record(ctx, KEY_USERS, 1);
8466     for(; uData; uData = uData->next)
8467     {
8468         if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8469             high_present = 1;
8470         saxdb_start_record(ctx, uData->handle->handle, 0);
8471         saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8472         saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8473         if(uData->flags)
8474             saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8475         if(uData->channel->vote && uData->voted)
8476             saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8477         if(uData->channel->vote && uData->votefor)
8478             saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8479         if(uData->info)
8480             saxdb_write_string(ctx, KEY_INFO, uData->info);
8481         saxdb_end_record(ctx);
8482     }
8483     saxdb_end_record(ctx);
8484     return high_present;
8485 }
8486
8487 static void
8488 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8489 {
8490     if(!bData)
8491         return;
8492     saxdb_start_record(ctx, KEY_BANS, 1);
8493     for(; bData; bData = bData->next)
8494     {
8495         saxdb_start_record(ctx, bData->mask, 0);
8496         saxdb_write_int(ctx, KEY_SET, bData->set);
8497         if(bData->triggered)
8498             saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8499         if(bData->expires)
8500             saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8501         if(bData->owner[0])
8502             saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8503         if(bData->reason)
8504             saxdb_write_string(ctx, KEY_REASON, bData->reason);
8505         saxdb_end_record(ctx);
8506     }
8507     saxdb_end_record(ctx);
8508 }
8509
8510 static void
8511 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8512 {
8513     saxdb_start_record(ctx, name, 0);
8514     saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8515     saxdb_write_string(ctx, KEY_REASON, susp->reason);
8516     if(susp->issued)
8517         saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8518     if(susp->expires)
8519         saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8520     if(susp->revoked)
8521         saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8522     if(susp->previous)
8523         chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8524     saxdb_end_record(ctx);
8525 }
8526
8527 static void
8528 chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
8529 {
8530     saxdb_start_record(ctx, name, 0);
8531     if(giveownership->staff_issuer)
8532       saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
8533     if(giveownership->old_owner)
8534       saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
8535     if(giveownership->target)
8536       saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
8537     if(giveownership->target_access)
8538       saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
8539     if(giveownership->reason)
8540       saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
8541     if(giveownership->issued)
8542         saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
8543     if(giveownership->previous)
8544         chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
8545     saxdb_end_record(ctx);
8546 }
8547
8548 static void
8549 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8550 {
8551     char buf[MAXLEN];
8552     int high_present;
8553     enum levelOption lvlOpt;
8554     enum charOption chOpt;
8555     dict_iterator_t it;
8556
8557     saxdb_start_record(ctx, channel->channel->name, 1);
8558
8559     saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8560     saxdb_write_int(ctx, KEY_MAX, channel->max);
8561     saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8562     if(channel->topic)
8563         saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8564     if(channel->registrar)
8565         saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8566     if(channel->greeting)
8567         saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8568     if(channel->user_greeting)
8569         saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8570     if(channel->topic_mask)
8571         saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8572     if(channel->suspended)
8573         chanserv_write_suspended(ctx, "suspended", channel->suspended);
8574     if(channel->giveownership)
8575         chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
8576     if(channel->expiry)
8577         saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8578
8579     if(channel->vote) {
8580         saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8581         if(channel->vote_start)
8582             saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8583         if (dict_size(channel->vote_options)) {
8584             saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8585             for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8586                 struct vote_option *vOpt = iter_data(it);
8587                 char str[50];
8588                 sprintf(str,"%i",vOpt->option_id);
8589                 saxdb_start_record(ctx, str, 0);
8590                 if(vOpt->voted)
8591                     saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8592                 if(vOpt->name)
8593                     saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8594                 saxdb_end_record(ctx);
8595             }
8596             saxdb_end_record(ctx);
8597         }
8598     }
8599
8600     saxdb_start_record(ctx, KEY_OPTIONS, 0);
8601     saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8602     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8603         saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8604     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8605     {
8606         buf[0] = channel->chOpts[chOpt];
8607         buf[1] = '\0';
8608         saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8609     }
8610     saxdb_end_record(ctx);
8611
8612     if(channel->modes.modes_set || channel->modes.modes_clear)
8613     {
8614         mod_chanmode_format(&channel->modes, buf);
8615         saxdb_write_string(ctx, KEY_MODES, buf);
8616     }
8617
8618     high_present = chanserv_write_users(ctx, channel->users);
8619     chanserv_write_bans(ctx, channel->bans);
8620
8621     if(channel->flags & CHANNEL_ADVTOPIC) {
8622         saxdb_start_record(ctx, KEY_ADVTOPIC_ENTRIES, 0);
8623         int advtopic_index;
8624         for(advtopic_index = 0; advtopic_index < MAXADVTOPICENTRIES; advtopic_index++) {
8625             if(channel->advtopic[advtopic_index])
8626                 saxdb_write_string(ctx, strtab(advtopic_index), channel->advtopic[advtopic_index]);
8627         }
8628         saxdb_end_record(ctx);
8629     }
8630
8631     if(dict_size(channel->notes))
8632     {
8633         dict_iterator_t it;
8634
8635         saxdb_start_record(ctx, KEY_NOTES, 1);
8636         for(it = dict_first(channel->notes); it; it = iter_next(it))
8637         {
8638             struct note *note = iter_data(it);
8639             saxdb_start_record(ctx, iter_key(it), 0);
8640             saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8641             saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8642             saxdb_end_record(ctx);
8643         }
8644         saxdb_end_record(ctx);
8645     }
8646
8647     if(channel->ownerTransfer)
8648         saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8649     saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8650     saxdb_end_record(ctx);
8651 }
8652
8653 static void
8654 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8655 {
8656     const char *str;
8657
8658     saxdb_start_record(ctx, ntype->name, 0);
8659     switch(ntype->set_access_type)
8660     {
8661     case NOTE_SET_CHANNEL_ACCESS:
8662         saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8663         break;
8664     case NOTE_SET_CHANNEL_SETTER:
8665         saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8666         break;
8667     case NOTE_SET_PRIVILEGED: default:
8668         saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8669         break;
8670     }
8671     switch(ntype->visible_type)
8672     {
8673     case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8674     case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8675     case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8676     }
8677     saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8678     saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8679     saxdb_end_record(ctx);
8680 }
8681
8682 static void
8683 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8684 {
8685     struct do_not_register *dnr;
8686     dict_iterator_t it, next;
8687
8688     for(it = dict_first(dnrs); it; it = next)
8689     {
8690         next = iter_next(it);
8691         dnr = iter_data(it);
8692         if(dnr->expires && dnr->expires <= now)
8693         {
8694             dict_remove(dnrs, iter_key(it));
8695             continue;
8696         }
8697         saxdb_start_record(ctx, dnr->chan_name, 0);
8698         if(dnr->set)
8699             saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8700         if(dnr->expires)
8701             saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8702         saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8703         saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8704         saxdb_end_record(ctx);
8705     }
8706 }
8707
8708 static int
8709 chanserv_saxdb_write(struct saxdb_context *ctx)
8710 {
8711     dict_iterator_t it;
8712     struct chanData *channel;
8713
8714     /* Notes */
8715     saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8716     for(it = dict_first(note_types); it; it = iter_next(it))
8717         chanserv_write_note_type(ctx, iter_data(it));
8718     saxdb_end_record(ctx);
8719
8720     /* DNRs */
8721     saxdb_start_record(ctx, KEY_DNR, 1);
8722     write_dnrs_helper(ctx, handle_dnrs);
8723     write_dnrs_helper(ctx, plain_dnrs);
8724     write_dnrs_helper(ctx, mask_dnrs);
8725     saxdb_end_record(ctx);
8726
8727     /* Channels */
8728     saxdb_start_record(ctx, KEY_CHANNELS, 1);
8729     for(channel = channelList; channel; channel = channel->next)
8730         chanserv_write_channel(ctx, channel);
8731     saxdb_end_record(ctx);
8732
8733     return 0;
8734 }
8735
8736 static void
8737 chanserv_db_cleanup(void) {
8738     unsigned int ii;
8739     unreg_part_func(handle_part);
8740     while(channelList)
8741         unregister_channel(channelList, "terminating.");
8742     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8743         UnlockChannel(chanserv_conf.support_channels.list[ii]);
8744     free(chanserv_conf.support_channels.list);
8745     dict_delete(handle_dnrs);
8746     dict_delete(plain_dnrs);
8747     dict_delete(mask_dnrs);
8748     dict_delete(note_types);
8749     free_string_list(chanserv_conf.eightball);
8750     free_string_list(chanserv_conf.old_ban_names);
8751     free_string_list(chanserv_conf.set_shows);
8752     free(set_shows_list.list);
8753     free(uset_shows_list.list);
8754     while(helperList)
8755     {
8756         struct userData *helper = helperList;
8757         helperList = helperList->next;
8758         free(helper);
8759     }
8760 }
8761
8762 #if defined(GCC_VARMACROS)
8763 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8764 #elif defined(C99_VARMACROS)
8765 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8766 #endif
8767 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8768 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8769
8770 void
8771 init_chanserv(const char *nick)
8772 {
8773     CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8774     conf_register_reload(chanserv_conf_read);
8775
8776     if(nick)
8777     {
8778         reg_server_link_func(handle_server_link);
8779         reg_new_channel_func(handle_new_channel);
8780         reg_join_func(handle_join);
8781         reg_part_func(handle_part);
8782         reg_kick_func(handle_kick);
8783         reg_topic_func(handle_topic);
8784         reg_mode_change_func(handle_mode);
8785         reg_nick_change_func(handle_nick_change);
8786         reg_auth_func(handle_auth);
8787     }
8788
8789     reg_handle_rename_func(handle_rename);
8790     reg_unreg_func(handle_unreg);
8791
8792     handle_dnrs = dict_new();
8793     dict_set_free_data(handle_dnrs, free);
8794     plain_dnrs = dict_new();
8795     dict_set_free_data(plain_dnrs, free);
8796     mask_dnrs = dict_new();
8797     dict_set_free_data(mask_dnrs, free);
8798
8799     reg_svccmd_unbind_func(handle_svccmd_unbind);
8800     chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8801     DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8802     DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8803     DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8804     DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8805     modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8806     modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8807     modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8808     DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8809     DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8810     DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8811     DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8812     DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8813
8814     DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8815     DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8816
8817     DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8818     DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8819     DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8820     DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8821     DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8822
8823     DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8824     DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8825     DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8826     DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8827     DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8828
8829     DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8830     DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8831     DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8832     DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8833
8834     DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8835     DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8836     DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8837     DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8838     DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8839     DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8840     DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8841     DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8842
8843     DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8844     DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8845     DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8846     DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8847     DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8848     DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8849     DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8850     DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8851     DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8852     DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8853     DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8854     DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8855     DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8856     DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8857     DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8858
8859     DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8860     DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8861     DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8862     DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8863     DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8864
8865     DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8866     DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8867
8868     DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8869     DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8870     DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8871     DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8872     DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8873     DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8874     DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8875     DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8876     DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8877     DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8878     DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8879
8880     DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8881     DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8882
8883     DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8884     DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8885     DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8886     DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8887
8888     DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8889     DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8890     DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8891     DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8892     DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8893
8894     DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8895     DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8896     DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8897     DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8898     DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8899     DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8900     
8901     DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8902     DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8903     DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8904     DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8905     DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8906     DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8907     DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8908     DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8909
8910     DEFINE_COMMAND(opme, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
8911     
8912     /* Channel options */
8913     DEFINE_CHANNEL_OPTION(defaulttopic);
8914     DEFINE_CHANNEL_OPTION(topicmask);
8915     DEFINE_CHANNEL_OPTION(greeting);
8916     DEFINE_CHANNEL_OPTION(usergreeting);
8917     DEFINE_CHANNEL_OPTION(modes);
8918     DEFINE_CHANNEL_OPTION(enfops);
8919     DEFINE_CHANNEL_OPTION(giveops);
8920     DEFINE_CHANNEL_OPTION(protect);
8921     DEFINE_CHANNEL_OPTION(enfmodes);
8922     DEFINE_CHANNEL_OPTION(enftopic);
8923     DEFINE_CHANNEL_OPTION(pubcmd);
8924     DEFINE_CHANNEL_OPTION(givevoice);
8925     DEFINE_CHANNEL_OPTION(userinfo);
8926     DEFINE_CHANNEL_OPTION(dynlimit);
8927     DEFINE_CHANNEL_OPTION(topicsnarf);
8928     DEFINE_CHANNEL_OPTION(vote);
8929     DEFINE_CHANNEL_OPTION(nodelete);
8930     DEFINE_CHANNEL_OPTION(toys);
8931     DEFINE_CHANNEL_OPTION(setters);
8932     DEFINE_CHANNEL_OPTION(topicrefresh);
8933     DEFINE_CHANNEL_OPTION(ctcpusers);
8934     DEFINE_CHANNEL_OPTION(ctcpreaction);
8935     DEFINE_CHANNEL_OPTION(inviteme);
8936     DEFINE_CHANNEL_OPTION(advtopic);
8937     DEFINE_CHANNEL_OPTION(unreviewed);
8938     modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8939     modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8940     modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8941     if(off_channel > 1)
8942         DEFINE_CHANNEL_OPTION(offchannel);
8943     modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8944
8945     /* Alias set topic to set defaulttopic for compatibility. */
8946     modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8947
8948     /* User options */
8949     DEFINE_USER_OPTION(noautoop);
8950     DEFINE_USER_OPTION(autoinvite);
8951     DEFINE_USER_OPTION(info);
8952
8953     /* Alias uset autovoice to uset autoop. */
8954     modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8955
8956     note_types = dict_new();
8957     dict_set_free_data(note_types, chanserv_deref_note_type);
8958     if(nick)
8959     {
8960         const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8961         chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8962         service_register(chanserv)->trigger = '!';
8963         reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8964     }
8965     saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8966
8967     if(chanserv_conf.channel_expire_frequency)
8968         timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8969
8970     if(chanserv_conf.dnr_expire_frequency)
8971         timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8972
8973     if(chanserv_conf.refresh_period)
8974     {
8975         unsigned long next_refresh;
8976         next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8977         timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8978     }
8979
8980     reg_exit_func(chanserv_db_cleanup);
8981     message_register_table(msgtab);
8982 }