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