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