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