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