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