fixed fail from NurPech
[srvx.git] / src / chanserv.c
1 /* chanserv.c - Channel service bot
2  * Copyright 2000-2007 srvx Development Team
3  *
4  * This file is part of srvx.
5  *
6  * srvx is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with srvx; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
19  */
20
21 #include "chanserv.h"
22 #include "conf.h"
23 #include "global.h"
24 #include "modcmd.h"
25 #include "opserv.h" /* for opserv_bad_channel() and devnull management */
26 #include "nickserv.h" /* for oper_outranks() */
27 #include "saxdb.h"
28 #include "spamserv.h"
29 #include "timeq.h"
30
31 #define CHANSERV_CONF_NAME  "services/chanserv"
32
33 /* ChanServ options */
34 #define KEY_SUPPORT_CHANNEL         "support_channel"
35 #define KEY_SUPPORT_CHANNEL_MODES   "support_channel_modes"
36 #define KEY_DB_BACKUP_FREQ          "db_backup_freq"
37 #define KEY_INFO_DELAY              "info_delay"
38 #define KEY_MAX_GREETLEN            "max_greetlen"
39 #define KEY_ADJUST_THRESHOLD        "adjust_threshold"
40 #define KEY_ADJUST_DELAY            "adjust_delay"
41 #define KEY_CHAN_EXPIRE_FREQ        "chan_expire_freq"
42 #define KEY_CHAN_EXPIRE_DELAY       "chan_expire_delay"
43 #define KEY_DNR_EXPIRE_FREQ         "dnr_expire_freq"
44 #define KEY_MAX_CHAN_USERS          "max_chan_users"
45 #define KEY_MAX_CHAN_BANS           "max_chan_bans"
46 #define KEY_MIN_TIME_BANS                       "min_time_bans"
47 #define KEY_NICK                    "nick"
48 #define KEY_OLD_CHANSERV_NAME       "old_chanserv_name"
49 #define KEY_8BALL_RESPONSES         "8ball"
50 #define KEY_OLD_BAN_NAMES           "old_ban_names"
51 #define KEY_REFRESH_PERIOD          "refresh_period"
52 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
53 #define KEY_CTCP_LONG_BAN_DURATION  "ctcp_long_ban_duration"
54 #define KEY_MAX_OWNED               "max_owned"
55 #define KEY_IRC_OPERATOR_EPITHET    "irc_operator_epithet"
56 #define KEY_NETWORK_HELPER_EPITHET  "network_helper_epithet"
57 #define KEY_SUPPORT_HELPER_EPITHET  "support_helper_epithet"
58 #define KEY_NODELETE_LEVEL          "nodelete_level"
59 #define KEY_MAX_USERINFO_LENGTH     "max_userinfo_length"
60 #define KEY_GIVEOWNERSHIP_PERIOD    "giveownership_timeout"
61 #define KEY_INVITED_INTERVAL            "invite_timeout"
62 #define KEY_REVOKE_MODE_A           "revoke_mode_a"
63 #define KEY_NEW_CHANNEL_AUTHED      "new_channel_authed_join"
64 #define KEY_NEW_CHANNEL_UNAUTHED    "new_channel_unauthed_join"
65 #define KEY_NEW_CHANNEL_MSG         "new_channel_message"
66
67 /* ChanServ database */
68 #define KEY_CHANNELS                "channels"
69 #define KEY_NOTE_TYPES              "note_types"
70
71 /* Note type parameters */
72 #define KEY_NOTE_OPSERV_ACCESS      "opserv_access"
73 #define KEY_NOTE_CHANNEL_ACCESS     "channel_access"
74 #define KEY_NOTE_SETTER_ACCESS      "setter_access"
75 #define KEY_NOTE_VISIBILITY         "visibility"
76 #define KEY_NOTE_VIS_PRIVILEGED     "privileged"
77 #define KEY_NOTE_VIS_CHANNEL_USERS  "channel_users"
78 #define KEY_NOTE_VIS_ALL            "all"
79 #define KEY_NOTE_MAX_LENGTH         "max_length"
80 #define KEY_NOTE_SETTER             "setter"
81 #define KEY_NOTE_NOTE               "note"
82
83 /* Do-not-register channels */
84 #define KEY_DNR             "dnr"
85 #define KEY_DNR_SET         "set"
86 #define KEY_DNR_SETTER      "setter"
87 #define KEY_DNR_REASON      "reason"
88
89 /* Channel data */
90 #define KEY_REGISTERED      "registered"
91 #define KEY_REGISTRAR       "registrar"
92 #define KEY_SUSPENDED       "suspended"
93 #define KEY_PREVIOUS        "previous"
94 #define KEY_SUSPENDER       "suspender"
95 #define KEY_ISSUED          "issued"
96 #define KEY_REVOKED         "revoked"
97 #define KEY_SUSPEND_EXPIRES "suspend_expires"
98 #define KEY_SUSPEND_REASON  "suspend_reason"
99 #define KEY_GIVEOWNERSHIP   "giveownership"
100 #define KEY_STAFF_ISSUER    "staff_issuer"
101 #define KEY_OLD_OWNER       "old_owner"
102 #define KEY_TARGET          "target"
103 #define KEY_TARGET_ACCESS   "target_access"
104 #define KEY_VISITED         "visited"
105 #define KEY_TOPIC           "topic"
106 #define KEY_GREETING        "greeting"
107 #define KEY_USER_GREETING   "user_greeting"
108 #define KEY_MODES           "modes"
109 #define KEY_FLAGS           "flags"
110 #define KEY_OPTIONS         "options"
111 #define KEY_USERS           "users"
112 #define KEY_BANS            "bans"
113 #define KEY_MAX             "max"
114 #define KEY_MAX_TIME        "max_time"
115 #define KEY_NOTES           "notes"
116 #define KEY_TOPIC_MASK      "topic_mask"
117 #define KEY_ADVTOPIC_ENTRIES "adv_topic"
118 #define KEY_OWNER_TRANSFER  "owner_transfer"
119 #define KEY_EXPIRE          "expire"
120
121 /* User data */
122 #define KEY_LEVEL   "level"
123 #define KEY_INFO    "info"
124 #define KEY_SEEN    "seen"
125
126 /* Votes */
127 #define KEY_VOTE    "vote"
128 #define KEY_VOTE_START    "votestart"
129 #define KEY_VOTE_OPTIONS    "voptions"
130 #define KEY_VOTE_OPTION_NAME    "voptionname"
131 #define KEY_VOTE_VOTED    "vvoted"
132 #define KEY_VOTE_VOTEDFOR    "vvotefor"
133 #define KEY_VOTE_OPTION_ID    "voptionid"
134 #define KEY_VOTE_OPTION_VOTED    "voptionvoted"
135
136 /* Ban data */
137 #define KEY_OWNER       "owner"
138 #define KEY_REASON      "reason"
139 #define KEY_SET         "set"
140 #define KEY_DURATION    "duration"
141 #define KEY_EXPIRES     "expires"
142 #define KEY_TRIGGERED   "triggered"
143
144 #define CHANNEL_DEFAULT_FLAGS   (CHANNEL_UNREVIEWED)
145 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
146 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
147
148 /* Administrative messages */
149 static const struct message_entry msgtab[] = {
150     { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
151
152 /* Channel registration */
153     { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
154     { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
155     { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
156     { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
157     { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
158     { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
159
160 /* Do-not-register channels */
161     { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
162     { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
163     { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
164     { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
165     { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
166     { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
167     { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
168     { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
169     { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
170     { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
171     { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
172     { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
173     { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
174     { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
175
176 /* Channel unregistration */
177     { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
178     { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
179     { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
180     { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
181
182 /* Channel moving */
183     { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
184     { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
185
186 /* Channel merging */
187     { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
188     { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
189     { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
190     { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
191     { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
192
193 /* Handle unregistration */
194     { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
195
196 /* Error messages */
197     { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
198     { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
199     { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
200     { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
201     { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
202     { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
203     { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
204     { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list.  Use the $bop$b command instead." },
205     { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
206     { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
207     { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
208     { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
209     { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
210     { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
211
212 /* Removing yourself from a channel. */
213     { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
214     { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
215     { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
216
217 /* User management */
218     { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
219     { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
220     { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be lower than maximum (%d)." },
221     { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
222     { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
223     { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
224     { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
225     { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
226
227     { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
228     { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
229     { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
230     { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
231     { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
232     { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
233     { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
234
235 /* Ban management */
236     { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
237     { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
238     { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
239     { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
240     { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
241     { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
242     { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
243     { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
244     { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
245     { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
246     { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
247     { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
248     { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
249     { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
250     { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
251     { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
252
253     { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
254
255 /* Channel management */
256     { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
257     { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
258     { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
259
260     { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
261     { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
262     { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
263     { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
264     { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
265     { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
266     { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
267     { "CSMSG_ADVTOPIC_INVALID_ID", "%d is an invalid advtopic id." },
268
269     { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
270     { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
271     { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
272     { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
273     { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
274     { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
275     { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
276     { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
277     { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
278     { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
279     { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
280     { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
281     { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
282     { "CSMSG_INVALID_NUMERIC",   "$b%d$b is not a valid choice.  Choose one:" },
283     { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
284     { "CSMSG_SET_TOPICMASK",     "$bTopicMask   $b %s" },
285     { "CSMSG_SET_ADVTOPIC",      "$bAdvTopic    $b %s" },
286     { "CSMSG_SET_GREETING",      "$bGreeting    $b %s" },
287     { "CSMSG_SET_USERGREETING",  "$bUserGreeting$b %s" },
288     { "CSMSG_SET_MODES",         "$bModes       $b %s" },
289     { "CSMSG_SET_NODELETE",      "$bNoDelete    $b %s" },
290     { "CSMSG_SET_DYNLIMIT",      "$bDynLimit    $b %s" },
291     { "CSMSG_SET_OFFCHANNEL",    "$bOffChannel  $b %s" },
292     { "CSMSG_SET_USERINFO",      "$bUserInfo    $b %d" },
293     { "CSMSG_SET_GIVE_VOICE",    "$bGiveVoice   $b %d" },
294     { "CSMSG_SET_TOPICSNARF",    "$bTopicSnarf  $b %d" },
295     { "CSMSG_SET_VOTE",          "$bVote        $b %d" },
296     { "CSMSG_SET_INVITEME",      "$bInviteMe    $b %d" },
297     { "CSMSG_SET_ENFOPS",        "$bEnfOps      $b %d" },
298     { "CSMSG_SET_GIVE_OPS",      "$bGiveOps     $b %d" },
299     { "CSMSG_SET_ENFMODES",      "$bEnfModes    $b %d" },
300     { "CSMSG_SET_ENFTOPIC",      "$bEnfTopic    $b %d" },
301     { "CSMSG_SET_PUBCMD",        "$bPubCmd      $b %d" },
302     { "CSMSG_SET_SETTERS",       "$bSetters     $b %d" },
303     { "CSMSG_SET_CTCPUSERS",     "$bCTCPUsers   $b %d" },
304     { "CSMSG_SET_PROTECT",       "$bProtect     $b %d - %s" },
305     { "CSMSG_SET_TOYS",          "$bToys        $b %d - %s" },
306     { "CSMSG_SET_CTCPREACTION",  "$bCTCPReaction$b %d - %s" },
307     { "CSMSG_SET_TOPICREFRESH",  "$bTopicRefresh$b %d - %s" },
308     { "CSMSG_SET_UNREVIEWED",    "$bUnreviewed  $b %s" },
309     { "CSMSG_SET_EXPIRE",        "$bExpire      $b %s" },
310     { "CSMSG_SET_EXPIRE_OFF",    "$bExpire      $b off" },
311     { "CSMSG_USET_NOAUTOOP",     "$bNoAutoOp    $b %s" },
312     { "CSMSG_USET_NOAUTOVOICE",  "$bNoAutoVoice $b %s" },
313     { "CSMSG_USET_AUTOINVITE",   "$bAutoInvite  $b %s" },
314     { "CSMSG_USET_INFO",         "$bInfo        $b %s" },
315
316     { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
317     { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
318     { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
319     { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
320     { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
321     { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
322     { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
323     { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
324     { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
325     { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
326     { "CSMSG_PROTECT_NONE", "No users will be protected." },
327     { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
328     { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
329     { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
330     { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
331     { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
332     { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
333     { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
334     { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
335     { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
336     { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
337     { "CSMSG_CTCPREACTION_SHORTBAN",  "Short timed ban on disallowed CTCPs" },
338     { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
339
340     { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
341     { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
342     { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
343     { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
344     { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
345     { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
346     { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
347     { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
348
349     { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
350     { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
351     { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
352
353 /* Channel userlist */
354     { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
355     { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
356     { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
357     { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
358     { "CSMSG_TOTAL_USERS", "There are $b%d$b users in %s." },
359
360 /* Channel note list */
361     { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
362     { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
363     { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
364     { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
365     { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
366     { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
367     { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
368     { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
369     { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
370     { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
371     { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
372     { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
373     { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
374     { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
375     { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
376
377 /* Channel [un]suspension */
378     { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
379     { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
380     { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
381     { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
382     { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
383     { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
384     { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
385
386 /* Access information */
387     { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
388     { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
389     { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
390     { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
391     { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
392     { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
393     { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
394     { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
395     { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
396     { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
397     { "CSMSG_OPERATOR_TITLE", "IRC operator" },
398     { "CSMSG_UC_H_TITLE", "network helper" },
399     { "CSMSG_LC_H_TITLE", "support helper" },
400     { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
401     { "CSMSG_MYACCESS_COUNT", "%s has access in $b%d$b channels and is owner of $b%d$b channel(s)." },
402         { "CSMSG_MYACCESS_COUNT_1", "%s has access in $b%d$b channel and is owner of $b%d$b channel(s)." },
403
404
405 /* Seen information */
406     { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
407     { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
408     { "CSMSG_USER_VACATION", "%s is currently on vacation." },
409     { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
410
411 /* Names information */
412     { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
413     { "CSMSG_END_NAMES", "End of names in $b%s$b" },
414
415 /* Channel information */
416     { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
417     { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic:       $b%s" },
418     { "CSMSG_CHANNEL_MODES", "$bMode Lock:           $b%s" },
419     { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
420     { "CSMSG_CHANNEL_MAX", "$bRecord Visitors:     $b%i" },
421     { "CSMSG_CHANNEL_MAX_TIME", "$bRecord Visitors:     $b%i (%s ago.)" },
422     { "CSMSG_CHANNEL_OWNER", "$bOwner:               $b%s" },
423     { "CSMSG_CHANNEL_BANS", "$bBan Count:           $b%i" },
424     { "CSMSG_CHANNEL_USERS", "$bTotal User Count:    $b%i" },
425     { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar:           $b%s" },
426     { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
427     { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
428     { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
429     { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
430     { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
431     { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
432     { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
433     { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
434     { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
435     { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
436     { "CSMSG_CHANNEL_REGISTERED", "$bRegistered:          $b%s ago." },
437     { "CSMSG_CHANNEL_VISITED", "$bVisited:             $b%s ago." },
438
439     { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for %s" },
440     { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", " from %s to %s (%d access) by %s on %s (Reason: %s)" },
441     { "CSMSG_CHANNEL_OWNERSHIP_STAFF", " from %s to %s (%d access) by %s on %s" },
442     { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", " from %s to %s (%d access) on %s" },
443
444     { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
445     { "CSMSG_PEEK_TOPIC", "$bTopic:          $b%s" },
446     { "CSMSG_PEEK_MODES", "$bModes:          $b%s" },
447     { "CSMSG_PEEK_USERS", "$bTotal users:    $b%d (%d ops, %d voices, %d regulars)" },
448     { "CSMSG_PEEK_OPS", "$bOps:$b" },
449     { "CSMSG_PEEK_NO_OPS", "$bOps:            $bNone present" },
450
451 /* Network information */
452     { "CSMSG_NETWORK_INFO", "Network Information:" },
453     { "CSMSG_NETWORK_SERVERS", "$bServers:             $b%i" },
454     { "CSMSG_NETWORK_USERS",   "$bTotal Users:         $b%i" },
455     { "CSMSG_NETWORK_BANS",    "$bTotal Ban Count:     $b%i" },
456     { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count:    $b%i" },
457     { "CSMSG_NETWORK_OPERS",   "$bIRC Operators:       $b%i" },
458     { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
459     { "CSMSG_SERVICES_UPTIME", "$bServices Uptime:     $b%s" },
460     { "CSMSG_BURST_LENGTH",    "$bLast Burst Length:   $b%s" },
461
462 /* Staff list */
463     { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
464     { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
465     { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
466
467 /* Channel searches */
468     { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
469     { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
470     { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
471     { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
472
473 /* Channel configuration */
474     { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
475     { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
476     { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
477     { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
478
479 /* User settings */
480     { "CSMSG_USER_OPTIONS", "User Options:" },
481     { "CSMSG_USER_PROTECTED_2", "That user is protected." },
482
483 /* Toys */
484     { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
485     { "CSMSG_PING_RESPONSE", "Pong!" },
486     { "CSMSG_WUT_RESPONSE", "wut" },
487     { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number.  Please use a number greater than 1 with this command." },
488     { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b.  Please use either a single number or standard 4d6+3 format." },
489     { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice.  Please use at most %lu." },
490     { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
491     { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
492     { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
493     { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
494
495 /* Vote */
496     { "CSMSG_ADDVOTE_DONE", "Vote added. Use $baddoption$b to add at least 2 vote options and then $bstartvote$b to start the voting." },
497     { "CSMSG_ADDVOTE_FULL", "There is already a vote in this channel. Use $bdelvote$b to delete it." },
498     { "CSMSG_DELVOTE_DONE", "Vote deleted." },
499     { "CSMSG_NO_VOTE", "There is no vote in this channel." },
500     { "CSMSG_ADDOPTION_DONE", "Vote option added with id %i (%i - %i)." },
501     { "CSMSG_DELOPTION_DONE", "Vote option deleted." },
502     { "CSMSG_DELOPTION_NONE", "Vote option does not exist." },
503     { "CSMSG_VOTE_NEED_OPTIONS", "There must be at least 2 options in a vote." },
504     { "CSMSG_VOTE_NOT_STARTED", "The vote is not started. Use $bstartvote$b to allow voting." },
505     { "CSMSG_VOTE_QUESTION", "Question:   %s" },
506     { "CSMSG_VOTE_OPTION", "$b%i$b:   %s ($b%i$b votes)" },
507     { "CSMSG_VOTERES_QUESTION", "Question:   %s" },
508     { "CSMSG_VOTERES_OPTION", "\ 2%i\ 2:   %s (\ 2%i\ 2 votes)" },
509     { "CSMSG_STARTVOTE_TOP", "\ 2%s\ 2 has started a voting:" },
510     { "CSMSG_STARTVOTE_QUESTION", "\ 2Question:\ 2 %s" },
511     { "CSMSG_STARTVOTE_OPTION", "\ 2%i:\ 2  %s" },
512     { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least \ 2%i\ 2 access can vote." },
513     { "CSMSG_STARTVOTE_HOWTO", "To vote for an option, use \ 2vote ID\ 2. To see the available options and the current votes, use \ 2vote\ 2 without parameters." },
514     { "CSMSG_STARTVOTE_RUNNING", "The vote is already running." },
515     { "CSMSG_VOTE_VOTED", "You have already voted." },
516     { "CSMSG_VOTE_INVALID", "Vote option does not exist." },
517     { "CSMSG_VOTE_DONE", "You voted for $b%s$b." },
518     { "CSMSG_ENDVOTE_DONE", "The vote has been finished." },
519     { "CSMSG_ENDVOTE_STOPPED", "The vote is not running." },
520
521 /* Other things */
522     { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
523     { NULL, NULL }
524 };
525
526 #define CSMSG_ALERT_REGISTERED "%s registered to %s by %s."
527 #define CSMSG_ALERT_UNREGISTERED "%s %s"
528
529 /* eject_user and unban_user flags */
530 #define ACTION_KICK             0x0001
531 #define ACTION_BAN              0x0002
532 #define ACTION_ADD_BAN          0x0004
533 #define ACTION_ADD_TIMED_BAN    0x0008
534 #define ACTION_UNBAN            0x0010
535 #define ACTION_DEL_BAN          0x0020
536
537 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
538 #define MODELEN     40 + KEYLEN
539 #define PADLEN      21
540 #define ACCESSLEN   10
541
542 #define CSFUNC_ARGS user, channel, argc, argv, cmd
543
544 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
545 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
546 #define REQUIRE_PARAMS(N)   if(argc < (N)) {            \
547         reply("MSG_MISSING_PARAMS", argv[0]); \
548         CHANSERV_SYNTAX(); \
549         return 0; }
550
551 DECLARE_LIST(dnrList, struct do_not_register *);
552 DEFINE_LIST(dnrList, struct do_not_register *)
553
554 #define chanserv_notice(target, format...) send_message(target , chanserv , ## format)
555 #define chanserv_oper_message(format...) do { if(chanserv_conf.oper_channel) send_channel_message(chanserv_conf.oper_channel , chanserv , ## format); } while(0)
556
557 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
558
559 struct userNode *chanserv;
560 dict_t note_types;
561 int off_channel;
562 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
563 static struct log_type *CS_LOG;
564
565 static struct
566 {
567     struct channelList  support_channels;
568     struct mod_chanmode default_modes;
569
570     unsigned long   db_backup_frequency;
571     unsigned long   channel_expire_frequency;
572     unsigned long   dnr_expire_frequency;
573     
574     unsigned long   invited_timeout;
575
576     unsigned long   info_delay;
577     unsigned long   adjust_delay;
578     unsigned long   channel_expire_delay;
579     unsigned int    nodelete_level;
580
581     unsigned int    adjust_threshold;
582     int             join_flood_threshold;
583
584     unsigned int    greeting_length;
585     unsigned int    refresh_period;
586     unsigned int    giveownership_period;
587
588     unsigned int    max_owned;
589     unsigned int    max_chan_users;
590     unsigned int    max_chan_bans;
591     unsigned int    min_time_bans;
592     unsigned int    max_userinfo_length;
593     
594     unsigned int    revoke_mode_a;
595
596     struct string_list  *set_shows;
597     struct string_list  *eightball;
598     struct string_list  *old_ban_names;
599
600     const char          *ctcp_short_ban_duration;
601     const char          *ctcp_long_ban_duration;
602
603     const char          *irc_operator_epithet;
604     const char          *network_helper_epithet;
605     const char          *support_helper_epithet;
606
607     const char          *new_channel_authed;
608     const char          *new_channel_unauthed;
609     const char          *new_channel_msg;
610
611     struct chanNode     *oper_channel;
612 } chanserv_conf;
613
614 struct listData
615 {
616     struct userNode *user;
617     struct userNode *bot;
618     struct chanNode *channel;
619     const char      *search;
620     unsigned short  lowest;
621     unsigned short  highest;
622     struct userData **users;
623     struct helpfile_table table;
624 };
625
626 struct ChanUser
627 {
628         struct userNode *user;
629     struct chanNode *chan;
630 };
631
632 enum note_access_type
633 {
634     NOTE_SET_CHANNEL_ACCESS,
635     NOTE_SET_CHANNEL_SETTER,
636     NOTE_SET_PRIVILEGED
637 };
638
639 enum note_visible_type
640 {
641     NOTE_VIS_ALL,
642     NOTE_VIS_CHANNEL_USERS,
643     NOTE_VIS_PRIVILEGED
644 };
645
646 struct note_type
647 {
648     enum note_access_type set_access_type;
649     union {
650         unsigned int     min_opserv;
651         unsigned short   min_ulevel;
652     } set_access;
653     enum note_visible_type visible_type;
654     unsigned int         max_length;
655     unsigned int         refs;
656     char                 name[1];
657 };
658
659 struct note
660 {
661     struct note_type     *type;
662     char                 setter[NICKSERV_HANDLE_LEN+1];
663     char                 note[1];
664 };
665
666 static unsigned int registered_channels;
667 static unsigned int banCount;
668
669 static const struct {
670     char *name;
671     char *title;
672     unsigned short level;
673     char ch;
674 } accessLevels[] = {
675     { "peon", "Peon", UL_PEON, '+' },
676     { "op", "Op", UL_OP, '@' },
677     { "master", "Master", UL_MASTER, '%' },
678     { "coowner", "Coowner", UL_COOWNER, '*' },
679     { "owner", "Owner", UL_OWNER, '!' },
680     { "helper", "BUG:", UL_HELPER, 'X' }
681 };
682
683 static const struct {
684     char *format_name;
685     char *db_name;
686     unsigned short default_value;
687     unsigned int old_idx;
688     unsigned int old_flag;
689     unsigned short flag_value;
690 } levelOptions[] = {
691     { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
692     { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
693     { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
694     { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
695     { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
696     { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
697     { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
698     { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
699     { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
700     { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
701     { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
702     { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
703 };
704
705 struct charOptionValues {
706     char value;
707     char *format_name;
708 } protectValues[] = {
709     { 'a', "CSMSG_PROTECT_ALL" },
710     { 'e', "CSMSG_PROTECT_EQUAL" },
711     { 'l', "CSMSG_PROTECT_LOWER" },
712     { 'n', "CSMSG_PROTECT_NONE" }
713 }, toysValues[] = {
714     { 'd', "CSMSG_TOYS_DISABLED" },
715     { 'n', "CSMSG_TOYS_PRIVATE" },
716     { 'p', "CSMSG_TOYS_PUBLIC" }
717 }, topicRefreshValues[] = {
718     { 'n', "CSMSG_TOPICREFRESH_NEVER" },
719     { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
720     { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
721     { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
722     { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
723 }, ctcpReactionValues[] = {
724     { 'k', "CSMSG_CTCPREACTION_KICK" },
725     { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
726     { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
727     { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
728 };
729
730 static const struct {
731     char *format_name;
732     char *db_name;
733     char default_value;
734     unsigned int old_idx;
735     unsigned char count;
736     struct charOptionValues *values;
737 } charOptions[] = {
738     { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
739     { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
740     { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
741     { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
742 };
743
744 struct userData *helperList;
745 struct chanData *channelList;
746 static struct module *chanserv_module;
747 static unsigned int userCount;
748
749 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
750 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
751 static void unregister_channel(struct chanData *channel, const char *reason);
752
753 unsigned short
754 user_level_from_name(const char *name, unsigned short clamp_level)
755 {
756     unsigned int level = 0, ii;
757     if(isdigit(name[0]))
758         level = strtoul(name, NULL, 10);
759     else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
760         if(!irccasecmp(name, accessLevels[ii].name))
761             level = accessLevels[ii].level;
762     if(level > clamp_level)
763         return 0;
764     return level;
765 }
766
767 int
768 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
769 {
770     char *sep;
771     *minl = strtoul(arg, &sep, 10);
772     if(*sep == '\0')
773     {
774         *maxl = *minl;
775         return 1;
776     }
777     else if(*sep == '-')
778     {
779         *maxl = strtoul(sep+1, &sep, 10);
780         return *sep == '\0';
781     }
782     else
783         return 0;
784 }
785
786 struct userData*
787 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
788 {
789     struct userData *uData, **head;
790
791     if(!channel || !handle)
792         return NULL;
793
794     if(override && HANDLE_FLAGGED(handle, HELPING)
795        && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
796     {
797         for(uData = helperList;
798             uData && uData->handle != handle;
799             uData = uData->next);
800
801         if(!uData)
802         {
803             uData = calloc(1, sizeof(struct userData));
804             uData->handle = handle;
805
806             uData->access = UL_HELPER;
807             uData->seen = 0;
808
809             uData->info = NULL;
810
811             uData->prev = NULL;
812             uData->next = helperList;
813             if(helperList)
814                 helperList->prev = uData;
815             helperList = uData;
816         }
817
818         head = &helperList;
819     }
820     else
821     {
822         for(uData = channel->users; uData; uData = uData->next)
823             if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
824                 break;
825
826         head = &(channel->users);
827     }
828
829     if(uData && (uData != *head))
830     {
831         /* Shuffle the user to the head of whatever list he was in. */
832         if(uData->next)
833             uData->next->prev = uData->prev;
834         if(uData->prev)
835             uData->prev->next = uData->next;
836
837         uData->prev = NULL;
838         uData->next = *head;
839
840         if(*head)
841             (**head).prev = uData;
842         *head = uData;
843     }
844
845     return uData;
846 }
847
848 /* Returns non-zero if user has at least the minimum access.
849  * exempt_owner is set when handling !set, so the owner can set things
850  * to/from >500.
851  */
852 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
853 {
854     struct userData *uData;
855     struct chanData *cData = channel->channel_info;
856     unsigned short minimum = cData->lvlOpts[opt];
857     if(!minimum)
858         return 1;
859     uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
860     if(!uData)
861         return 0;
862     if(minimum <= uData->access)
863         return 1;
864     if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
865         return 1;
866     return 0;
867 }
868
869 /* Scan for other users authenticated to the same handle
870    still in the channel. If so, keep them listed as present.
871
872    user is optional, if not null, it skips checking that userNode
873    (for the handle_part function) */
874 static void
875 scan_user_presence(struct userData *uData, struct userNode *user)
876 {
877     struct modeNode *mn;
878
879     if(IsSuspended(uData->channel)
880        || IsUserSuspended(uData)
881        || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
882     {
883         uData->present = 0;
884     }
885     else
886     {
887         uData->present = 1;
888         uData->seen = now;
889     }
890 }
891
892 static void
893 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
894 {
895     unsigned int eflags, argc;
896     char *argv[4];
897     static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
898
899     /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
900     if(!channel->channel_info
901        || IsSuspended(channel->channel_info)
902        || IsService(user)
903        || !ircncasecmp(text, "ACTION ", 7))
904         return;
905     /* Figure out the minimum level needed to CTCP the channel */
906     if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
907         return;
908     /* We need to enforce against them; do so. */
909     eflags = 0;
910     argv[0] = (char*)text;
911     argv[1] = user->nick;
912     argc = 2;
913     if(GetUserMode(channel, user))
914         eflags |= ACTION_KICK;
915     switch(channel->channel_info->chOpts[chCTCPReaction]) {
916     default: case 'k': /* just do the kick */ break;
917     case 'b':
918         eflags |= ACTION_BAN;
919         break;
920     case 't':
921         eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
922         argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
923         break;
924     case 'T':
925         eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
926         argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
927         break;
928     }
929     argv[argc++] = bad_ctcp_reason;
930     eject_user(chanserv, channel, argc, argv, NULL, eflags);
931 }
932
933 struct note_type *
934 chanserv_create_note_type(const char *name)
935 {
936     struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
937     strcpy(ntype->name, name);
938     ntype->refs = 1;
939     dict_insert(note_types, ntype->name, ntype);
940     return ntype;
941 }
942
943 static void
944 free_vote_options(void *data)
945 {
946     struct vote_option *vOpt = data;
947     free(vOpt->name);
948     free(vOpt->option_str);
949     free(vOpt);
950 }
951
952 static void
953 chanserv_deref_note_type(void *data)
954 {
955     struct note_type *ntype = data;
956
957     if(--ntype->refs > 0)
958         return;
959     free(ntype);
960 }
961
962 static void
963 chanserv_flush_note_type(struct note_type *ntype)
964 {
965     struct chanData *cData;
966     for(cData = channelList; cData; cData = cData->next)
967         dict_remove(cData->notes, ntype->name);
968 }
969
970 static void
971 chanserv_truncate_notes(struct note_type *ntype)
972 {
973     struct chanData *cData;
974     struct note *note;
975     unsigned int size = sizeof(*note) + ntype->max_length;
976
977     for(cData = channelList; cData; cData = cData->next) {
978         note = dict_find(cData->notes, ntype->name, NULL);
979         if(!note)
980             continue;
981         if(strlen(note->note) <= ntype->max_length)
982             continue;
983         dict_remove2(cData->notes, ntype->name, 1);
984         note = realloc(note, size);
985         note->note[ntype->max_length] = 0;
986         dict_insert(cData->notes, ntype->name, note);
987     }
988 }
989
990 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
991
992 static struct note *
993 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
994 {
995     struct note *note;
996     unsigned int len = strlen(text);
997
998     if(len > type->max_length) len = type->max_length;
999     note = calloc(1, sizeof(*note) + len);
1000     note->type = type;
1001     strncpy(note->setter, setter, sizeof(note->setter)-1);
1002     memcpy(note->note, text, len);
1003     note->note[len] = 0;
1004     dict_insert(channel->notes, type->name, note);
1005     type->refs++;
1006     return note;
1007 }
1008
1009 static void
1010 chanserv_free_note(void *data)
1011 {
1012     struct note *note = data;
1013
1014     chanserv_deref_note_type(note->type);
1015     assert(note->type->refs > 0); /* must use delnote to remove the type */
1016     free(note);
1017 }
1018
1019 static MODCMD_FUNC(cmd_createnote) {
1020     struct note_type *ntype;
1021     unsigned int arg = 1, existed = 0, max_length;
1022
1023     if((ntype = dict_find(note_types, argv[1], NULL)))
1024         existed = 1;
1025     else
1026         ntype = chanserv_create_note_type(argv[arg]);
1027     if(!irccasecmp(argv[++arg], "privileged"))
1028     {
1029         arg++;
1030         ntype->set_access_type = NOTE_SET_PRIVILEGED;
1031         ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1032     }
1033     else if(!irccasecmp(argv[arg], "channel"))
1034     {
1035         unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1036         if(!ulvl)
1037         {
1038             reply("CSMSG_INVALID_ACCESS", argv[arg]);
1039             goto fail;
1040         }
1041         ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1042         ntype->set_access.min_ulevel = ulvl;
1043     }
1044     else if(!irccasecmp(argv[arg], "setter"))
1045     {
1046         ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1047     }
1048     else
1049     {
1050         reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1051         goto fail;
1052     }
1053
1054     if(!irccasecmp(argv[++arg], "privileged"))
1055         ntype->visible_type = NOTE_VIS_PRIVILEGED;
1056     else if(!irccasecmp(argv[arg], "channel_users"))
1057         ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1058     else if(!irccasecmp(argv[arg], "all"))
1059         ntype->visible_type = NOTE_VIS_ALL;
1060     else {
1061         reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1062         goto fail;
1063     }
1064
1065     if((arg+1) >= argc) {
1066         reply("MSG_MISSING_PARAMS", argv[0]);
1067         goto fail;
1068     }
1069     max_length = strtoul(argv[++arg], NULL, 0);
1070     if(max_length < 20 || max_length > 450)
1071     {
1072         reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1073         goto fail;
1074     }
1075     if(existed && (max_length < ntype->max_length))
1076     {
1077         ntype->max_length = max_length;
1078         chanserv_truncate_notes(ntype);
1079     }
1080     ntype->max_length = max_length;
1081
1082     if(existed)
1083         reply("CSMSG_NOTE_MODIFIED", ntype->name);
1084     else
1085         reply("CSMSG_NOTE_CREATED", ntype->name);
1086     return 1;
1087
1088 fail:
1089     if(!existed)
1090         dict_remove(note_types, ntype->name);
1091     return 0;
1092 }
1093
1094 static MODCMD_FUNC(cmd_removenote) {
1095     struct note_type *ntype;
1096     int force;
1097
1098     ntype = dict_find(note_types, argv[1], NULL);
1099     force = (argc > 2) && !irccasecmp(argv[2], "force");
1100     if(!ntype)
1101     {
1102         reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1103         return 0;
1104     }
1105     if(ntype->refs > 1)
1106     {
1107         if(!force)
1108         {
1109             reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1110             return 0;
1111         }
1112         chanserv_flush_note_type(ntype);
1113     }
1114     dict_remove(note_types, argv[1]);
1115     reply("CSMSG_NOTE_DELETED", argv[1]);
1116     return 1;
1117 }
1118
1119 static void
1120 chanserv_expire_channel(void *data)
1121 {
1122     struct chanData *channel = data;
1123     char reason[MAXLEN];
1124     sprintf(reason, "channel expired.");
1125     channel->expiry = 0;
1126     spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1127     unregister_channel(channel, reason);
1128 }
1129
1130 static MODCMD_FUNC(chan_opt_expire)
1131 {
1132     struct chanData *cData = channel->channel_info;
1133     unsigned long value = cData->expiry;
1134
1135     if(argc > 1)
1136     {
1137         if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1138         {
1139             reply("MSG_SETTING_PRIVILEGED", argv[0]);
1140             return 0;
1141         }
1142         unsigned long expiry,duration;
1143
1144         /* The two directions can have different ACLs. */
1145         if(!strcmp(argv[1], "0"))
1146             expiry = 0;
1147         else if((duration = ParseInterval(argv[1])))
1148             expiry = now + duration;
1149         else
1150         {
1151             reply("MSG_INVALID_DURATION", argv[1]);
1152             return 0;
1153         }
1154
1155         if (expiry != value)
1156         {
1157             if(value) {
1158                 //unset old timer
1159                 timeq_del(value, chanserv_expire_channel, cData, 0);
1160             }
1161             value = expiry;
1162             cData->expiry = value;
1163             if(value > 0) {
1164                 //New timer!
1165                 timeq_add(expiry, chanserv_expire_channel, cData);
1166             }
1167         }
1168     }
1169
1170     if(cData->expiry > now) {
1171         char expirestr[INTERVALLEN];
1172         reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1173     } else
1174         reply("CSMSG_SET_EXPIRE_OFF");
1175     return 1;
1176 }
1177
1178 static int
1179 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1180 {
1181     if(!orig)
1182         return 0;
1183     if(orig->modes_set & change->modes_clear)
1184         return 1;
1185     if(orig->modes_clear & change->modes_set)
1186         return 1;
1187     if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1188        && strcmp(orig->new_key, change->new_key))
1189         return 1;
1190     if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1191        && (orig->new_limit != change->new_limit))
1192         return 1;
1193     return 0;
1194 }
1195
1196 static char max_length_text[MAXLEN+1][16];
1197
1198 static struct helpfile_expansion
1199 chanserv_expand_variable(const char *variable)
1200 {
1201     struct helpfile_expansion exp;
1202
1203     if(!irccasecmp(variable, "notes"))
1204     {
1205         dict_iterator_t it;
1206         exp.type = HF_TABLE;
1207         exp.value.table.length = 1;
1208         exp.value.table.width = 3;
1209         exp.value.table.flags = 0;
1210         exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1211         exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1212         exp.value.table.contents[0][0] = "Note Type";
1213         exp.value.table.contents[0][1] = "Visibility";
1214         exp.value.table.contents[0][2] = "Max Length";
1215         for(it=dict_first(note_types); it; it=iter_next(it))
1216         {
1217             struct note_type *ntype = iter_data(it);
1218             int row;
1219
1220             if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1221             row = exp.value.table.length++;
1222             exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1223             exp.value.table.contents[row][0] = ntype->name;
1224             exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1225                 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1226                 "unknown";
1227             if(!max_length_text[ntype->max_length][0])
1228                 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1229             exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1230         }
1231         return exp;
1232     }
1233
1234     exp.type = HF_STRING;
1235     exp.value.str = NULL;
1236     return exp;
1237 }
1238
1239 static struct chanData*
1240 register_channel(struct chanNode *cNode, char *registrar)
1241 {
1242     struct chanData *channel;
1243     enum levelOption lvlOpt;
1244     enum charOption chOpt;
1245     int i;
1246
1247     channel = calloc(1, sizeof(struct chanData));
1248
1249     channel->notes = dict_new();
1250     dict_set_free_data(channel->notes, chanserv_free_note);
1251
1252     channel->registrar = strdup(registrar);
1253     channel->registered = now;
1254     channel->visited = now;
1255     channel->limitAdjusted = now;
1256     channel->ownerTransfer = now;
1257     channel->flags = CHANNEL_DEFAULT_FLAGS;
1258     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1259         channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1260     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1261         channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1262     for(i = 0; i < MAXADVTOPICENTRIES; i++) 
1263         channel->advtopic[i] = NULL;
1264
1265     channel->prev = NULL;
1266     channel->next = channelList;
1267
1268     if(channelList)
1269         channelList->prev = channel;
1270     channelList = channel;
1271     registered_channels++;
1272
1273     channel->channel = cNode;
1274     LockChannel(cNode);
1275     cNode->channel_info = channel;
1276     
1277     channel->vote = NULL;
1278
1279     return channel;
1280 }
1281
1282 static struct userData*
1283 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1284 {
1285     struct userData *ud;
1286
1287     if(access_level > UL_OWNER)
1288         return NULL;
1289
1290     ud = calloc(1, sizeof(*ud));
1291     ud->channel = channel;
1292     ud->handle = handle;
1293     ud->seen = seen;
1294     ud->access = access_level;
1295     ud->info = info ? strdup(info) : NULL;
1296
1297     ud->prev = NULL;
1298     ud->next = channel->users;
1299     if(channel->users)
1300         channel->users->prev = ud;
1301     channel->users = ud;
1302
1303     channel->userCount++;
1304     userCount++;
1305
1306     ud->u_prev = NULL;
1307     ud->u_next = ud->handle->channels;
1308     if(ud->u_next)
1309         ud->u_next->u_prev = ud;
1310     ud->handle->channels = ud;
1311
1312     return ud;
1313 }
1314
1315 void
1316 del_channel_user(struct userData *user, int do_gc)
1317 {
1318     struct chanData *channel = user->channel;
1319
1320     channel->userCount--;
1321     userCount--;
1322
1323     if(user->prev)
1324         user->prev->next = user->next;
1325     else
1326         channel->users = user->next;
1327     if(user->next)
1328         user->next->prev = user->prev;
1329
1330     if(user->u_prev)
1331         user->u_prev->u_next = user->u_next;
1332     else
1333         user->handle->channels = user->u_next;
1334     if(user->u_next)
1335         user->u_next->u_prev = user->u_prev;
1336
1337     free(user->info);
1338     free(user);
1339     if(do_gc && !channel->users && !IsProtected(channel)) {
1340         spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1341         unregister_channel(channel, "lost all users.");
1342     }
1343 }
1344
1345 static void expire_ban(void *data);
1346
1347 struct banData*
1348 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1349 {
1350     struct banData *bd;
1351     unsigned int ii, l1, l2;
1352
1353     if(!mask)
1354         return NULL;
1355
1356     bd = malloc(sizeof(struct banData));
1357
1358     bd->channel = channel;
1359     bd->set = set;
1360     bd->triggered = triggered;
1361     bd->expires = expires;
1362
1363     for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1364     {
1365         extern const char *hidden_host_suffix;
1366         const char *old_name = chanserv_conf.old_ban_names->list[ii];
1367         char *new_mask;
1368
1369         l1 = strlen(mask);
1370         l2 = strlen(old_name);
1371         if(l2+2 > l1)
1372             continue;
1373         if(irccasecmp(mask + l1 - l2, old_name))
1374             continue;
1375         new_mask = alloca(MAXLEN);
1376         sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1377         mask = new_mask;
1378     }
1379     safestrncpy(bd->mask, mask, sizeof(bd->mask));
1380     if(owner)
1381         safestrncpy(bd->owner, owner, sizeof(bd->owner));
1382     bd->reason = strdup(reason);
1383
1384     if(expires)
1385         timeq_add(expires, expire_ban, bd);
1386
1387     bd->prev = NULL;
1388     bd->next = channel->bans;
1389     if(channel->bans)
1390         channel->bans->prev = bd;
1391     channel->bans = bd;
1392     channel->banCount++;
1393     banCount++;
1394
1395     return bd;
1396 }
1397
1398 static void
1399 del_channel_ban(struct banData *ban)
1400 {
1401     ban->channel->banCount--;
1402     banCount--;
1403
1404     if(ban->prev)
1405         ban->prev->next = ban->next;
1406     else
1407         ban->channel->bans = ban->next;
1408
1409     if(ban->next)
1410         ban->next->prev = ban->prev;
1411
1412     if(ban->expires)
1413         timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1414
1415     if(ban->reason)
1416         free(ban->reason);
1417
1418     free(ban);
1419 }
1420
1421 static void
1422 expire_ban(void *data)
1423 {
1424     struct banData *bd = data;
1425     if(!IsSuspended(bd->channel))
1426     {
1427         struct banList bans;
1428         struct mod_chanmode change;
1429         unsigned int ii;
1430         bans = bd->channel->channel->banlist;
1431         mod_chanmode_init(&change);
1432         for(ii=0; ii<bans.used; ii++)
1433         {
1434             if(!strcmp(bans.list[ii]->ban, bd->mask))
1435             {
1436                 change.argc = 1;
1437                 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1438                 change.args[0].u.hostmask = bd->mask;
1439                 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1440                 break;
1441             }
1442         }
1443     }
1444     bd->expires = 0;
1445     del_channel_ban(bd);
1446 }
1447
1448 static void chanserv_expire_suspension(void *data);
1449
1450 static void
1451 unregister_channel(struct chanData *channel, const char *reason)
1452 {
1453     struct mod_chanmode change;
1454     char msgbuf[MAXLEN];
1455     int i;
1456
1457     /* After channel unregistration, the following must be cleaned
1458        up:
1459        - Channel information.
1460        - Channel users.
1461        - Channel bans.
1462        - Channel suspension data.
1463        - Timeq entries. (Except timed bans, which are handled elsewhere.)
1464     */
1465
1466     if(!channel)
1467         return;
1468
1469     timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1470
1471     if(off_channel > 0 || chanserv_conf.revoke_mode_a) {
1472         mod_chanmode_init(&change);
1473         if(off_channel > 0)
1474             change.modes_clear |= MODE_REGISTERED;
1475         if(chanserv_conf.revoke_mode_a)
1476             change.modes_clear |= MODE_ACCESS;
1477         mod_chanmode_announce(chanserv, channel->channel, &change);
1478     }
1479
1480     while(channel->users)
1481         del_channel_user(channel->users, 0);
1482
1483     while(channel->bans)
1484         del_channel_ban(channel->bans);
1485
1486     free(channel->topic);
1487     free(channel->registrar);
1488     free(channel->greeting);
1489     free(channel->user_greeting);
1490     free(channel->topic_mask);
1491     
1492     for(i = 0; i < MAXADVTOPICENTRIES; i++) {
1493         if(channel->advtopic[i]) 
1494             free(channel->advtopic[i]);
1495     }
1496
1497     if(channel->prev)
1498         channel->prev->next = channel->next;
1499     else
1500         channelList = channel->next;
1501
1502     if(channel->next)
1503         channel->next->prev = channel->prev;
1504
1505     if(channel->suspended)
1506     {
1507         struct chanNode *cNode = channel->channel;
1508         struct suspended *suspended, *next_suspended;
1509
1510         for(suspended = channel->suspended; suspended; suspended = next_suspended)
1511         {
1512             next_suspended = suspended->previous;
1513             free(suspended->suspender);
1514             free(suspended->reason);
1515             if(suspended->expires)
1516                 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1517             free(suspended);
1518         }
1519
1520         if(cNode)
1521             cNode->channel_info = NULL;
1522     }
1523     if(channel->expiry)
1524         timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1525     channel->channel->channel_info = NULL;
1526     sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1527     dict_delete(channel->notes);
1528     if(!IsSuspended(channel))
1529         DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1530
1531     chanserv_oper_message(CSMSG_ALERT_UNREGISTERED, channel->channel->name, reason);
1532     UnlockChannel(channel->channel);
1533     free(channel);
1534     registered_channels--;
1535 }
1536
1537 static void
1538 expire_channels(void *data)
1539 {
1540     struct chanData *channel, *next;
1541     struct userData *user;
1542     char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1543
1544     intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1545     sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1546
1547     for(channel = channelList; channel; channel = next)
1548     {
1549         next = channel->next;
1550
1551         /* See if the channel can be expired. */
1552         if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1553            || IsProtected(channel))
1554             continue;
1555
1556         /* Make sure there are no high-ranking users still in the channel. */
1557         for(user=channel->users; user; user=user->next)
1558             if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1559                 break;
1560         if(user)
1561             continue;
1562
1563         /* Unregister the channel */
1564         log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1565         spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1566         unregister_channel(channel, "registration expired.");
1567     }
1568
1569     if(chanserv_conf.channel_expire_frequency && !data)
1570         timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1571 }
1572
1573 static void
1574 expire_dnrs(UNUSED_ARG(void *data))
1575 {
1576     dict_iterator_t it, next;
1577     struct do_not_register *dnr;
1578
1579     for(it = dict_first(handle_dnrs); it; it = next)
1580     {
1581         dnr = iter_data(it);
1582         next = iter_next(it);
1583         if(dnr->expires && dnr->expires <= now)
1584             dict_remove(handle_dnrs, dnr->chan_name + 1);
1585     }
1586     for(it = dict_first(plain_dnrs); it; it = next)
1587     {
1588         dnr = iter_data(it);
1589         next = iter_next(it);
1590         if(dnr->expires && dnr->expires <= now)
1591             dict_remove(plain_dnrs, dnr->chan_name + 1);
1592     }
1593     for(it = dict_first(mask_dnrs); it; it = next)
1594     {
1595         dnr = iter_data(it);
1596         next = iter_next(it);
1597         if(dnr->expires && dnr->expires <= now)
1598             dict_remove(mask_dnrs, dnr->chan_name + 1);
1599     }
1600
1601     if(chanserv_conf.dnr_expire_frequency)
1602         timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1603 }
1604
1605 static int
1606 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1607 {
1608     char protect = channel->chOpts[chProtect];
1609     struct userData *cs_victim, *cs_aggressor;
1610
1611     /* Don't protect if no one is to be protected, someone is attacking
1612        himself, or if the aggressor is an IRC Operator. */
1613     if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1614         return 0;
1615
1616     /* Don't protect if the victim isn't authenticated (because they
1617        can't be a channel user), unless we are to protect non-users
1618        also. */
1619     cs_victim = GetChannelAccess(channel, victim->handle_info);
1620     if(protect != 'a' && !cs_victim)
1621         return 0;
1622
1623     /* Protect if the aggressor isn't a user because at this point,
1624        the aggressor can only be less than or equal to the victim. */
1625     cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1626     if(!cs_aggressor)
1627         return 1;
1628
1629     /* If the aggressor was a user, then the victim can't be helped. */
1630     if(!cs_victim)
1631         return 0;
1632
1633     switch(protect)
1634     {
1635     case 'l':
1636         if(cs_victim->access > cs_aggressor->access)
1637             return 1;
1638         break;
1639     case 'a':
1640     case 'e':
1641         if(cs_victim->access >= cs_aggressor->access)
1642             return 1;
1643         break;
1644     }
1645
1646     return 0;
1647 }
1648
1649 static int
1650 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1651 {
1652     struct chanData *cData = channel->channel_info;
1653     struct userData *cs_victim;
1654
1655     if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1656         || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1657        && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1658     {
1659         send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1660         return 0;
1661     }
1662
1663     return 1;
1664 }
1665
1666 static int
1667 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1668 {
1669     if(IsService(victim))
1670     {
1671         send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1672         return 0;
1673     }
1674
1675     if(protect_user(victim, user, channel->channel_info))
1676     {
1677         send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1678         return 0;
1679     }
1680
1681     return 1;
1682 }
1683
1684 static struct do_not_register *
1685 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1686 {
1687     struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1688     safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1689     safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1690     strcpy(dnr->reason, reason);
1691     dnr->set = now;
1692     dnr->expires = expires;
1693     if(dnr->chan_name[0] == '*')
1694         dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1695     else if(strpbrk(dnr->chan_name, "*?"))
1696         dict_insert(mask_dnrs, dnr->chan_name, dnr);
1697     else
1698         dict_insert(plain_dnrs, dnr->chan_name, dnr);
1699     return dnr;
1700 }
1701
1702 static struct dnrList
1703 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1704 {
1705     struct dnrList list;
1706     dict_iterator_t it, next;
1707     struct do_not_register *dnr;
1708
1709     dnrList_init(&list);
1710
1711     if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1712     {
1713         if(dnr->expires && dnr->expires <= now)
1714             dict_remove(handle_dnrs, handle);
1715         else if(list.used < max)
1716             dnrList_append(&list, dnr);
1717     }
1718
1719     if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1720     {
1721         if(dnr->expires && dnr->expires <= now)
1722             dict_remove(plain_dnrs, chan_name);
1723         else if(list.used < max)
1724             dnrList_append(&list, dnr);
1725     }
1726
1727     if(chan_name)
1728     {
1729         for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1730         {
1731             next = iter_next(it);
1732             if(!match_ircglob(chan_name, iter_key(it)))
1733                 continue;
1734             dnr = iter_data(it);
1735             if(dnr->expires && dnr->expires <= now)
1736                 dict_remove(mask_dnrs, iter_key(it));
1737             else
1738                 dnrList_append(&list, dnr);
1739         }
1740     }
1741
1742     return list;
1743 }
1744
1745 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1746 {
1747     struct userNode *user;
1748     char buf1[INTERVALLEN];
1749     char buf2[INTERVALLEN];
1750     time_t feh;
1751
1752     user = extra;
1753     if(dnr->set)
1754     {
1755         feh = dnr->set;
1756         strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1757     }
1758     if(dnr->expires)
1759     {
1760         feh = dnr->expires;
1761         strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1762         send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1763     }
1764     else if(dnr->set)
1765     {
1766         send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1767     }
1768     else
1769         send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1770     return 0;
1771 }
1772
1773 static unsigned int
1774 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1775 {
1776     struct dnrList list;
1777     unsigned int ii;
1778
1779     list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1780     for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1781         dnr_print_func(list.list[ii], user);
1782     if(ii < list.used)
1783         reply("CSMSG_MORE_DNRS", list.used - ii);
1784     free(list.list);
1785     return ii;
1786 }
1787
1788 struct do_not_register *
1789 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1790 {
1791     struct dnrList list;
1792     struct do_not_register *dnr;
1793
1794     list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1795     dnr = list.used ? list.list[0] : NULL;
1796     free(list.list);
1797     return dnr;
1798 }
1799
1800 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1801 {
1802     struct do_not_register *dnr;
1803     dict_iterator_t it, next;
1804     unsigned int matches = 0;
1805
1806     for(it = dict_first(dict); it; it = next)
1807     {
1808         dnr = iter_data(it);
1809         next = iter_next(it);
1810         if(dnr->expires && dnr->expires <= now)
1811         {
1812             dict_remove(dict, iter_key(it));
1813             continue;
1814         }
1815         dnr_print_func(dnr, user);
1816         matches++;
1817     }
1818
1819     return matches;
1820 }
1821
1822 static CHANSERV_FUNC(cmd_noregister)
1823 {
1824     const char *target;
1825     const char *reason;
1826     unsigned long expiry, duration;
1827     unsigned int matches;
1828
1829     if(argc < 2)
1830     {
1831         reply("CSMSG_DNR_SEARCH_RESULTS");
1832         matches = send_dnrs(user, handle_dnrs);
1833         matches += send_dnrs(user, plain_dnrs);
1834         matches += send_dnrs(user, mask_dnrs);
1835         if(matches)
1836             reply("MSG_MATCH_COUNT", matches);
1837         else
1838             reply("MSG_NO_MATCHES");
1839         return 0;
1840     }
1841
1842     target = argv[1];
1843
1844     if(!IsChannelName(target) && (*target != '*'))
1845     {
1846         reply("CSMSG_NOT_DNR", target);
1847         return 0;
1848     }
1849
1850     if(argc > 2)
1851     {
1852         if(argc == 3)
1853         {
1854             reply("MSG_INVALID_DURATION", argv[2]);
1855             return 0;
1856         }
1857
1858         if(!strcmp(argv[2], "0"))
1859             expiry = 0;
1860         else if((duration = ParseInterval(argv[2])))
1861             expiry = now + duration;
1862         else
1863         {
1864             reply("MSG_INVALID_DURATION", argv[2]);
1865             return 0;
1866         }
1867
1868         reason = unsplit_string(argv + 3, argc - 3, NULL);
1869         if((*target == '*') && !get_handle_info(target + 1))
1870         {
1871             reply("MSG_HANDLE_UNKNOWN", target + 1);
1872             return 0;
1873         }
1874         chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1875         reply("CSMSG_NOREGISTER_CHANNEL", target);
1876         return 1;
1877     }
1878
1879     reply("CSMSG_DNR_SEARCH_RESULTS");
1880     if(*target == '*')
1881         matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1882     else
1883         matches = chanserv_show_dnrs(user, cmd, target, NULL);
1884     if(!matches)
1885         reply("MSG_NO_MATCHES");
1886     return 0;
1887 }
1888
1889 static CHANSERV_FUNC(cmd_allowregister)
1890 {
1891     const char *chan_name = argv[1];
1892
1893     if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1894        || dict_remove(plain_dnrs, chan_name)
1895        || dict_remove(mask_dnrs, chan_name))
1896     {
1897         reply("CSMSG_DNR_REMOVED", chan_name);
1898         return 1;
1899     }
1900     reply("CSMSG_NO_SUCH_DNR", chan_name);
1901     return 0;
1902 }
1903
1904 struct dnr_search {
1905     struct userNode *source;
1906     char *chan_mask;
1907     char *setter_mask;
1908     char *reason_mask;
1909     unsigned long min_set, max_set;
1910     unsigned long min_expires, max_expires;
1911     unsigned int limit;
1912 };
1913
1914 static int
1915 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1916 {
1917     return !((dnr->set < search->min_set)
1918              || (dnr->set > search->max_set)
1919              || (dnr->expires < search->min_expires)
1920              || (search->max_expires
1921                  && ((dnr->expires == 0)
1922                      || (dnr->expires > search->max_expires)))
1923              || (search->chan_mask
1924                  && !match_ircglob(dnr->chan_name, search->chan_mask))
1925              || (search->setter_mask
1926                  && !match_ircglob(dnr->setter, search->setter_mask))
1927              || (search->reason_mask
1928                  && !match_ircglob(dnr->reason, search->reason_mask)));
1929 }
1930
1931 static struct dnr_search *
1932 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1933 {
1934     struct dnr_search *discrim;
1935     unsigned int ii;
1936
1937     discrim = calloc(1, sizeof(*discrim));
1938     discrim->source = user;
1939     discrim->chan_mask = NULL;
1940     discrim->setter_mask = NULL;
1941     discrim->reason_mask = NULL;
1942     discrim->max_set = INT_MAX;
1943     discrim->limit = 50;
1944
1945     for(ii=0; ii<argc; ++ii)
1946     {
1947         if(ii == argc - 1)
1948         {
1949             reply("MSG_MISSING_PARAMS", argv[ii]);
1950             goto fail;
1951         }
1952         else if(0 == irccasecmp(argv[ii], "channel"))
1953         {
1954             discrim->chan_mask = argv[++ii];
1955         }
1956         else if(0 == irccasecmp(argv[ii], "setter"))
1957         {
1958             discrim->setter_mask = argv[++ii];
1959         }
1960         else if(0 == irccasecmp(argv[ii], "reason"))
1961         {
1962             discrim->reason_mask = argv[++ii];
1963         }
1964         else if(0 == irccasecmp(argv[ii], "limit"))
1965         {
1966             discrim->limit = strtoul(argv[++ii], NULL, 0);
1967         }
1968         else if(0 == irccasecmp(argv[ii], "set"))
1969         {
1970             const char *cmp = argv[++ii];
1971             if(cmp[0] == '<') {
1972                 if(cmp[1] == '=')
1973                     discrim->min_set = now - ParseInterval(cmp + 2);
1974                 else
1975                     discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1976             } else if(cmp[0] == '=') {
1977                 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1978             } else if(cmp[0] == '>') {
1979                 if(cmp[1] == '=')
1980                     discrim->max_set = now - ParseInterval(cmp + 2);
1981                 else
1982                     discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1983             } else {
1984                 discrim->max_set = now - (ParseInterval(cmp) - 1);
1985             }
1986         }
1987         else if(0 == irccasecmp(argv[ii], "expires"))
1988         {
1989             const char *cmp = argv[++ii];
1990             if(cmp[0] == '<') {
1991                 if(cmp[1] == '=')
1992                     discrim->max_expires = now + ParseInterval(cmp + 2);
1993                 else
1994                     discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1995             } else if(cmp[0] == '=') {
1996                 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1997             } else if(cmp[0] == '>') {
1998                 if(cmp[1] == '=')
1999                     discrim->min_expires = now + ParseInterval(cmp + 2);
2000                 else
2001                     discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
2002             } else {
2003                 discrim->min_expires = now + (ParseInterval(cmp) - 1);
2004             }
2005         }
2006         else
2007         {
2008             reply("MSG_INVALID_CRITERIA", argv[ii]);
2009             goto fail;
2010         }
2011     }
2012     return discrim;
2013
2014   fail:
2015     free(discrim);
2016     return NULL;
2017 }
2018
2019 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
2020
2021 static unsigned int
2022 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
2023 {
2024     struct do_not_register *dnr;
2025     dict_iterator_t next;
2026     dict_iterator_t it;
2027     unsigned int count;
2028     int target_fixed;
2029
2030     /* Initialize local variables. */
2031     count = 0;
2032     target_fixed = 0;
2033     if(discrim->chan_mask)
2034     {
2035         int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
2036         if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
2037             target_fixed = 1;
2038     }
2039
2040     if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
2041     {
2042         /* Check against account-based DNRs. */
2043         dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
2044         if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2045             dsf(dnr, data);
2046     }
2047     else if(target_fixed)
2048     {
2049         /* Check against channel-based DNRs. */
2050         dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2051         if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2052             dsf(dnr, data);
2053     }
2054     else
2055     {
2056         /* Exhaustively search account DNRs. */
2057         for(it = dict_first(handle_dnrs); it; it = next)
2058         {
2059             next = iter_next(it);
2060             dnr = iter_data(it);
2061             if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2062                 break;
2063         }
2064
2065         /* Do the same for channel DNRs. */
2066         for(it = dict_first(plain_dnrs); it; it = next)
2067         {
2068             next = iter_next(it);
2069             dnr = iter_data(it);
2070             if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2071                 break;
2072         }
2073
2074         /* Do the same for wildcarded channel DNRs. */
2075         for(it = dict_first(mask_dnrs); it; it = next)
2076         {
2077             next = iter_next(it);
2078             dnr = iter_data(it);
2079             if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2080                 break;
2081         }
2082     }
2083     return count;
2084 }
2085
2086 static int
2087 dnr_remove_func(struct do_not_register *match, void *extra)
2088 {
2089     struct userNode *user;
2090     char *chan_name;
2091
2092     chan_name = alloca(strlen(match->chan_name) + 1);
2093     strcpy(chan_name, match->chan_name);
2094     user = extra;
2095     if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2096        || dict_remove(plain_dnrs, chan_name)
2097        || dict_remove(mask_dnrs, chan_name))
2098     {
2099         send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2100     }
2101     return 0;
2102 }
2103
2104 static int
2105 dnr_count_func(struct do_not_register *match, void *extra)
2106 {
2107     return 0; (void)match; (void)extra;
2108 }
2109
2110 static MODCMD_FUNC(cmd_dnrsearch)
2111 {
2112     struct dnr_search *discrim;
2113     dnr_search_func action;
2114     struct svccmd *subcmd;
2115     unsigned int matches;
2116     char buf[MAXLEN];
2117
2118     sprintf(buf, "dnrsearch %s", argv[1]);
2119     subcmd = dict_find(cmd->parent->commands, buf, NULL);
2120     if(!subcmd)
2121     {
2122         reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2123         return 0;
2124     }
2125     if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2126         return 0;
2127     if(!irccasecmp(argv[1], "print"))
2128         action = dnr_print_func;
2129     else if(!irccasecmp(argv[1], "remove"))
2130         action = dnr_remove_func;
2131     else if(!irccasecmp(argv[1], "count"))
2132         action = dnr_count_func;
2133     else
2134     {
2135         reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2136         return 0;
2137     }
2138
2139     discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2140     if(!discrim)
2141         return 0;
2142
2143     if(action == dnr_print_func)
2144         reply("CSMSG_DNR_SEARCH_RESULTS");
2145     matches = dnr_search(discrim, action, user);
2146     if(matches)
2147         reply("MSG_MATCH_COUNT", matches);
2148     else
2149         reply("MSG_NO_MATCHES");
2150     free(discrim);
2151     return 1;
2152 }
2153
2154 unsigned int
2155 chanserv_get_owned_count(struct handle_info *hi)
2156 {
2157     struct userData *cList;
2158     unsigned int owned;
2159
2160     for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2161         if(cList->access == UL_OWNER)
2162             owned++;
2163     return owned;
2164 }
2165
2166 static CHANSERV_FUNC(cmd_register)
2167 {
2168     struct handle_info *handle;
2169     struct chanData *cData;
2170     struct modeNode *mn;
2171     char *chan_name;
2172     unsigned int new_channel, force=0;
2173     struct do_not_register *dnr;
2174
2175     if(channel)
2176     {
2177         if(channel->channel_info)
2178         {
2179             reply("CSMSG_ALREADY_REGGED", channel->name);
2180             return 0;
2181         }
2182
2183         if(channel->bad_channel)
2184         {
2185             reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2186             return 0;
2187         }
2188
2189         if(!IsHelping(user)
2190            && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2191         {
2192             reply("CSMSG_MUST_BE_OPPED", channel->name);
2193             return 0;
2194         }
2195
2196         new_channel = 0;
2197         chan_name = channel->name;
2198     }
2199     else
2200     {
2201         if((argc < 2) || !IsChannelName(argv[1]))
2202         {
2203             reply("MSG_NOT_CHANNEL_NAME");
2204             return 0;
2205         }
2206
2207         if(opserv_bad_channel(argv[1]))
2208         {
2209             reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2210             return 0;
2211         }
2212
2213         new_channel = 1;
2214         chan_name = argv[1];
2215     }
2216
2217     if(argc >= (new_channel+2))
2218     {
2219         if(!IsHelping(user))
2220         {
2221             reply("CSMSG_PROXY_FORBIDDEN");
2222             return 0;
2223         }
2224
2225         if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2226             return 0;
2227         force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2228         dnr = chanserv_is_dnr(chan_name, handle);
2229     }
2230     else
2231     {
2232         handle = user->handle_info;
2233         dnr = chanserv_is_dnr(chan_name, handle);
2234     }
2235     if(dnr && !force)
2236     {
2237         if(!IsHelping(user))
2238             reply("CSMSG_DNR_CHANNEL", chan_name);
2239         else
2240             chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2241         return 0;
2242     }
2243
2244     if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2245     {
2246         reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2247         return 0;
2248     }
2249
2250     if(new_channel)
2251         channel = AddChannel(argv[1], now, NULL, NULL);
2252
2253     cData = register_channel(channel, user->handle_info->handle);
2254     scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2255     cData->modes = chanserv_conf.default_modes;
2256     if(off_channel > 0)
2257         cData->modes.modes_set |= MODE_REGISTERED;
2258     if (IsOffChannel(cData))
2259     {
2260         mod_chanmode_announce(chanserv, channel, &cData->modes);
2261     }
2262     else
2263     {
2264         struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2265         change->args[change->argc].mode = MODE_CHANOP;
2266         change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2267         change->argc++;
2268         mod_chanmode_announce(chanserv, channel, change);
2269         mod_chanmode_free(change);
2270     }
2271
2272     /* Initialize the channel's max user record. */
2273     cData->max = channel->members.used;
2274     cData->max_time = 0;
2275
2276     if(handle != user->handle_info)
2277         reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2278     else
2279         reply("CSMSG_REG_SUCCESS", channel->name);
2280
2281     chanserv_oper_message(CSMSG_ALERT_REGISTERED, channel->name, handle->handle, user->handle_info->handle);
2282     return 1;
2283 }
2284
2285 static const char *
2286 make_confirmation_string(struct userData *uData)
2287 {
2288     static char strbuf[16];
2289     char *src;
2290     unsigned int accum;
2291
2292     accum = 0;
2293     for(src = uData->handle->handle; *src; )
2294         accum = accum * 31 + toupper(*src++);
2295     if(uData->channel)
2296         for(src = uData->channel->channel->name; *src; )
2297             accum = accum * 31 + toupper(*src++);
2298     sprintf(strbuf, "%08x", accum);
2299     return strbuf;
2300 }
2301
2302 static CHANSERV_FUNC(cmd_unregister)
2303 {
2304     char *name;
2305     char reason[MAXLEN];
2306     struct chanData *cData;
2307     struct userData *uData;
2308
2309     cData = channel->channel_info;
2310     if(!cData)
2311     {
2312         reply("CSMSG_NOT_REGISTERED", channel->name);
2313         return 0;
2314     }
2315
2316     uData = GetChannelUser(cData, user->handle_info);
2317     if(!uData || (uData->access < UL_OWNER))
2318     {
2319         reply("CSMSG_NO_ACCESS");
2320         return 0;
2321     }
2322
2323     if(IsProtected(cData) && !IsOper(user))
2324     {
2325         reply("CSMSG_UNREG_NODELETE", channel->name);
2326         return 0;
2327     }
2328
2329     if(!IsHelping(user))
2330     {
2331         const char *confirm_string;
2332         if(IsSuspended(cData))
2333         {
2334             reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2335             return 0;
2336         }
2337         confirm_string = make_confirmation_string(uData);
2338         if((argc < 2) || strcmp(argv[1], confirm_string))
2339         {
2340             reply("CSMSG_CONFIRM_UNREG", confirm_string);
2341             return 0;
2342         }
2343     }
2344
2345     sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2346     name = strdup(channel->name);
2347     unregister_channel(cData, reason);
2348     spamserv_cs_unregister(user, channel, manually, "unregistered");
2349     reply("CSMSG_UNREG_SUCCESS", name);
2350     free(name);
2351     return 1;
2352 }
2353
2354 static void
2355 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2356 {
2357     extern struct userNode *spamserv;
2358     struct mod_chanmode *change;
2359
2360     if(spamserv && spamserv_join && get_chanInfo(channel->name))
2361     {
2362         change = mod_chanmode_alloc(2);
2363         change->argc = 2;
2364         change->args[0].mode = MODE_CHANOP;
2365         change->args[0].u.member = AddChannelUser(chanserv, channel);
2366         change->args[1].mode = MODE_CHANOP;
2367         change->args[1].u.member = AddChannelUser(spamserv, channel);
2368     }
2369     else
2370     {
2371         change = mod_chanmode_alloc(1);
2372         change->argc = 1;
2373         change->args[0].mode = MODE_CHANOP;
2374         change->args[0].u.member = AddChannelUser(chanserv, channel);
2375     }
2376
2377    mod_chanmode_announce(chanserv, channel, change);
2378         mod_chanmode_free(change);
2379 }
2380
2381 static CHANSERV_FUNC(cmd_move)
2382 {
2383     struct mod_chanmode change;
2384     struct chanNode *target;
2385     struct modeNode *mn;
2386     struct userData *uData;
2387     char reason[MAXLEN];
2388     struct do_not_register *dnr;
2389     int chanserv_join = 0, spamserv_join;
2390
2391     REQUIRE_PARAMS(2);
2392
2393     if(IsProtected(channel->channel_info))
2394     {
2395         reply("CSMSG_MOVE_NODELETE", channel->name);
2396         return 0;
2397     }
2398
2399     if(!IsChannelName(argv[1]))
2400     {
2401         reply("MSG_NOT_CHANNEL_NAME");
2402         return 0;
2403     }
2404
2405     if(opserv_bad_channel(argv[1]))
2406     {
2407         reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2408         return 0;
2409     }
2410
2411     if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2412     {
2413         for(uData = channel->channel_info->users; uData; uData = uData->next)
2414         {
2415             if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2416             {
2417                 if(!IsHelping(user))
2418                     reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2419                 else
2420                     chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2421                 return 0;
2422             }
2423         }
2424     }
2425
2426     mod_chanmode_init(&change);
2427     if(!(target = GetChannel(argv[1])))
2428     {
2429         target = AddChannel(argv[1], now, NULL, NULL);
2430         if(!IsSuspended(channel->channel_info))
2431             chanserv_join = 1;
2432     }
2433     else if(target->channel_info)
2434     {
2435         reply("CSMSG_ALREADY_REGGED", target->name);
2436         return 0;
2437     }
2438     else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2439             && !IsHelping(user))
2440     {
2441         reply("CSMSG_MUST_BE_OPPED", target->name);
2442         return 0;
2443     }
2444     else if(!IsSuspended(channel->channel_info))
2445         chanserv_join = 1;
2446
2447     if(off_channel > 0)
2448     {
2449         /* Clear MODE_REGISTERED from old channel, add it to new. */
2450         change.argc = 0;
2451         change.modes_clear = MODE_REGISTERED;
2452         mod_chanmode_announce(chanserv, channel, &change);
2453         change.modes_clear = 0;
2454         change.modes_set = MODE_REGISTERED;
2455         mod_chanmode_announce(chanserv, target, &change);
2456     }
2457
2458     /* Move the channel_info to the target channel; it
2459        shouldn't be necessary to clear timeq callbacks
2460        for the old channel. */
2461     target->channel_info = channel->channel_info;
2462     target->channel_info->channel = target;
2463     channel->channel_info = NULL;
2464
2465     /* Check whether users are present in the new channel. */
2466     for(uData = target->channel_info->users; uData; uData = uData->next)
2467         scan_user_presence(uData, NULL);
2468
2469     spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2470
2471         if(chanserv_join)
2472                 ss_cs_join_channel(target, spamserv_join);
2473
2474     sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2475     if(!IsSuspended(target->channel_info))
2476     {
2477         char reason2[MAXLEN];
2478         sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2479         DelChannelUser(chanserv, channel, reason2, 0);
2480     }
2481     UnlockChannel(channel);
2482     LockChannel(target);
2483     global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2484     reply("CSMSG_MOVE_SUCCESS", target->name);
2485     return 1;
2486 }
2487
2488 static void
2489 merge_users(struct chanData *source, struct chanData *target)
2490 {
2491     struct userData *suData, *tuData, *next;
2492     dict_iterator_t it;
2493     dict_t merge;
2494
2495     merge = dict_new();
2496
2497     /* Insert the source's users into the scratch area. */
2498     for(suData = source->users; suData; suData = suData->next)
2499         dict_insert(merge, suData->handle->handle, suData);
2500
2501     /* Iterate through the target's users, looking for
2502        users common to both channels. The lower access is
2503        removed from either the scratch area or target user
2504        list. */
2505     for(tuData = target->users; tuData; tuData = next)
2506     {
2507         struct userData *choice;
2508
2509         next = tuData->next;
2510
2511         /* If a source user exists with the same handle as a target
2512            channel's user, resolve the conflict by removing one. */
2513         suData = dict_find(merge, tuData->handle->handle, NULL);
2514         if(!suData)
2515             continue;
2516
2517         /* Pick the data we want to keep. */
2518         /* If the access is the same, use the later seen time. */
2519         if(suData->access == tuData->access)
2520             choice = (suData->seen > tuData->seen) ? suData : tuData;
2521         else /* Otherwise, keep the higher access level. */
2522             choice = (suData->access > tuData->access) ? suData : tuData;
2523         /* Use the later seen time. */
2524         if(suData->seen < tuData->seen)
2525             suData->seen = tuData->seen;
2526         else
2527             tuData->seen = suData->seen;
2528
2529         /* Remove the user that wasn't picked. */
2530         if(choice == tuData)
2531         {
2532             dict_remove(merge, suData->handle->handle);
2533             del_channel_user(suData, 0);
2534         }
2535         else
2536             del_channel_user(tuData, 0);
2537     }
2538
2539     /* Move the remaining users to the target channel. */
2540     for(it = dict_first(merge); it; it = iter_next(it))
2541     {
2542         suData = iter_data(it);
2543
2544         /* Insert the user into the target channel's linked list. */
2545         suData->prev = NULL;
2546         suData->next = target->users;
2547         suData->channel = target;
2548
2549         if(target->users)
2550             target->users->prev = suData;
2551         target->users = suData;
2552
2553         /* Update the user counts for the target channel; the
2554            source counts are left alone. */
2555         target->userCount++;
2556
2557         /* Check whether the user is in the target channel. */
2558         scan_user_presence(suData, NULL);
2559     }
2560
2561     /* Possible to assert (source->users == NULL) here. */
2562     source->users = NULL;
2563     dict_delete(merge);
2564 }
2565
2566 static void
2567 merge_bans(struct chanData *source, struct chanData *target)
2568 {
2569     struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2570
2571     /* Hold on to the original head of the target ban list
2572        to avoid comparing source bans with source bans. */
2573     tFront = target->bans;
2574
2575     /* Perform a totally expensive O(n*m) merge, ick. */
2576     for(sbData = source->bans; sbData; sbData = sNext)
2577     {
2578         /* Flag to track whether the ban's been moved
2579            to the destination yet. */
2580         int moved = 0;
2581
2582         /* Possible to assert (sbData->prev == NULL) here. */
2583         sNext = sbData->next;
2584
2585         for(tbData = tFront; tbData; tbData = tNext)
2586         {
2587             tNext = tbData->next;
2588
2589             /* Perform two comparisons between each source
2590                and target ban, conflicts are resolved by
2591                keeping the broader ban and copying the later
2592                expiration and triggered time. */
2593             if(match_ircglobs(tbData->mask, sbData->mask))
2594             {
2595                 /* There is a broader ban in the target channel that
2596                    overrides one in the source channel; remove the
2597                    source ban and break. */
2598                 if(sbData->expires > tbData->expires)
2599                     tbData->expires = sbData->expires;
2600                 if(sbData->triggered > tbData->triggered)
2601                     tbData->triggered = sbData->triggered;
2602                 del_channel_ban(sbData);
2603                 break;
2604             }
2605             else if(match_ircglobs(sbData->mask, tbData->mask))
2606             {
2607                 /* There is a broader ban in the source channel that
2608                    overrides one in the target channel; remove the
2609                    target ban, fall through and move the source over. */
2610                 if(tbData->expires > sbData->expires)
2611                     sbData->expires = tbData->expires;
2612                 if(tbData->triggered > sbData->triggered)
2613                     sbData->triggered = tbData->triggered;
2614                 if(tbData == tFront)
2615                     tFront = tNext;
2616                 del_channel_ban(tbData);
2617             }
2618
2619             /* Source bans can override multiple target bans, so
2620                we allow a source to run through this loop multiple
2621                times, but we can only move it once. */
2622             if(moved)
2623                 continue;
2624             moved = 1;
2625
2626             /* Remove the source ban from the source ban list. */
2627             if(sbData->next)
2628                 sbData->next->prev = sbData->prev;
2629
2630             /* Modify the source ban's associated channel. */
2631             sbData->channel = target;
2632
2633             /* Insert the ban into the target channel's linked list. */
2634             sbData->prev = NULL;
2635             sbData->next = target->bans;
2636
2637             if(target->bans)
2638                 target->bans->prev = sbData;
2639             target->bans = sbData;
2640
2641             /* Update the user counts for the target channel. */
2642             target->banCount++;
2643         }
2644     }
2645
2646     /* Possible to assert (source->bans == NULL) here. */
2647     source->bans = NULL;
2648 }
2649
2650 static void
2651 merge_data(struct chanData *source, struct chanData *target)
2652 {
2653     /* Use more recent visited and owner-transfer time; use older
2654      * registered time.  Bitwise or may_opchan.  Use higher max.
2655      * Do not touch last_refresh, ban count or user counts.
2656      */
2657     if(source->visited > target->visited)
2658         target->visited = source->visited;
2659     if(source->registered < target->registered)
2660         target->registered = source->registered;
2661     if(source->ownerTransfer > target->ownerTransfer)
2662         target->ownerTransfer = source->ownerTransfer;
2663     if(source->may_opchan)
2664         target->may_opchan = 1;
2665     if(source->max > target->max) {
2666         target->max = source->max;
2667         target->max_time = source->max_time;
2668     }
2669 }
2670
2671 static void
2672 merge_channel(struct chanData *source, struct chanData *target)
2673 {
2674     merge_users(source, target);
2675     merge_bans(source, target);
2676     merge_data(source, target);
2677 }
2678
2679 static CHANSERV_FUNC(cmd_merge)
2680 {
2681     struct userData *target_user;
2682     struct chanNode *target;
2683     char reason[MAXLEN];
2684
2685     REQUIRE_PARAMS(2);
2686
2687     /* Make sure the target channel exists and is registered to the user
2688        performing the command. */
2689     if(!(target = GetChannel(argv[1])))
2690     {
2691         reply("MSG_INVALID_CHANNEL");
2692         return 0;
2693     }
2694
2695     if(!target->channel_info)
2696     {
2697         reply("CSMSG_NOT_REGISTERED", target->name);
2698         return 0;
2699     }
2700
2701     if(IsProtected(channel->channel_info))
2702     {
2703         reply("CSMSG_MERGE_NODELETE");
2704         return 0;
2705     }
2706
2707     if(IsSuspended(target->channel_info))
2708     {
2709         reply("CSMSG_MERGE_SUSPENDED");
2710         return 0;
2711     }
2712
2713     if(channel == target)
2714     {
2715         reply("CSMSG_MERGE_SELF");
2716         return 0;
2717     }
2718
2719     target_user = GetChannelUser(target->channel_info, user->handle_info);
2720     if(!target_user || (target_user->access < UL_OWNER))
2721     {
2722         reply("CSMSG_MERGE_NOT_OWNER");
2723         return 0;
2724     }
2725
2726     /* Merge the channel structures and associated data. */
2727     merge_channel(channel->channel_info, target->channel_info);
2728     spamserv_cs_move_merge(user, channel, target, 0);
2729     sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2730     unregister_channel(channel->channel_info, reason);
2731     reply("CSMSG_MERGE_SUCCESS", target->name);
2732     return 1;
2733 }
2734
2735 static CHANSERV_FUNC(cmd_opchan)
2736 {
2737     struct mod_chanmode change;
2738     if(!IsHelping(user) && !channel->channel_info->may_opchan)
2739     {
2740         reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2741         return 0;
2742     }
2743     channel->channel_info->may_opchan = 0;
2744     mod_chanmode_init(&change);
2745     change.argc = 1;
2746     change.args[0].mode = MODE_CHANOP;
2747     change.args[0].u.member = GetUserMode(channel, chanserv);
2748     if(!change.args[0].u.member)
2749     {
2750         reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2751         return 0;
2752     }
2753     mod_chanmode_announce(chanserv, channel, &change);
2754     reply("CSMSG_OPCHAN_DONE", channel->name);
2755     return 1;
2756 }
2757
2758 static CHANSERV_FUNC(cmd_adduser)
2759 {
2760     struct userData *actee;
2761     struct userData *actor, *real_actor;
2762     struct handle_info *handle;
2763     unsigned short access_level, override = 0;
2764
2765     REQUIRE_PARAMS(3);
2766
2767     if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2768     {
2769         reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2770         return 0;
2771     }
2772
2773     access_level = user_level_from_name(argv[2], UL_OWNER);
2774     if(!access_level)
2775     {
2776         reply("CSMSG_INVALID_ACCESS", argv[2]);
2777         return 0;
2778     }
2779
2780     actor = GetChannelUser(channel->channel_info, user->handle_info);
2781     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2782
2783     if(actor->access <= access_level)
2784     {
2785         reply("CSMSG_NO_BUMP_ACCESS");
2786         return 0;
2787     }
2788
2789     /* Trying to add someone with equal/more access? */
2790     if (!real_actor || real_actor->access <= access_level)
2791         override = CMD_LOG_OVERRIDE;
2792
2793     if(!(handle = modcmd_get_handle_info(user, argv[1])))
2794         return 0;
2795
2796     if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2797     {
2798         reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2799         return 0;
2800     }
2801
2802     actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2803     scan_user_presence(actee, NULL);
2804     reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2805     return 1 | override;
2806 }
2807
2808 static CHANSERV_FUNC(cmd_clvl)
2809 {
2810     struct handle_info *handle;
2811     struct userData *victim;
2812     struct userData *actor, *real_actor;
2813     unsigned short new_access, override = 0;
2814     int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2815
2816     REQUIRE_PARAMS(3);
2817
2818     actor = GetChannelUser(channel->channel_info, user->handle_info);
2819     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2820
2821     if(!(handle = modcmd_get_handle_info(user, argv[1])))
2822         return 0;
2823
2824     if(handle == user->handle_info && !privileged)
2825     {
2826         reply("CSMSG_NO_SELF_CLVL");
2827         return 0;
2828     }
2829
2830     if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2831     {
2832         reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2833         return 0;
2834     }
2835
2836     if(actor->access <= victim->access && !privileged)
2837     {
2838         reply("MSG_USER_OUTRANKED", handle->handle);
2839         return 0;
2840     }
2841
2842     new_access = user_level_from_name(argv[2], UL_OWNER);
2843
2844     if(!new_access)
2845     {
2846         reply("CSMSG_INVALID_ACCESS", argv[2]);
2847         return 0;
2848     }
2849
2850     if(new_access >= actor->access && !privileged)
2851     {
2852         reply("CSMSG_NO_BUMP_ACCESS");
2853         return 0;
2854     }
2855
2856     /* Trying to clvl a equal/higher user? */
2857     if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2858         override = CMD_LOG_OVERRIDE;
2859     /* Trying to clvl someone to equal/higher access? */
2860     if(!real_actor || new_access >= real_actor->access)
2861         override = CMD_LOG_OVERRIDE;
2862     /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2863      * If they lower their own access it's not a big problem.
2864      */
2865
2866     victim->access = new_access;
2867     reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2868     return 1 | override;
2869 }
2870
2871 static CHANSERV_FUNC(cmd_deluser)
2872 {
2873     struct handle_info *handle;
2874     struct userData *victim;
2875     struct userData *actor, *real_actor;
2876     unsigned short access_level, override = 0;
2877     char *chan_name;
2878
2879     REQUIRE_PARAMS(2);
2880
2881     actor = GetChannelUser(channel->channel_info, user->handle_info);
2882     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2883
2884     if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2885         return 0;
2886
2887     if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2888     {
2889         reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2890         return 0;
2891     }
2892
2893     if(argc > 2)
2894     {
2895         access_level = user_level_from_name(argv[1], UL_OWNER);
2896         if(!access_level)
2897         {
2898             reply("CSMSG_INVALID_ACCESS", argv[1]);
2899             return 0;
2900         }
2901         if(access_level != victim->access)
2902         {
2903             reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2904             return 0;
2905         }
2906     }
2907     else
2908     {
2909         access_level = victim->access;
2910     }
2911
2912     if((actor->access <= victim->access) && !IsHelping(user))
2913     {
2914         reply("MSG_USER_OUTRANKED", victim->handle->handle);
2915         return 0;
2916     }
2917
2918     /* If people delete themselves it is an override, but they
2919      * could've used deleteme so we don't log it as an override
2920      */
2921     if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2922         override = CMD_LOG_OVERRIDE;
2923
2924     chan_name = strdup(channel->name);
2925     del_channel_user(victim, 1);
2926     reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2927     free(chan_name);
2928     return 1 | override;
2929 }
2930
2931 static int
2932 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2933 {
2934     struct userData *actor, *real_actor, *uData, *next;
2935     unsigned int override = 0;
2936
2937     actor = GetChannelUser(channel->channel_info, user->handle_info);
2938     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2939
2940     if(min_access > max_access)
2941     {
2942         reply("CSMSG_BAD_RANGE", min_access, max_access);
2943         return 0;
2944     }
2945
2946     if(actor->access <= max_access)
2947     {
2948         reply("CSMSG_NO_ACCESS");
2949         return 0;
2950     }
2951
2952     if(!real_actor || real_actor->access <= max_access)
2953         override = CMD_LOG_OVERRIDE;
2954
2955     for(uData = channel->channel_info->users; uData; uData = next)
2956     {
2957         next = uData->next;
2958
2959         if((uData->access >= min_access)
2960            && (uData->access <= max_access)
2961            && match_ircglob(uData->handle->handle, mask))
2962             del_channel_user(uData, 1);
2963     }
2964
2965     reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2966     return 1 | override;
2967 }
2968
2969 static CHANSERV_FUNC(cmd_mdelowner)
2970 {
2971     return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2972 }
2973
2974 static CHANSERV_FUNC(cmd_mdelcoowner)
2975 {
2976     return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2977 }
2978
2979 static CHANSERV_FUNC(cmd_mdelmaster)
2980 {
2981     return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2982 }
2983
2984 static CHANSERV_FUNC(cmd_mdelop)
2985 {
2986     return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2987 }
2988
2989 static CHANSERV_FUNC(cmd_mdelpeon)
2990 {
2991     return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2992 }
2993
2994 static int
2995 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2996 {
2997     struct banData *bData, *next;
2998     char interval[INTERVALLEN];
2999     unsigned int count;
3000     unsigned long limit;
3001
3002     count = 0;
3003     limit = now - duration;
3004     for(bData = channel->channel_info->bans; bData; bData = next)
3005     {
3006         next = bData->next;
3007
3008         if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
3009             continue;
3010
3011         del_channel_ban(bData);
3012         count++;
3013     }
3014
3015     intervalString(interval, duration, user->handle_info);
3016     send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
3017     return 1;
3018 }
3019
3020 static int
3021 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
3022 {
3023     struct userData *actor, *uData, *next;
3024     char interval[INTERVALLEN];
3025     unsigned int count;
3026     unsigned long limit;
3027
3028     actor = GetChannelUser(channel->channel_info, user->handle_info);
3029     if(min_access > max_access)
3030     {
3031         send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
3032         return 0;
3033     }
3034
3035     if(!actor || actor->access <= max_access)
3036     {
3037         send_message(user, chanserv, "CSMSG_NO_ACCESS");
3038         return 0;
3039     }
3040
3041     count = 0;
3042     limit = now - duration;
3043     for(uData = channel->channel_info->users; uData; uData = next)
3044     {
3045         next = uData->next;
3046
3047         if((uData->seen > limit)
3048            || uData->present
3049            || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3050             continue;
3051
3052         if(((uData->access >= min_access) && (uData->access <= max_access))
3053            || (!max_access && (uData->access < actor->access)))
3054         {
3055             del_channel_user(uData, 1);
3056             count++;
3057         }
3058     }
3059
3060     if(!max_access)
3061     {
3062         min_access = 1;
3063         max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3064     }
3065     send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3066     return 1;
3067 }
3068
3069 static CHANSERV_FUNC(cmd_trim)
3070 {
3071     unsigned long duration;
3072     unsigned short min_level, max_level;
3073     int vacation;
3074
3075     REQUIRE_PARAMS(3);
3076
3077     vacation = argc > 3 && !strcmp(argv[3], "vacation");
3078     duration = ParseInterval(argv[2]);
3079     if(duration < 60)
3080     {
3081         reply("CSMSG_CANNOT_TRIM");
3082         return 0;
3083     }
3084
3085     if(!irccasecmp(argv[1], "bans"))
3086     {
3087         cmd_trim_bans(user, channel, duration);
3088         return 1;
3089     }
3090     else if(!irccasecmp(argv[1], "users"))
3091     {
3092         cmd_trim_users(user, channel, 0, 0, duration, vacation);
3093         return 1;
3094     }
3095     else if(parse_level_range(&min_level, &max_level, argv[1]))
3096     {
3097         cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3098         return 1;
3099     }
3100     else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3101     {
3102         cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3103         return 1;
3104     }
3105     else
3106     {
3107         reply("CSMSG_INVALID_TRIM", argv[1]);
3108         return 0;
3109     }
3110 }
3111
3112 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3113    to the user. cmd_all takes advantage of this. */
3114 static CHANSERV_FUNC(cmd_up)
3115 {
3116     struct mod_chanmode change;
3117     struct userData *uData;
3118     const char *errmsg;
3119
3120     mod_chanmode_init(&change);
3121     change.argc = 1;
3122     change.args[0].u.member = GetUserMode(channel, user);
3123     if(!change.args[0].u.member)
3124     {
3125         if(argc)
3126             reply("MSG_CHANNEL_ABSENT", channel->name);
3127         return 0;
3128     }
3129
3130     uData = GetChannelAccess(channel->channel_info, user->handle_info);
3131     if(!uData)
3132     {
3133         if(argc)
3134             reply("CSMSG_GODMODE_UP", argv[0]);
3135         return 0;
3136     }
3137     else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3138     {
3139         change.args[0].mode = MODE_CHANOP;
3140         errmsg = "CSMSG_ALREADY_OPPED";
3141     }
3142     else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3143     {
3144         change.args[0].mode = MODE_VOICE;
3145         errmsg = "CSMSG_ALREADY_VOICED";
3146     }
3147     else
3148     {
3149         if(argc)
3150             reply("CSMSG_NO_ACCESS");
3151         return 0;
3152     }
3153     change.args[0].mode &= ~change.args[0].u.member->modes;
3154     if(!change.args[0].mode)
3155     {
3156         if(argc)
3157             reply(errmsg, channel->name);
3158         return 0;
3159     }
3160     modcmd_chanmode_announce(&change);
3161     return 1;
3162 }
3163
3164 static CHANSERV_FUNC(cmd_down)
3165 {
3166     struct mod_chanmode change;
3167
3168     mod_chanmode_init(&change);
3169     change.argc = 1;
3170     change.args[0].u.member = GetUserMode(channel, user);
3171     if(!change.args[0].u.member)
3172     {
3173         if(argc)
3174             reply("MSG_CHANNEL_ABSENT", channel->name);
3175         return 0;
3176     }
3177
3178     if(!change.args[0].u.member->modes)
3179     {
3180         if(argc)
3181             reply("CSMSG_ALREADY_DOWN", channel->name);
3182         return 0;
3183     }
3184
3185     change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3186     modcmd_chanmode_announce(&change);
3187     return 1;
3188 }
3189
3190 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)
3191 {
3192     struct userData *cList;
3193
3194     for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3195     {
3196         if(IsSuspended(cList->channel)
3197            || IsUserSuspended(cList)
3198            || !GetUserMode(cList->channel->channel, user))
3199             continue;
3200
3201         mcmd(user, cList->channel->channel, 0, NULL, cmd);
3202     }
3203
3204     return 1;
3205 }
3206
3207 static CHANSERV_FUNC(cmd_upall)
3208 {
3209     return cmd_all(CSFUNC_ARGS, cmd_up);
3210 }
3211
3212 static CHANSERV_FUNC(cmd_downall)
3213 {
3214     return cmd_all(CSFUNC_ARGS, cmd_down);
3215 }
3216
3217 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3218 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3219
3220 static int
3221 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)
3222 {
3223     unsigned int ii, valid;
3224     struct userNode *victim;
3225     struct mod_chanmode *change;
3226
3227     change = mod_chanmode_alloc(argc - 1);
3228
3229     for(ii=valid=0; ++ii < argc; )
3230     {
3231         if(!(victim = GetUserH(argv[ii])))
3232             continue;
3233         change->args[valid].mode = mode;
3234         change->args[valid].u.member = GetUserMode(channel, victim);
3235         if(!change->args[valid].u.member)
3236             continue;
3237         if(validate && !validate(user, channel, victim))
3238             continue;
3239         valid++;
3240     }
3241
3242     change->argc = valid;
3243     if(valid < (argc-1))
3244         reply("CSMSG_PROCESS_FAILED");
3245     if(valid)
3246     {
3247         modcmd_chanmode_announce(change);
3248         reply(action, channel->name);
3249     }
3250     mod_chanmode_free(change);
3251     return 1;
3252 }
3253
3254 static CHANSERV_FUNC(cmd_op)
3255 {
3256     return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3257 }
3258
3259 static CHANSERV_FUNC(cmd_deop)
3260 {
3261     return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3262 }
3263
3264 static CHANSERV_FUNC(cmd_voice)
3265 {
3266     return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3267 }
3268
3269 static CHANSERV_FUNC(cmd_devoice)
3270 {
3271     return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3272 }
3273
3274 static CHANSERV_FUNC(cmd_opme)
3275 {
3276     struct mod_chanmode change;
3277     const char *errmsg;
3278
3279     mod_chanmode_init(&change);
3280     change.argc = 1;
3281     change.args[0].u.member = GetUserMode(channel, user);
3282     if(!change.args[0].u.member)
3283     {
3284         if(argc)
3285             reply("MSG_CHANNEL_ABSENT", channel->name);
3286         return 0;
3287     }
3288
3289     struct devnull_class *devnull;
3290     if(user->handle_info->devnull && (devnull = devnull_get(user->handle_info->devnull)) && (devnull->modes & DEVNULL_MODE_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))
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_wut)
7133 {
7134     if(channel)
7135     {
7136         char response[MAXLEN];
7137         const char *fmt = user_find_message(user, "CSMSG_WUT_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_WUT_RESPONSE");
7143     return 1;
7144 }
7145
7146 static CHANSERV_FUNC(cmd_8ball)
7147 {
7148     unsigned int i, j, accum;
7149     const char *resp;
7150
7151     REQUIRE_PARAMS(2);
7152     accum = 0;
7153     for(i=1; i<argc; i++)
7154         for(j=0; argv[i][j]; j++)
7155             accum = (accum << 5) - accum + toupper(argv[i][j]);
7156     resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
7157     if(channel)
7158     {
7159         char response[MAXLEN];
7160         sprintf(response, "\ 2%s\ 2: %s", user->nick, resp);
7161         irc_privmsg(cmd->parent->bot, channel->name, response);
7162     }
7163     else
7164         send_message_type(4, user, cmd->parent->bot, "%s", resp);
7165     return 1;
7166 }
7167
7168 static CHANSERV_FUNC(cmd_d)
7169 {
7170     unsigned long sides, count, modifier, ii, total;
7171     char response[MAXLEN], *sep;
7172     const char *fmt;
7173
7174     REQUIRE_PARAMS(2);
7175     if((count = strtoul(argv[1], &sep, 10)) < 1)
7176         goto no_dice;
7177     if(sep[0] == 0)
7178     {
7179         if(count == 1)
7180             goto no_dice;
7181         sides = count;
7182         count = 1;
7183         modifier = 0;
7184     }
7185     else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
7186             && (sides = strtoul(sep+1, &sep, 10)) > 1)
7187     {
7188         if(sep[0] == 0)
7189             modifier = 0;
7190         else if((sep[0] == '-') && isdigit(sep[1]))
7191             modifier = strtoul(sep, NULL, 10);
7192         else if((sep[0] == '+') && isdigit(sep[1]))
7193             modifier = strtoul(sep+1, NULL, 10);
7194         else
7195             goto no_dice;
7196     }
7197     else
7198     {
7199       no_dice:
7200         reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
7201         return 0;
7202     }
7203     if(count > 10)
7204     {
7205         reply("CSMSG_BAD_DICE_COUNT", count, 10);
7206         return 0;
7207     }
7208     for(total = ii = 0; ii < count; ++ii)
7209         total += (rand() % sides) + 1;
7210     total += modifier;
7211
7212     if((count > 1) || modifier)
7213     {
7214         fmt = user_find_message(user, "CSMSG_DICE_ROLL");
7215         sprintf(response, fmt, total, count, sides, modifier);
7216     }
7217     else
7218     {
7219         fmt = user_find_message(user, "CSMSG_DIE_ROLL");
7220         sprintf(response, fmt, total, sides);
7221     }
7222     if(channel)
7223         send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7224     else
7225         send_message_type(4, user, cmd->parent->bot, "%s", response);
7226     return 1;
7227 }
7228
7229 static CHANSERV_FUNC(cmd_huggle)
7230 {
7231     /* CTCP must be via PRIVMSG, never notice */
7232     if(channel)
7233         send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7234     else
7235         send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7236     return 1;
7237 }
7238
7239 static void
7240 chanserv_adjust_limit(void *data)
7241 {
7242     struct mod_chanmode change;
7243     struct chanData *cData = data;
7244     struct chanNode *channel = cData->channel;
7245     unsigned int limit;
7246
7247     if(IsSuspended(cData))
7248         return;
7249
7250     cData->limitAdjusted = now;
7251     limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7252     if(cData->modes.modes_set & MODE_LIMIT)
7253     {
7254         if(limit > cData->modes.new_limit)
7255             limit = cData->modes.new_limit;
7256         else if(limit == cData->modes.new_limit)
7257             return;
7258     }
7259
7260     mod_chanmode_init(&change);
7261     change.modes_set = MODE_LIMIT;
7262     change.new_limit = limit;
7263     mod_chanmode_announce(chanserv, channel, &change);
7264 }
7265
7266 static void
7267 handle_new_channel(struct chanNode *channel)
7268 {
7269     struct chanData *cData;
7270
7271     if(!(cData = channel->channel_info))
7272         return;
7273
7274     if(cData->modes.modes_set || cData->modes.modes_clear)
7275         mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7276
7277     if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7278         SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7279 }
7280
7281 void handle_new_channel_created(char *chan, struct userNode *user) {
7282     if(user->handle_info && chanserv_conf.new_channel_authed) {
7283         send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7284     } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7285         send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7286     }
7287     if(chanserv_conf.new_channel_msg)
7288         send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7289 }
7290
7291 /* Welcome to my worst nightmare. Warning: Read (or modify)
7292    the code below at your own risk. */
7293 static int
7294 handle_join(struct modeNode *mNode)
7295 {
7296     struct mod_chanmode change;
7297     struct userNode *user = mNode->user;
7298     struct chanNode *channel = mNode->channel;
7299     struct chanData *cData;
7300     struct userData *uData = NULL;
7301     struct banData *bData;
7302     struct handle_info *handle;
7303     unsigned int modes = 0, info = 0;
7304     char *greeting;
7305     unsigned int i = 0;
7306
7307     if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7308         return 0;
7309
7310     cData = channel->channel_info;
7311     if(channel->members.used > cData->max) {
7312         cData->max = channel->members.used;
7313         cData->max_time = now;
7314     }
7315
7316     for(i = 0; i < channel->invited.used; i++)
7317     {
7318         if(channel->invited.list[i] == user) {
7319             userList_remove(&channel->invited, user);
7320         }
7321     }
7322
7323     /* Check for bans.  If they're joining through a ban, one of two
7324      * cases applies:
7325      *   1: Join during a netburst, by riding the break.  Kick them
7326      *      unless they have ops or voice in the channel.
7327      *   2: They're allowed to join through the ban (an invite in
7328      *   ircu2.10, or a +e on Hybrid, or something).
7329      * If they're not joining through a ban, and the banlist is not
7330      * full, see if they're on the banlist for the channel.  If so,
7331      * kickban them.
7332      */
7333     if(user->uplink->burst && !mNode->modes)
7334     {
7335         unsigned int ii;
7336         for(ii = 0; ii < channel->banlist.used; ii++)
7337         {
7338             if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7339             {
7340                 /* Riding a netburst.  Naughty. */
7341                 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7342                 return 1;
7343             }
7344         }
7345     }
7346
7347     mod_chanmode_init(&change);
7348     change.argc = 1;
7349     if(channel->banlist.used < MAXBANS)
7350     {
7351         /* Not joining through a ban. */
7352         for(bData = cData->bans;
7353             bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7354             bData = bData->next);
7355
7356         if(bData)
7357         {
7358             char kick_reason[MAXLEN];
7359             sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7360
7361             bData->triggered = now;
7362             if(bData != cData->bans)
7363             {
7364                 /* Shuffle the ban to the head of the list. */
7365                 if(bData->next)
7366                     bData->next->prev = bData->prev;
7367                 if(bData->prev)
7368                     bData->prev->next = bData->next;
7369
7370                 bData->prev = NULL;
7371                 bData->next = cData->bans;
7372
7373                 if(cData->bans)
7374                     cData->bans->prev = bData;
7375                 cData->bans = bData;
7376             }
7377
7378             change.args[0].mode = MODE_BAN;
7379             change.args[0].u.hostmask = bData->mask;
7380             mod_chanmode_announce(chanserv, channel, &change);
7381             KickChannelUser(user, channel, chanserv, kick_reason);
7382             return 1;
7383         }
7384     }
7385
7386     /* ChanServ will not modify the limits in join-flooded channels,
7387        or when there are enough slots left below the limit. */
7388     if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7389        && !channel->join_flooded
7390        && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7391     {
7392         /* The user count has begun "bumping" into the channel limit,
7393            so set a timer to raise the limit a bit. Any previous
7394            timers are removed so three incoming users within the delay
7395            results in one limit change, not three. */
7396
7397         timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7398         timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7399     }
7400
7401     if(channel->join_flooded)
7402     {
7403         /* don't automatically give ops or voice during a join flood */
7404     }
7405     else if(cData->lvlOpts[lvlGiveOps] == 0)
7406         modes |= MODE_CHANOP;
7407     else if(cData->lvlOpts[lvlGiveVoice] == 0)
7408         modes |= MODE_VOICE;
7409
7410     greeting = cData->greeting;
7411     if(user->handle_info)
7412     {
7413         handle = user->handle_info;
7414
7415         if(IsHelper(user) && !IsHelping(user))
7416         {
7417             unsigned int ii;
7418             for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7419             {
7420                 if(channel == chanserv_conf.support_channels.list[ii])
7421                 {
7422                     HANDLE_SET_FLAG(user->handle_info, HELPING);
7423                     break;
7424                 }
7425             }
7426         }
7427
7428         uData = GetTrueChannelAccess(cData, handle);
7429         if(uData && !IsUserSuspended(uData))
7430         {
7431             /* Ops and above were handled by the above case. */
7432             if(IsUserAutoOp(uData))
7433             {
7434                 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7435                     modes |= MODE_CHANOP;
7436                 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7437                     modes |= MODE_VOICE;
7438             }
7439             if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7440                 cData->visited = now;
7441             if(cData->user_greeting)
7442                 greeting = cData->user_greeting;
7443             if(uData->info
7444                && (uData->access >= cData->lvlOpts[lvlUserInfo])
7445                && ((now - uData->seen) >= chanserv_conf.info_delay)
7446                && !uData->present)
7447                 info = 1;
7448             uData->seen = now;
7449             uData->present = 1;
7450         }
7451     }
7452
7453     /* If user joining normally (not during burst), apply op or voice,
7454      * and send greeting/userinfo as appropriate.
7455      */
7456     if(!user->uplink->burst)
7457     {
7458         if(modes)
7459         {
7460             if(modes & MODE_CHANOP)
7461                 modes &= ~MODE_VOICE;
7462             change.args[0].mode = modes;
7463             change.args[0].u.member = mNode;
7464             mod_chanmode_announce(chanserv, channel, &change);
7465         }
7466         if(greeting)
7467             send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7468         if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7469             send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7470     }
7471     return 0;
7472 }
7473
7474 static void
7475 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7476 {
7477     struct mod_chanmode change;
7478     struct userData *channel;
7479     unsigned int ii, jj;
7480
7481     if(!user->handle_info)
7482         return;
7483
7484     mod_chanmode_init(&change);
7485     change.argc = 1;
7486     for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7487     {
7488         struct chanNode *cn;
7489         struct modeNode *mn;
7490         if(IsUserSuspended(channel)
7491            || IsSuspended(channel->channel)
7492            || !(cn = channel->channel->channel))
7493             continue;
7494
7495         mn = GetUserMode(cn, user);
7496         if(!mn)
7497         {
7498             if(!IsUserSuspended(channel)
7499                && IsUserAutoInvite(channel)
7500                && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7501                && !self->burst
7502                && !user->uplink->burst)
7503                 irc_invite(chanserv, user, cn);
7504             continue;
7505         }
7506
7507         if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7508             channel->channel->visited = now;
7509
7510         if(IsUserAutoOp(channel))
7511         {
7512             if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7513                 change.args[0].mode = MODE_CHANOP;
7514             else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7515                 change.args[0].mode = MODE_VOICE;
7516             else
7517                 change.args[0].mode = 0;
7518             change.args[0].u.member = mn;
7519             if(change.args[0].mode)
7520                 mod_chanmode_announce(chanserv, cn, &change);
7521         }
7522
7523         channel->seen = now;
7524         channel->present = 1;
7525     }
7526
7527     for(ii = 0; ii < user->channels.used; ++ii)
7528     {
7529         struct chanNode *chan = user->channels.list[ii]->channel;
7530         struct banData *ban;
7531
7532         if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7533            || !chan->channel_info
7534            || IsSuspended(chan->channel_info))
7535             continue;
7536         for(jj = 0; jj < chan->banlist.used; ++jj)
7537             if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7538                 break;
7539         if(jj < chan->banlist.used)
7540             continue;
7541         for(ban = chan->channel_info->bans; ban; ban = ban->next)
7542         {
7543             char kick_reason[MAXLEN];
7544             if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7545                 continue;
7546             change.args[0].mode = MODE_BAN;
7547             change.args[0].u.hostmask = ban->mask;
7548             mod_chanmode_announce(chanserv, chan, &change);
7549             sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7550             KickChannelUser(user, chan, chanserv, kick_reason);
7551             ban->triggered = now;
7552             break;
7553         }
7554     }
7555
7556     if(IsSupportHelper(user))
7557     {
7558         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7559         {
7560             if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7561             {
7562                 HANDLE_SET_FLAG(user->handle_info, HELPING);
7563                 break;
7564             }
7565         }
7566     }
7567 }
7568
7569 static void
7570 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7571 {
7572     struct chanData *cData;
7573     struct userData *uData;
7574
7575     cData = mn->channel->channel_info;
7576     if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7577         return;
7578
7579     if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7580     {
7581         /* Allow for a bit of padding so that the limit doesn't
7582            track the user count exactly, which could get annoying. */
7583         if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7584         {
7585             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7586             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7587         }
7588     }
7589
7590     if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7591     {
7592         scan_user_presence(uData, mn->user);
7593         uData->seen = now;
7594         if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7595             cData->visited = now;
7596     }
7597
7598     if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7599     {
7600         unsigned int ii;
7601         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7602             struct chanNode *channel;
7603             struct userNode *exclude;
7604             /* When looking at the channel that is being /part'ed, we
7605              * have to skip over the client that is leaving.  For
7606              * other channels, we must not do that.
7607              */
7608             channel = chanserv_conf.support_channels.list[ii];
7609             exclude = (channel == mn->channel) ? mn->user : NULL;
7610             if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7611                 break;
7612         }
7613         if(ii == chanserv_conf.support_channels.used)
7614             HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7615     }
7616 }
7617
7618 static void
7619 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7620 {
7621     struct userData *uData;
7622
7623     if(!channel->channel_info || !kicker || IsService(kicker)
7624        || (kicker == victim) || IsSuspended(channel->channel_info)
7625        || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7626         return;
7627
7628     if(protect_user(victim, kicker, channel->channel_info))
7629     {
7630         const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7631         KickChannelUser(kicker, channel, chanserv, reason);
7632     }
7633
7634     if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7635         uData->seen = now;
7636 }
7637
7638 static int
7639 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7640 {
7641     struct chanData *cData;
7642
7643     if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7644         return 0;
7645
7646     cData = channel->channel_info;
7647     if(bad_topic(channel, user, channel->topic))
7648     {
7649         send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7650         if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7651             SetChannelTopic(channel, chanserv, old_topic, 1);
7652         else if(cData->topic)
7653             SetChannelTopic(channel, chanserv, cData->topic, 1);
7654         return 1;
7655     }
7656     /* With topicsnarf, grab the topic and save it as the default topic. */
7657     if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7658     {
7659         free(cData->topic);
7660         cData->topic = strdup(channel->topic);
7661     }
7662     return 0;
7663 }
7664
7665 static void
7666 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7667 {
7668     struct mod_chanmode *bounce = NULL;
7669     unsigned int bnc, ii;
7670     char deopped = 0;
7671
7672     if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7673         return;
7674
7675     if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7676        && mode_lock_violated(&channel->channel_info->modes, change))
7677     {
7678         char correct[MAXLEN];
7679         bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7680         mod_chanmode_format(&channel->channel_info->modes, correct);
7681         send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7682     }
7683     for(ii = bnc = 0; ii < change->argc; ++ii)
7684     {
7685         if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7686         {
7687             const struct userNode *victim = change->args[ii].u.member->user;
7688             if(!protect_user(victim, user, channel->channel_info))
7689                 continue;
7690             if(!bounce)
7691                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7692             if(!deopped)
7693             {
7694                 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7695                 bounce->args[bnc].u.member = GetUserMode(channel, user);
7696                 if(bounce->args[bnc].u.member)
7697                     bnc++;
7698                 deopped = 1;
7699             }
7700             bounce->args[bnc].mode = MODE_CHANOP;
7701             bounce->args[bnc].u.member = change->args[ii].u.member;
7702             bnc++;
7703             send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7704         }
7705         else if(change->args[ii].mode & MODE_CHANOP)
7706         {
7707             const struct userNode *victim = change->args[ii].u.member->user;
7708             if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7709                 continue;
7710             if(!bounce)
7711                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7712             bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7713             bounce->args[bnc].u.member = change->args[ii].u.member;
7714             bnc++;
7715         }
7716         else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7717         {
7718             const char *ban = change->args[ii].u.hostmask;
7719             if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7720                 continue;
7721             if(!bounce)
7722                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7723             bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7724             bounce->args[bnc].u.hostmask = strdup(ban);
7725             bnc++;
7726             send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7727         }
7728     }
7729     if(bounce)
7730     {
7731         if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7732             mod_chanmode_announce(chanserv, channel, bounce);
7733         for(ii = 0; ii < change->argc; ++ii)
7734             if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7735                 free((char*)bounce->args[ii].u.hostmask);
7736         mod_chanmode_free(bounce);
7737     }
7738 }
7739
7740 static void
7741 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7742 {
7743     struct chanNode *channel;
7744     struct banData *bData;
7745     struct mod_chanmode change;
7746     unsigned int ii, jj;
7747     char kick_reason[MAXLEN];
7748
7749     mod_chanmode_init(&change);
7750     change.argc = 1;
7751     change.args[0].mode = MODE_BAN;
7752     for(ii = 0; ii < user->channels.used; ++ii)
7753     {
7754         channel = user->channels.list[ii]->channel;
7755         /* Need not check for bans if they're opped or voiced. */
7756         if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7757             continue;
7758         /* Need not check for bans unless channel registration is active. */
7759         if(!channel->channel_info || IsSuspended(channel->channel_info))
7760             continue;
7761         /* Look for a matching ban already on the channel. */
7762         for(jj = 0; jj < channel->banlist.used; ++jj)
7763             if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7764                 break;
7765         /* Need not act if we found one. */
7766         if(jj < channel->banlist.used)
7767             continue;
7768         /* Look for a matching ban in this channel. */
7769         for(bData = channel->channel_info->bans; bData; bData = bData->next)
7770         {
7771             if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7772                 continue;
7773             change.args[0].u.hostmask = bData->mask;
7774             mod_chanmode_announce(chanserv, channel, &change);
7775             sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7776             KickChannelUser(user, channel, chanserv, kick_reason);
7777             bData->triggered = now;
7778             break; /* we don't need to check any more bans in the channel */
7779         }
7780     }
7781 }
7782
7783 static void handle_rename(struct handle_info *handle, const char *old_handle)
7784 {
7785     struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7786
7787     if(dnr)
7788     {
7789         dict_remove2(handle_dnrs, old_handle, 1);
7790         safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7791         dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7792     }
7793 }
7794
7795 static void
7796 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7797 {
7798     struct userNode *h_user;
7799
7800     if(handle->channels)
7801     {
7802         for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7803             send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7804
7805         while(handle->channels)
7806             del_channel_user(handle->channels, 1);
7807     }
7808 }
7809
7810 static void
7811 handle_server_link(UNUSED_ARG(struct server *server))
7812 {
7813     struct chanData *cData;
7814
7815     for(cData = channelList; cData; cData = cData->next)
7816     {
7817         if(!IsSuspended(cData))
7818             cData->may_opchan = 1;
7819         if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7820            && !cData->channel->join_flooded
7821            && ((cData->channel->limit - cData->channel->members.used)
7822                < chanserv_conf.adjust_threshold))
7823         {
7824             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7825             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7826         }
7827     }
7828 }
7829
7830 static void
7831 chanserv_conf_read(void)
7832 {
7833     dict_t conf_node;
7834     const char *str;
7835     char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7836     struct mod_chanmode *change;
7837     struct string_list *strlist;
7838     struct chanNode *chan;
7839     unsigned int ii;
7840
7841     if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7842     {
7843         log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7844         return;
7845     }
7846     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7847         UnlockChannel(chanserv_conf.support_channels.list[ii]);
7848     chanserv_conf.support_channels.used = 0;
7849     if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7850     {
7851         for(ii = 0; ii < strlist->used; ++ii)
7852         {
7853             const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7854             if(!str2)
7855                 str2 = "+nt";
7856             chan = AddChannel(strlist->list[ii], now, str2, NULL);
7857             LockChannel(chan);
7858             channelList_append(&chanserv_conf.support_channels, chan);
7859         }
7860     }
7861     else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7862     {
7863         const char *str2;
7864         str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7865         if(!str2)
7866             str2 = "+nt";
7867         chan = AddChannel(str, now, str2, NULL);
7868         LockChannel(chan);
7869         channelList_append(&chanserv_conf.support_channels, chan);
7870     }
7871     str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7872     chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7873     str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7874     chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7875     str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7876     chanserv_conf.greeting_length = str ? atoi(str) : 200;
7877     str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7878     chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7879     str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7880     chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7881     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7882     chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7883     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7884     chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7885     str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7886     chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7887     str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7888     chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7889     str = database_get_data(conf_node, KEY_REVOKE_MODE_A, RECDB_QSTRING);
7890     chanserv_conf.revoke_mode_a = str ? atoi(str) : 1;
7891     str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7892     chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7893     str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7894     chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7895     str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7896     chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7897     str = database_get_data(conf_node, KEY_MIN_TIME_BANS, RECDB_QSTRING);
7898     chanserv_conf.min_time_bans = str ? atoi(str) : 5;
7899     str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7900     chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7901     str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7902     if(chanserv && str)
7903         NickChange(chanserv, str, 0);
7904     str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7905     chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7906     str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7907     chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7908     str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7909     chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7910     str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7911     chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7912     str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7913     chanserv_conf.max_owned = str ? atoi(str) : 5;
7914     str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7915     chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7916     str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7917     chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7918     str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7919     chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7920     str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7921     chanserv_conf.new_channel_authed = (str && *str) ? str : NULL;
7922     str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7923     chanserv_conf.new_channel_unauthed = (str && *str) ? str : NULL;
7924     str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7925     chanserv_conf.new_channel_msg = (str && *str) ? str : NULL;
7926     str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7927     if(!str)
7928         str = "+nt";
7929     safestrncpy(mode_line, str, sizeof(mode_line));
7930     ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7931     if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7932        && (change->argc < 2))
7933     {
7934         chanserv_conf.default_modes = *change;
7935         mod_chanmode_free(change);
7936     }
7937     free_string_list(chanserv_conf.set_shows);
7938     strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7939     if(strlist)
7940         strlist = string_list_copy(strlist);
7941     else
7942     {
7943         static const char *list[] = {
7944             /* free form text */
7945             "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7946             /* options based on user level */
7947             "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7948             "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7949             /* multiple choice options */
7950             "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7951             /* binary options */
7952             "DynLimit", "NoDelete", "expire", "Vote",
7953             /* delimiter */
7954             NULL
7955         };
7956         strlist = alloc_string_list(ArrayLength(list)-1);
7957         for(ii=0; list[ii]; ii++)
7958             string_list_append(strlist, strdup(list[ii]));
7959     }
7960     chanserv_conf.set_shows = strlist;
7961     /* We don't look things up now, in case the list refers to options
7962      * defined by modules initialized after this point.  Just mark the
7963      * function list as invalid, so it will be initialized.
7964      */
7965     set_shows_list.used = 0;
7966     free_string_list(chanserv_conf.eightball);
7967     strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7968     if(strlist)
7969     {
7970         strlist = string_list_copy(strlist);
7971     }
7972     else
7973     {
7974         strlist = alloc_string_list(4);
7975         string_list_append(strlist, strdup("Yes."));
7976         string_list_append(strlist, strdup("No."));
7977         string_list_append(strlist, strdup("Maybe so."));
7978     }
7979     chanserv_conf.eightball = strlist;
7980     free_string_list(chanserv_conf.old_ban_names);
7981     strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7982     if(strlist)
7983         strlist = string_list_copy(strlist);
7984     else
7985         strlist = alloc_string_list(2);
7986     chanserv_conf.old_ban_names = strlist;
7987     str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7988     off_channel = str ? atoi(str) : 0;
7989
7990     str = database_get_data(conf_node, "oper_channel", RECDB_QSTRING);
7991         if(str)
7992         {
7993                 chanserv_conf.oper_channel = AddChannel(str, now, "+tinms", NULL);
7994         }
7995         else
7996         {
7997                 chanserv_conf.oper_channel = NULL;
7998         }
7999 }
8000
8001 static void
8002 chanserv_note_type_read(const char *key, struct record_data *rd)
8003 {
8004     dict_t obj;
8005     struct note_type *ntype;
8006     const char *str;
8007
8008     if(!(obj = GET_RECORD_OBJECT(rd)))
8009     {
8010         log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
8011         return;
8012     }
8013     if(!(ntype = chanserv_create_note_type(key)))
8014     {
8015         log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
8016         return;
8017     }
8018
8019     /* Figure out set access */
8020     if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
8021     {
8022         ntype->set_access_type = NOTE_SET_PRIVILEGED;
8023         ntype->set_access.min_opserv = strtoul(str, NULL, 0);
8024     }
8025     else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
8026     {
8027         ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
8028         ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
8029     }
8030     else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
8031     {
8032         ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
8033     }
8034     else
8035     {
8036         log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
8037         ntype->set_access_type = NOTE_SET_PRIVILEGED;
8038         ntype->set_access.min_opserv = 0;
8039     }
8040
8041     /* Figure out visibility */
8042     if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
8043         ntype->visible_type = NOTE_VIS_PRIVILEGED;
8044     else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
8045         ntype->visible_type = NOTE_VIS_PRIVILEGED;
8046     else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
8047         ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
8048     else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
8049         ntype->visible_type = NOTE_VIS_ALL;
8050     else
8051         ntype->visible_type = NOTE_VIS_PRIVILEGED;
8052
8053     str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
8054     ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
8055 }
8056
8057 static void
8058 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8059 {
8060     struct vote_option *vOpt;
8061     char *str;
8062     
8063     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8064     {
8065         log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
8066         return;
8067     }
8068     
8069     vOpt = calloc(1, sizeof(*vOpt));
8070     vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
8071     str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
8072     vOpt->voted = str ? atoi(str) : 0;
8073     vOpt->option_id = str ? atoi(key) : 0;
8074     vOpt->option_str = strdup(key);
8075     dict_insert(chan->vote_options,vOpt->option_str,vOpt);
8076 }
8077
8078 static void
8079 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8080 {
8081     struct handle_info *handle;
8082     struct userData *uData;
8083     char *seen, *inf, *flags, *voted, *votefor, *expires;
8084     unsigned long last_seen;
8085     unsigned short access_level;
8086
8087     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8088     {
8089         log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
8090         return;
8091     }
8092
8093     access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
8094     if(access_level > UL_OWNER)
8095     {
8096         log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
8097         return;
8098     }
8099
8100     inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
8101     seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
8102     last_seen = seen ? strtoul(seen, NULL, 0) : now;
8103     flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
8104     expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
8105     voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
8106     votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
8107     handle = get_handle_info(key);
8108     if(!handle)
8109     {
8110         log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
8111         return;
8112     }
8113
8114     uData = add_channel_user(chan, handle, access_level, last_seen, inf);
8115     uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
8116         uData->expires = expires ? (signed)strtoul(expires, NULL, 0) : 0;
8117
8118         if((uData->flags & USER_SUSPENDED) && uData->expires)
8119         {
8120           if(uData->expires > now)
8121                   timeq_add(uData->expires, chanserv_expire_user_suspension, uData);
8122           else
8123                   uData->flags &= ~USER_SUSPENDED;
8124         }
8125     if(chan->vote) {
8126         uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
8127         uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
8128     } else {
8129         uData->voted = 0;
8130         uData->votefor = 0;
8131     }
8132 }
8133
8134 static void
8135 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
8136 {
8137     char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
8138     unsigned long set_time, triggered_time, expires_time;
8139
8140     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
8141     {
8142         log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
8143         return;
8144     }
8145
8146     set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
8147     triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
8148     s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
8149     s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
8150     owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
8151     reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
8152     if (!reason || !owner)
8153         return;
8154
8155     set_time = set ? strtoul(set, NULL, 0) : now;
8156     triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
8157     if(s_expires)
8158         expires_time = strtoul(s_expires, NULL, 0);
8159     else if(s_duration)
8160         expires_time = set_time + atoi(s_duration);
8161     else
8162         expires_time = 0;
8163
8164     if(!reason || (expires_time && (expires_time < now)))
8165         return;
8166
8167     add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
8168 }
8169
8170 static struct suspended *
8171 chanserv_read_suspended(dict_t obj)
8172 {
8173     struct suspended *suspended = calloc(1, sizeof(*suspended));
8174     char *str;
8175     dict_t previous;
8176
8177     str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
8178     suspended->expires = str ? strtoul(str, NULL, 0) : 0;
8179     str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
8180     suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
8181     str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8182     suspended->issued = str ? strtoul(str, NULL, 0) : 0;
8183     suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
8184     suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
8185     previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8186     suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
8187     return suspended;
8188 }
8189
8190 static struct giveownership *
8191 chanserv_read_giveownership(dict_t obj)
8192 {
8193     struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
8194     char *str;
8195     dict_t previous;
8196
8197     str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
8198     giveownership->staff_issuer = str ? strdup(str) : NULL;
8199
8200     giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
8201
8202     giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
8203     giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
8204
8205     str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
8206     giveownership->reason = str ? strdup(str) : NULL;
8207     str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
8208     giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
8209
8210     previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
8211     giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
8212     return giveownership;
8213 }
8214
8215 static int
8216 chanserv_channel_read(const char *key, struct record_data *hir)
8217 {
8218     struct suspended *suspended;
8219     struct giveownership *giveownership;
8220     struct mod_chanmode *modes;
8221     struct chanNode *cNode;
8222     struct chanData *cData;
8223     struct dict *channel, *obj;
8224     char *str, *argv[10];
8225     dict_iterator_t it;
8226     unsigned int argc;
8227
8228     channel = hir->d.object;
8229
8230     str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
8231     if(!str)
8232         str = "<unknown>";
8233     cNode = AddChannel(key, now, NULL, NULL);
8234     if(!cNode)
8235     {
8236         log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
8237         return 0;
8238     }
8239     cData = register_channel(cNode, str);
8240     if(!cData)
8241     {
8242         log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
8243         return 0;
8244     }
8245
8246     if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
8247     {
8248         enum levelOption lvlOpt;
8249         enum charOption chOpt;
8250
8251         if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
8252             cData->flags = atoi(str);
8253
8254         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8255         {
8256             str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
8257             if(str)
8258                 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
8259             else if(levelOptions[lvlOpt].old_flag)
8260             {
8261                 if(cData->flags & levelOptions[lvlOpt].old_flag)
8262                     cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
8263                 else
8264                     cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
8265             }
8266         }
8267
8268         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8269         {
8270             if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8271                 continue;
8272             cData->chOpts[chOpt] = str[0];
8273         }
8274     }
8275     else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8276     {
8277         enum levelOption lvlOpt;
8278         enum charOption chOpt;
8279         unsigned int count;
8280
8281         cData->flags = base64toint(str, 5);
8282         count = strlen(str += 5);
8283         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8284         {
8285             unsigned short lvl;
8286             if(levelOptions[lvlOpt].old_flag)
8287             {
8288                 if(cData->flags & levelOptions[lvlOpt].old_flag)
8289                     lvl = levelOptions[lvlOpt].flag_value;
8290                 else
8291                     lvl = levelOptions[lvlOpt].default_value;
8292             }
8293             else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8294             {
8295             case 'c': lvl = UL_COOWNER; break;
8296             case 'm': lvl = UL_MASTER; break;
8297             case 'n': lvl = UL_OWNER+1; break;
8298             case 'o': lvl = UL_OP; break;
8299             case 'p': lvl = UL_PEON; break;
8300             case 'w': lvl = UL_OWNER; break;
8301             default: lvl = 0; break;
8302             }
8303             cData->lvlOpts[lvlOpt] = lvl;
8304         }
8305         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8306             cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8307     }
8308
8309     if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8310     {
8311         cData->expiry = atoi(str);
8312         if(cData->expiry > 0) {
8313             if(cData->expiry > now) {
8314                 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8315             } else {
8316                 timeq_add(1, chanserv_expire_channel, cData);
8317             }
8318         }
8319     } else {
8320         cData->expiry = 0;
8321     }
8322
8323     if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8324     {
8325         suspended = chanserv_read_suspended(obj);
8326         cData->suspended = suspended;
8327         suspended->cData = cData;
8328         /* We could use suspended->expires and suspended->revoked to
8329          * set the CHANNEL_SUSPENDED flag, but we don't. */
8330     }
8331     else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8332     {
8333         suspended = calloc(1, sizeof(*suspended));
8334         suspended->issued = 0;
8335         suspended->revoked = 0;
8336         suspended->suspender = strdup(str);
8337         str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8338         suspended->expires = str ? atoi(str) : 0;
8339         str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8340         suspended->reason = strdup(str ? str : "No reason");
8341         suspended->previous = NULL;
8342         cData->suspended = suspended;
8343         suspended->cData = cData;
8344     }
8345     else
8346     {
8347         cData->flags &= ~CHANNEL_SUSPENDED;
8348         suspended = NULL; /* to squelch a warning */
8349     }
8350
8351     if(IsSuspended(cData)) {
8352         if(suspended->expires > now)
8353             timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8354         else if(suspended->expires)
8355             cData->flags &= ~CHANNEL_SUSPENDED;
8356     }
8357
8358     if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
8359     {
8360         giveownership = chanserv_read_giveownership(obj);
8361         cData->giveownership = giveownership;
8362     }
8363
8364     if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8365         struct mod_chanmode change;
8366         mod_chanmode_init(&change);
8367         change.argc = 1;
8368         change.args[0].mode = MODE_CHANOP;
8369         change.args[0].u.member = AddChannelUser(chanserv, cNode);
8370         mod_chanmode_announce(chanserv, cNode, &change);
8371     }
8372
8373     str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8374     cData->registered = str ? strtoul(str, NULL, 0) : now;
8375     str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8376     cData->visited = str ? strtoul(str, NULL, 0) : now;
8377     str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8378     cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8379     str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8380     cData->max = str ? atoi(str) : 0;
8381     str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8382     cData->max_time = str ? atoi(str) : 0;
8383     str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8384     cData->greeting = str ? strdup(str) : NULL;
8385     str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8386     cData->user_greeting = str ? strdup(str) : NULL;
8387     str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8388     cData->topic_mask = str ? strdup(str) : NULL;
8389     str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8390     cData->topic = str ? strdup(str) : NULL;
8391
8392     str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8393     if(str) {
8394         cData->vote = str ? strdup(str) : NULL;
8395         dict_delete(cData->vote_options);
8396         cData->vote_options = dict_new();
8397         dict_set_free_data(cData->vote_options, free_vote_options);
8398         str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8399         cData->vote_start = str ? atoi(str) : 0;
8400         obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8401         for(it = dict_first(obj); it; it = iter_next(it)) {
8402             vote_option_read_helper(iter_key(it), iter_data(it), cData);
8403         }
8404     }
8405
8406     obj = database_get_data(channel, KEY_ADVTOPIC_ENTRIES, RECDB_OBJECT);
8407     for(it = dict_first(obj); it; it = iter_next(it))
8408     {
8409         struct record_data *rd = iter_data(it);
8410         if(rd->type != RECDB_QSTRING) continue;
8411         int advtopic_index = atoi(iter_key(it));
8412         if(advtopic_index < 0 || advtopic_index >= MAXADVTOPICENTRIES) continue;
8413         cData->advtopic[advtopic_index] = (rd ? strdup(rd->d.qstring) : NULL);
8414     }
8415
8416     if(!IsSuspended(cData)
8417        && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8418        && (argc = split_line(str, 0, ArrayLength(argv), argv))
8419        && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8420         cData->modes = *modes;
8421         if(off_channel > 0)
8422           cData->modes.modes_set |= MODE_REGISTERED;
8423         if(cData->modes.argc > 1)
8424             cData->modes.argc = 1;
8425         mod_chanmode_announce(chanserv, cNode, &cData->modes);
8426         mod_chanmode_free(modes);
8427     }
8428
8429     obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8430     for(it = dict_first(obj); it; it = iter_next(it))
8431         user_read_helper(iter_key(it), iter_data(it), cData);
8432
8433     if(!cData->users && !IsProtected(cData))
8434     {
8435         log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8436         unregister_channel(cData, "has empty user list.");
8437         return 0;
8438     }
8439
8440     obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8441     for(it = dict_first(obj); it; it = iter_next(it))
8442         ban_read_helper(iter_key(it), iter_data(it), cData);
8443
8444     obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8445     for(it = dict_first(obj); it; it = iter_next(it))
8446     {
8447         struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8448         struct record_data *rd = iter_data(it);
8449         const char *note, *setter;
8450
8451         if(rd->type != RECDB_OBJECT)
8452         {
8453             log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8454         }
8455         else if(!ntype)
8456         {
8457             log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8458         }
8459         else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8460         {
8461             log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8462         }
8463         else
8464         {
8465             setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8466             if(!setter) setter = "<unknown>";
8467             chanserv_add_channel_note(cData, ntype, setter, note);
8468         }
8469     }
8470
8471     return 0;
8472 }
8473
8474 static void
8475 chanserv_dnr_read(const char *key, struct record_data *hir)
8476 {
8477     const char *setter, *reason, *str;
8478     struct do_not_register *dnr;
8479     unsigned long expiry;
8480
8481     setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8482     if(!setter)
8483     {
8484         log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8485         return;
8486     }
8487     reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8488     if(!reason)
8489     {
8490         log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8491         return;
8492     }
8493     str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8494     expiry = str ? strtoul(str, NULL, 0) : 0;
8495     if(expiry && expiry <= now)
8496         return;
8497     dnr = chanserv_add_dnr(key, setter, expiry, reason);
8498     if(!dnr)
8499         return;
8500     str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8501     if(str)
8502         dnr->set = atoi(str);
8503     else
8504         dnr->set = 0;
8505 }
8506
8507 static int
8508 chanserv_saxdb_read(struct dict *database)
8509 {
8510     struct dict *section;
8511     dict_iterator_t it;
8512
8513     if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8514         for(it = dict_first(section); it; it = iter_next(it))
8515             chanserv_note_type_read(iter_key(it), iter_data(it));
8516
8517     if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8518         for(it = dict_first(section); it; it = iter_next(it))
8519             chanserv_channel_read(iter_key(it), iter_data(it));
8520
8521     if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8522         for(it = dict_first(section); it; it = iter_next(it))
8523             chanserv_dnr_read(iter_key(it), iter_data(it));
8524
8525     return 0;
8526 }
8527
8528 static int
8529 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8530 {
8531     int high_present = 0;
8532     saxdb_start_record(ctx, KEY_USERS, 1);
8533     for(; uData; uData = uData->next)
8534     {
8535         if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8536             high_present = 1;
8537         saxdb_start_record(ctx, uData->handle->handle, 0);
8538         saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8539         saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8540         if(uData->flags)
8541             saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8542         if(uData->expires)
8543             saxdb_write_int(ctx, KEY_EXPIRES, uData->expires);
8544         if(uData->channel->vote && uData->voted)
8545             saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8546         if(uData->channel->vote && uData->votefor)
8547             saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8548         if(uData->info)
8549             saxdb_write_string(ctx, KEY_INFO, uData->info);
8550         saxdb_end_record(ctx);
8551     }
8552     saxdb_end_record(ctx);
8553     return high_present;
8554 }
8555
8556 static void
8557 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8558 {
8559     if(!bData)
8560         return;
8561     saxdb_start_record(ctx, KEY_BANS, 1);
8562     for(; bData; bData = bData->next)
8563     {
8564         saxdb_start_record(ctx, bData->mask, 0);
8565         saxdb_write_int(ctx, KEY_SET, bData->set);
8566         if(bData->triggered)
8567             saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8568         if(bData->expires)
8569             saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8570         if(bData->owner[0])
8571             saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8572         if(bData->reason)
8573             saxdb_write_string(ctx, KEY_REASON, bData->reason);
8574         saxdb_end_record(ctx);
8575     }
8576     saxdb_end_record(ctx);
8577 }
8578
8579 static void
8580 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8581 {
8582     saxdb_start_record(ctx, name, 0);
8583     saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8584     saxdb_write_string(ctx, KEY_REASON, susp->reason);
8585     if(susp->issued)
8586         saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8587     if(susp->expires)
8588         saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8589     if(susp->revoked)
8590         saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8591     if(susp->previous)
8592         chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8593     saxdb_end_record(ctx);
8594 }
8595
8596 static void
8597 chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
8598 {
8599     saxdb_start_record(ctx, name, 0);
8600     if(giveownership->staff_issuer)
8601       saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
8602     if(giveownership->old_owner)
8603       saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
8604     if(giveownership->target)
8605       saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
8606     if(giveownership->target_access)
8607       saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
8608     if(giveownership->reason)
8609       saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
8610     if(giveownership->issued)
8611         saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
8612     if(giveownership->previous)
8613         chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
8614     saxdb_end_record(ctx);
8615 }
8616
8617 static void
8618 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8619 {
8620     char buf[MAXLEN];
8621     int high_present;
8622     enum levelOption lvlOpt;
8623     enum charOption chOpt;
8624     dict_iterator_t it;
8625
8626     saxdb_start_record(ctx, channel->channel->name, 1);
8627
8628     saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8629     saxdb_write_int(ctx, KEY_MAX, channel->max);
8630     saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8631     if(channel->topic)
8632         saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8633     if(channel->registrar)
8634         saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8635     if(channel->greeting)
8636         saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8637     if(channel->user_greeting)
8638         saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8639     if(channel->topic_mask)
8640         saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8641     if(channel->suspended)
8642         chanserv_write_suspended(ctx, "suspended", channel->suspended);
8643     if(channel->giveownership)
8644         chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
8645     if(channel->expiry)
8646         saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8647
8648     if(channel->vote) {
8649         saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8650         if(channel->vote_start)
8651             saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8652         if (dict_size(channel->vote_options)) {
8653             saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8654             for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8655                 struct vote_option *vOpt = iter_data(it);
8656                 char str[50];
8657                 sprintf(str,"%i",vOpt->option_id);
8658                 saxdb_start_record(ctx, str, 0);
8659                 if(vOpt->voted)
8660                     saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8661                 if(vOpt->name)
8662                     saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8663                 saxdb_end_record(ctx);
8664             }
8665             saxdb_end_record(ctx);
8666         }
8667     }
8668
8669     saxdb_start_record(ctx, KEY_OPTIONS, 0);
8670     saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8671     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8672         saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8673     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8674     {
8675         buf[0] = channel->chOpts[chOpt];
8676         buf[1] = '\0';
8677         saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8678     }
8679     saxdb_end_record(ctx);
8680
8681     if(channel->modes.modes_set || channel->modes.modes_clear)
8682     {
8683         mod_chanmode_format(&channel->modes, buf);
8684         saxdb_write_string(ctx, KEY_MODES, buf);
8685     }
8686
8687     high_present = chanserv_write_users(ctx, channel->users);
8688     chanserv_write_bans(ctx, channel->bans);
8689
8690     if(channel->flags & CHANNEL_ADVTOPIC) {
8691         saxdb_start_record(ctx, KEY_ADVTOPIC_ENTRIES, 0);
8692         int advtopic_index;
8693         for(advtopic_index = 0; advtopic_index < MAXADVTOPICENTRIES; advtopic_index++) {
8694             if(channel->advtopic[advtopic_index])
8695                 saxdb_write_string(ctx, strtab(advtopic_index), channel->advtopic[advtopic_index]);
8696         }
8697         saxdb_end_record(ctx);
8698     }
8699
8700     if(dict_size(channel->notes))
8701     {
8702         dict_iterator_t it;
8703
8704         saxdb_start_record(ctx, KEY_NOTES, 1);
8705         for(it = dict_first(channel->notes); it; it = iter_next(it))
8706         {
8707             struct note *note = iter_data(it);
8708             saxdb_start_record(ctx, iter_key(it), 0);
8709             saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8710             saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8711             saxdb_end_record(ctx);
8712         }
8713         saxdb_end_record(ctx);
8714     }
8715
8716     if(channel->ownerTransfer)
8717         saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8718     saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8719     saxdb_end_record(ctx);
8720 }
8721
8722 static void
8723 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8724 {
8725     const char *str;
8726
8727     saxdb_start_record(ctx, ntype->name, 0);
8728     switch(ntype->set_access_type)
8729     {
8730     case NOTE_SET_CHANNEL_ACCESS:
8731         saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8732         break;
8733     case NOTE_SET_CHANNEL_SETTER:
8734         saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8735         break;
8736     case NOTE_SET_PRIVILEGED: default:
8737         saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8738         break;
8739     }
8740     switch(ntype->visible_type)
8741     {
8742     case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8743     case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8744     case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8745     }
8746     saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8747     saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8748     saxdb_end_record(ctx);
8749 }
8750
8751 static void
8752 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8753 {
8754     struct do_not_register *dnr;
8755     dict_iterator_t it, next;
8756
8757     for(it = dict_first(dnrs); it; it = next)
8758     {
8759         next = iter_next(it);
8760         dnr = iter_data(it);
8761         if(dnr->expires && dnr->expires <= now)
8762         {
8763             dict_remove(dnrs, iter_key(it));
8764             continue;
8765         }
8766         saxdb_start_record(ctx, dnr->chan_name, 0);
8767         if(dnr->set)
8768             saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8769         if(dnr->expires)
8770             saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8771         saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8772         saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8773         saxdb_end_record(ctx);
8774     }
8775 }
8776
8777 static int
8778 chanserv_saxdb_write(struct saxdb_context *ctx)
8779 {
8780     dict_iterator_t it;
8781     struct chanData *channel;
8782
8783     /* Notes */
8784     saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8785     for(it = dict_first(note_types); it; it = iter_next(it))
8786         chanserv_write_note_type(ctx, iter_data(it));
8787     saxdb_end_record(ctx);
8788
8789     /* DNRs */
8790     saxdb_start_record(ctx, KEY_DNR, 1);
8791     write_dnrs_helper(ctx, handle_dnrs);
8792     write_dnrs_helper(ctx, plain_dnrs);
8793     write_dnrs_helper(ctx, mask_dnrs);
8794     saxdb_end_record(ctx);
8795
8796     /* Channels */
8797     saxdb_start_record(ctx, KEY_CHANNELS, 1);
8798     for(channel = channelList; channel; channel = channel->next)
8799         chanserv_write_channel(ctx, channel);
8800     saxdb_end_record(ctx);
8801
8802     return 0;
8803 }
8804
8805 static void
8806 chanserv_db_cleanup(void) {
8807     unsigned int ii;
8808     unreg_part_func(handle_part);
8809     while(channelList)
8810         unregister_channel(channelList, "terminating.");
8811     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8812         UnlockChannel(chanserv_conf.support_channels.list[ii]);
8813     free(chanserv_conf.support_channels.list);
8814     dict_delete(handle_dnrs);
8815     dict_delete(plain_dnrs);
8816     dict_delete(mask_dnrs);
8817     dict_delete(note_types);
8818     free_string_list(chanserv_conf.eightball);
8819     free_string_list(chanserv_conf.old_ban_names);
8820     free_string_list(chanserv_conf.set_shows);
8821     free(set_shows_list.list);
8822     free(uset_shows_list.list);
8823     while(helperList)
8824     {
8825         struct userData *helper = helperList;
8826         helperList = helperList->next;
8827         free(helper);
8828     }
8829 }
8830
8831 #if defined(GCC_VARMACROS)
8832 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8833 #elif defined(C99_VARMACROS)
8834 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8835 #endif
8836 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8837 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8838
8839 void
8840 init_chanserv(const char *nick)
8841 {
8842     CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8843     conf_register_reload(chanserv_conf_read);
8844
8845     if(nick)
8846     {
8847         reg_server_link_func(handle_server_link);
8848         reg_new_channel_func(handle_new_channel);
8849         reg_join_func(handle_join);
8850         reg_part_func(handle_part);
8851         reg_kick_func(handle_kick);
8852         reg_topic_func(handle_topic);
8853         reg_mode_change_func(handle_mode);
8854         reg_nick_change_func(handle_nick_change);
8855         reg_auth_func(handle_auth);
8856     }
8857
8858     reg_handle_rename_func(handle_rename);
8859     reg_unreg_func(handle_unreg);
8860
8861     handle_dnrs = dict_new();
8862     dict_set_free_data(handle_dnrs, free);
8863     plain_dnrs = dict_new();
8864     dict_set_free_data(plain_dnrs, free);
8865     mask_dnrs = dict_new();
8866     dict_set_free_data(mask_dnrs, free);
8867
8868     reg_svccmd_unbind_func(handle_svccmd_unbind);
8869     chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8870     DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8871     DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8872     DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8873     DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8874     modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8875     modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8876     modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8877     DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8878     DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8879     DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8880     DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8881     DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8882
8883     DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8884     DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8885
8886     DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8887     DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8888     DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8889     DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8890     DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8891
8892     DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8893     DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8894     DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8895     DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8896     DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8897
8898     DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8899     DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8900     DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8901     DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8902
8903     DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8904     DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8905     DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8906     DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8907     DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8908     DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8909     DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8910     DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8911
8912     DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8913     DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8914     DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8915     DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8916     DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8917     DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8918     DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8919     DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8920     DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8921     DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8922     DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8923     DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8924     DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8925     DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8926     DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8927
8928     DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8929     DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8930     DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8931     DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8932     DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8933
8934     DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8935     DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8936
8937     DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8938     DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8939     DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8940     DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8941     DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8942     DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8943     DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8944     DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8945     DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8946     DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8947     DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8948
8949     DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8950     DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8951
8952     DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8953     DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8954     DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8955     DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8956
8957     DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8958     DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8959     DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8960     DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8961     DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8962
8963     DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8964     DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8965     DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8966     DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8967     DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8968     DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8969     
8970     DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8971     DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8972     DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8973     DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8974     DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8975     DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8976     DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8977     DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8978
8979     DEFINE_COMMAND(opme, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
8980     
8981     /* Channel options */
8982     DEFINE_CHANNEL_OPTION(defaulttopic);
8983     DEFINE_CHANNEL_OPTION(topicmask);
8984     DEFINE_CHANNEL_OPTION(greeting);
8985     DEFINE_CHANNEL_OPTION(usergreeting);
8986     DEFINE_CHANNEL_OPTION(modes);
8987     DEFINE_CHANNEL_OPTION(enfops);
8988     DEFINE_CHANNEL_OPTION(giveops);
8989     DEFINE_CHANNEL_OPTION(protect);
8990     DEFINE_CHANNEL_OPTION(enfmodes);
8991     DEFINE_CHANNEL_OPTION(enftopic);
8992     DEFINE_CHANNEL_OPTION(pubcmd);
8993     DEFINE_CHANNEL_OPTION(givevoice);
8994     DEFINE_CHANNEL_OPTION(userinfo);
8995     DEFINE_CHANNEL_OPTION(dynlimit);
8996     DEFINE_CHANNEL_OPTION(topicsnarf);
8997     DEFINE_CHANNEL_OPTION(vote);
8998     DEFINE_CHANNEL_OPTION(nodelete);
8999     DEFINE_CHANNEL_OPTION(toys);
9000     DEFINE_CHANNEL_OPTION(setters);
9001     DEFINE_CHANNEL_OPTION(topicrefresh);
9002     DEFINE_CHANNEL_OPTION(ctcpusers);
9003     DEFINE_CHANNEL_OPTION(ctcpreaction);
9004     DEFINE_CHANNEL_OPTION(inviteme);
9005     DEFINE_CHANNEL_OPTION(advtopic);
9006     DEFINE_CHANNEL_OPTION(unreviewed);
9007     modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
9008     modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
9009     modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
9010     if(off_channel > 1)
9011         DEFINE_CHANNEL_OPTION(offchannel);
9012     modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
9013
9014     /* Alias set topic to set defaulttopic for compatibility. */
9015     modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
9016
9017     /* User options */
9018     DEFINE_USER_OPTION(noautoop);
9019     DEFINE_USER_OPTION(autoinvite);
9020     DEFINE_USER_OPTION(info);
9021
9022     /* Alias uset autovoice to uset autoop. */
9023     modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
9024
9025     note_types = dict_new();
9026     dict_set_free_data(note_types, chanserv_deref_note_type);
9027     if(nick)
9028     {
9029         const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
9030         chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
9031         service_register(chanserv)->trigger = '!';
9032         reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
9033     }
9034     saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
9035
9036     if(chanserv_conf.channel_expire_frequency)
9037         timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
9038
9039     if(chanserv_conf.dnr_expire_frequency)
9040         timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
9041
9042     if(chanserv_conf.refresh_period)
9043     {
9044         unsigned long next_refresh;
9045         next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
9046         timeq_add(next_refresh, chanserv_refresh_topics, NULL);
9047     }
9048
9049     reg_exit_func(chanserv_db_cleanup);
9050     message_register_table(msgtab);
9051 }