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