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