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