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