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