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