don't use empty new_channel_unauthed_join, new_channel_authed_join or new_channel_mes...
[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(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 && !data)
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 = GetChannelUser(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         unsigned int base_len;
3889
3890         if(uData->access > UL_OWNER)
3891             continue;
3892         if(uData->access == UL_OWNER)
3893             ocount++;
3894
3895         if(IsProtected(cData)
3896            && (target_handle != user->handle_info)
3897            && !GetTrueChannelAccess(cData, user->handle_info)
3898            && !IsNetworkHelper(user))
3899             continue;
3900         sbuf.used = 0;
3901         string_buffer_append_printf(&sbuf, "[%s (%d,", cData->channel->name, uData->access);
3902         base_len = sbuf.used;
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(sbuf.used==base_len)
3915             sbuf.used--;
3916         if(uData->info)
3917             string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3918         else
3919             string_buffer_append_string(&sbuf, ")]");
3920         string_buffer_append(&sbuf, '\0');
3921         send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3922     }
3923
3924     if(ccount == 1) {
3925         reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount, ocount);
3926     } else {
3927         reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount, ocount);
3928     }
3929
3930     return 1;
3931 }
3932
3933 static CHANSERV_FUNC(cmd_access)
3934 {
3935     struct userNode *target;
3936     struct handle_info *target_handle;
3937     struct userData *uData;
3938     int helping;
3939     char prefix[MAXLEN];
3940
3941     if(argc < 2)
3942     {
3943         target = user;
3944         target_handle = target->handle_info;
3945     }
3946     else if((target = GetUserH(argv[1])))
3947     {
3948         target_handle = target->handle_info;
3949     }
3950     else if(argv[1][0] == '*')
3951     {
3952         if(!(target_handle = get_handle_info(argv[1]+1)))
3953         {
3954             reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3955             return 0;
3956         }
3957     }
3958     else
3959     {
3960         reply("MSG_NICK_UNKNOWN", argv[1]);
3961         return 0;
3962     }
3963
3964     assert(target || target_handle);
3965
3966     if(target == chanserv)
3967     {
3968         reply("CSMSG_IS_CHANSERV");
3969         return 1;
3970     }
3971
3972     if(!target_handle)
3973     {
3974         if(IsOper(target))
3975         {
3976             reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3977             return 0;
3978         }
3979         if(target != user)
3980         {
3981             reply("MSG_USER_AUTHENTICATE", target->nick);
3982             return 0;
3983         }
3984         reply("MSG_AUTHENTICATE");
3985         return 0;
3986     }
3987
3988     if(target)
3989     {
3990         const char *epithet = NULL, *type = NULL;
3991         if(IsOper(target))
3992         {
3993             epithet = chanserv_conf.irc_operator_epithet;
3994             type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3995         }
3996         else if(IsNetworkHelper(target))
3997         {
3998             epithet = chanserv_conf.network_helper_epithet;
3999             type = user_find_message(user, "CSMSG_UC_H_TITLE");
4000         }
4001         else if(IsSupportHelper(target))
4002         {
4003             epithet = chanserv_conf.support_helper_epithet;
4004             type = user_find_message(user, "CSMSG_LC_H_TITLE");
4005         }
4006         if(epithet)
4007         {
4008             if(target_handle->epithet)
4009                 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
4010             else if(epithet)
4011                 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
4012         }
4013         sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
4014     }
4015     else
4016     {
4017         sprintf(prefix, "%s", target_handle->handle);
4018     }
4019
4020     if(!channel->channel_info)
4021     {
4022         reply("CSMSG_NOT_REGISTERED", channel->name);
4023         return 1;
4024     }
4025
4026     helping = HANDLE_FLAGGED(target_handle, HELPING)
4027         && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
4028     if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
4029     {
4030         reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
4031         /* To prevent possible information leaks, only show infolines
4032          * if the requestor is in the channel or it's their own
4033          * handle. */
4034         if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
4035         {
4036             send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
4037         }
4038         /* Likewise, only say it's suspended if the user has active
4039          * access in that channel or it's their own entry. */
4040         if(IsUserSuspended(uData)
4041            && (GetChannelUser(channel->channel_info, user->handle_info)
4042                || (user->handle_info == uData->handle)))
4043         {
4044             reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4045         }
4046     }
4047     else
4048     {
4049         reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4050     }
4051
4052     return 1;
4053 }
4054
4055 static void
4056 def_list(struct listData *list)
4057 {
4058     const char *msg;
4059     if(list->search)
4060         send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4061     else
4062         send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4063     table_send(list->bot, list->user->nick, 0, NULL, list->table);
4064     if(list->table.length == 1)
4065     {
4066         msg = user_find_message(list->user, "MSG_NONE");
4067         send_message_type(4, list->user, list->bot, "  %s", msg);
4068     }
4069 }
4070
4071 static int
4072 userData_access_comp(const void *arg_a, const void *arg_b)
4073 {
4074     const struct userData *a = *(struct userData**)arg_a;
4075     const struct userData *b = *(struct userData**)arg_b;
4076     int res;
4077     if(a->access != b->access)
4078         res = b->access - a->access;
4079     else
4080         res = irccasecmp(a->handle->handle, b->handle->handle);
4081     return res;
4082 }
4083
4084 static int
4085 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4086 {
4087     void (*send_list)(struct listData *);
4088     struct userData *uData;
4089     struct listData lData;
4090     unsigned int matches;
4091     const char **ary;
4092
4093     lData.user = user;
4094     lData.bot = cmd->parent->bot;
4095     lData.channel = channel;
4096     lData.lowest = lowest;
4097     lData.highest = highest;
4098     lData.search = (argc > 1) ? argv[1] : NULL;
4099     send_list = def_list;
4100
4101     if(user->handle_info)
4102     {
4103         switch(user->handle_info->userlist_style)
4104         {
4105         case HI_STYLE_DEF: send_list = def_list; break;
4106         case HI_STYLE_ZOOT: send_list = def_list; break;
4107         }
4108     }
4109
4110     lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4111     matches = 0;
4112     for(uData = channel->channel_info->users; uData; uData = uData->next)
4113     {
4114         if((uData->access < lowest)
4115            || (uData->access > highest)
4116            || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4117             continue;
4118         lData.users[matches++] = uData;
4119     }
4120     qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4121
4122     lData.table.length = matches+1;
4123     lData.table.width = 4;
4124     lData.table.flags = TABLE_NO_FREE;
4125     lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4126     ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4127     lData.table.contents[0] = ary;
4128     ary[0] = "Access";
4129     ary[1] = "Account";
4130     ary[2] = "Last Seen";
4131     ary[3] = "Status";
4132     for(matches = 1; matches < lData.table.length; ++matches)
4133     {
4134         char seen[INTERVALLEN];
4135
4136         uData = lData.users[matches-1];
4137         ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4138         lData.table.contents[matches] = ary;
4139         ary[0] = strtab(uData->access);
4140         ary[1] = uData->handle->handle;
4141         if(uData->present)
4142             ary[2] = "Here";
4143         else if(!uData->seen)
4144             ary[2] = "Never";
4145         else
4146             ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4147         ary[2] = strdup(ary[2]);
4148         if(IsUserSuspended(uData))
4149             ary[3] = "Suspended";
4150         else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4151             ary[3] = "Vacation";
4152         else if(HANDLE_FLAGGED(uData->handle, BOT))
4153             ary[3] = "Bot";
4154         else
4155             ary[3] = "Normal";
4156     }
4157     send_list(&lData);
4158     for(matches = 1; matches < lData.table.length; ++matches)
4159     {
4160         free((char*)lData.table.contents[matches][2]);
4161         free(lData.table.contents[matches]);
4162     }
4163     free(lData.table.contents[0]);
4164     free(lData.table.contents);
4165     reply("CSMSG_TOTAL_USERS",(lData.table.length - 1),channel->name);
4166     return 1;
4167 }
4168
4169 static CHANSERV_FUNC(cmd_users)
4170 {
4171     return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4172 }
4173
4174 static CHANSERV_FUNC(cmd_wlist)
4175 {
4176     return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4177 }
4178
4179 static CHANSERV_FUNC(cmd_clist)
4180 {
4181     return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4182 }
4183
4184 static CHANSERV_FUNC(cmd_mlist)
4185 {
4186     return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4187 }
4188
4189 static CHANSERV_FUNC(cmd_olist)
4190 {
4191     return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4192 }
4193
4194 static CHANSERV_FUNC(cmd_plist)
4195 {
4196     return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4197 }
4198
4199 static CHANSERV_FUNC(cmd_bans)
4200 {
4201     struct userNode *search_u = NULL;
4202     struct helpfile_table tbl;
4203     unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4204     char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4205     const char *msg_never, *triggered, *expires;
4206     struct banData *ban, **bans;
4207
4208     if(argc < 2)
4209         search = NULL;
4210     else if(strchr(search = argv[1], '!'))
4211     {
4212         search = argv[1];
4213         search_wilds = search[strcspn(search, "?*")];
4214     }
4215     else if(!(search_u = GetUserH(search)))
4216         reply("MSG_NICK_UNKNOWN", search);
4217
4218     bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4219
4220     for(ban = channel->channel_info->bans; ban; ban = ban->next)
4221     {
4222         if(search_u)
4223         {
4224             if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4225                 continue;
4226         }
4227         else if(search)
4228         {
4229             if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4230                 continue;
4231         }
4232         bans[matches++] = ban;
4233         if(ban->expires)
4234             timed = 1;
4235     }
4236
4237     tbl.length = matches + 1;
4238     tbl.width = 4 + timed;
4239     tbl.flags = 0;
4240     tbl.flags = TABLE_NO_FREE;
4241     tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4242     tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4243     tbl.contents[0][0] = "Mask";
4244     tbl.contents[0][1] = "Set By";
4245     tbl.contents[0][2] = "Triggered";
4246     if(timed)
4247     {
4248         tbl.contents[0][3] = "Expires";
4249         tbl.contents[0][4] = "Reason";
4250     }
4251     else
4252         tbl.contents[0][3] = "Reason";
4253     if(!matches)
4254     {
4255         table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4256         reply("MSG_NONE");
4257         free(tbl.contents[0]);
4258         free(tbl.contents);
4259         return 0;
4260     }
4261
4262     msg_never = user_find_message(user, "MSG_NEVER");
4263     for(ii = 0; ii < matches; )
4264     {
4265         ban = bans[ii];
4266
4267         if(!timed)
4268             expires = "";
4269         else if(ban->expires)
4270             expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4271         else
4272             expires = msg_never;
4273
4274         if(ban->triggered)
4275             triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4276         else
4277             triggered = msg_never;
4278
4279         tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4280         tbl.contents[ii][0] = ban->mask;
4281         tbl.contents[ii][1] = ban->owner;
4282         tbl.contents[ii][2] = strdup(triggered);
4283         if(timed)
4284         {
4285             tbl.contents[ii][3] = strdup(expires);
4286             tbl.contents[ii][4] = ban->reason;
4287         }
4288         else
4289             tbl.contents[ii][3] = ban->reason;
4290     }
4291     table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4292     reply("MSG_MATCH_COUNT", matches);
4293     for(ii = 1; ii < tbl.length; ++ii)
4294     {
4295         free((char*)tbl.contents[ii][2]);
4296         if(timed)
4297             free((char*)tbl.contents[ii][3]);
4298         free(tbl.contents[ii]);
4299     }
4300     free(tbl.contents[0]);
4301     free(tbl.contents);
4302     return 1;
4303 }
4304
4305 static int
4306 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4307 {
4308     struct chanData *cData = channel->channel_info;
4309     if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4310         return 0;
4311     if(cData->topic_mask)
4312         return !match_ircglob(new_topic, cData->topic_mask);
4313     else if(cData->topic)
4314         return irccasecmp(new_topic, cData->topic);
4315     else
4316         return 0;
4317 }
4318
4319 static CHANSERV_FUNC(cmd_topic)
4320 {
4321     struct chanData *cData;
4322     char *topic;
4323
4324     cData = channel->channel_info;
4325     if(argc < 2)
4326     {
4327         if(cData->topic)
4328         {
4329             SetChannelTopic(channel, chanserv, cData->topic, 1);
4330             reply("CSMSG_TOPIC_SET", cData->topic);
4331             return 1;
4332         }
4333
4334         reply("CSMSG_NO_TOPIC", channel->name);
4335         return 0;
4336     }
4337
4338     topic = unsplit_string(argv + 1, argc - 1, NULL);
4339     /* If they say "!topic *", use an empty topic. */
4340     if((topic[0] == '*') && (topic[1] == 0))
4341         topic[0] = 0;
4342     if(bad_topic(channel, user, topic))
4343     {
4344         char *topic_mask = cData->topic_mask;
4345         if(topic_mask)
4346         {
4347             char new_topic[TOPICLEN+1], tchar;
4348             int pos=0, starpos=-1, dpos=0, len;
4349
4350             while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4351             {
4352                 switch(tchar)
4353                 {
4354                 case '*':
4355                     if(starpos != -1)
4356                         goto bad_mask;
4357                     len = strlen(topic);
4358                     if((dpos + len) > TOPICLEN)
4359                         len = TOPICLEN + 1 - dpos;
4360                     memcpy(new_topic+dpos, topic, len);
4361                     dpos += len;
4362                     starpos = pos;
4363                     break;
4364                 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4365                 default: new_topic[dpos++] = tchar; break;
4366                 }
4367             }
4368             if((dpos > TOPICLEN) || tchar)
4369             {
4370             bad_mask:
4371                 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4372                 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4373                 return 0;
4374             }
4375             new_topic[dpos] = 0;
4376             SetChannelTopic(channel, chanserv, new_topic, 1);
4377         } else {
4378             reply("CSMSG_TOPIC_LOCKED", channel->name);
4379             return 0;
4380         }
4381     }
4382     else
4383         SetChannelTopic(channel, chanserv, topic, 1);
4384
4385     if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4386     {
4387         /* Grab the topic and save it as the default topic. */
4388         free(cData->topic);
4389         cData->topic = strdup(channel->topic);
4390     }
4391
4392     return 1;
4393 }
4394
4395 static CHANSERV_FUNC(cmd_mode)
4396 {
4397     struct userData *uData;
4398     struct mod_chanmode *change;
4399     short base_oplevel;
4400     char fmt[MAXLEN];
4401
4402     if(argc < 2)
4403     {
4404         change = &channel->channel_info->modes;
4405         if(change->modes_set || change->modes_clear) {
4406             modcmd_chanmode_announce(change);
4407             reply("CSMSG_DEFAULTED_MODES", channel->name);
4408         } else
4409             reply("CSMSG_NO_MODES", channel->name);
4410         return 1;
4411     }
4412
4413     uData = GetChannelUser(channel->channel_info, user->handle_info);
4414     if (!uData)
4415         base_oplevel = MAXOPLEVEL;
4416     else if (uData->access >= UL_OWNER)
4417         base_oplevel = 1;
4418     else
4419         base_oplevel = 1 + UL_OWNER - uData->access;
4420     change = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4421     if(!change)
4422     {
4423         reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4424         return 0;
4425     }
4426
4427     if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4428        && mode_lock_violated(&channel->channel_info->modes, change))
4429     {
4430         char modes[MAXLEN];
4431         mod_chanmode_format(&channel->channel_info->modes, modes);
4432         reply("CSMSG_MODE_LOCKED", modes, channel->name);
4433         return 0;
4434     }
4435
4436     modcmd_chanmode_announce(change);
4437     mod_chanmode_format(change, fmt);
4438     mod_chanmode_free(change);
4439     reply("CSMSG_MODES_SET", fmt);
4440     return 1;
4441 }
4442
4443 static void
4444 chanserv_del_invite_mark(void *data)
4445 {
4446         struct ChanUser *chanuser = data;
4447         struct chanNode *channel = chanuser->chan;
4448         unsigned int i;
4449         if(!channel) return;
4450         for(i = 0; i < channel->invited.used; i++)
4451     {
4452         if(channel->invited.list[i] == chanuser->user) {
4453                         userList_remove(&channel->invited, chanuser->user);
4454                 }
4455         }
4456         free(chanuser);
4457 }
4458
4459 static CHANSERV_FUNC(cmd_invite)
4460 {
4461     struct userNode *invite;
4462     struct ChanUser *chanuser;
4463     unsigned int i;
4464
4465     if(argc > 1)
4466     {
4467         if(!(invite = GetUserH(argv[1])))
4468         {
4469             reply("MSG_NICK_UNKNOWN", argv[1]);
4470             return 0;
4471         }
4472     }
4473     else
4474         invite = user;
4475
4476     if(GetUserMode(channel, invite))
4477     {
4478         reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4479         return 0;
4480     }
4481     
4482     for(i = 0; i < channel->invited.used; i++)
4483     {
4484         if(channel->invited.list[i] == invite) {
4485             reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4486             return 0;
4487         }
4488     }
4489
4490     if(user != invite)
4491     {
4492         if(argc > 2)
4493         {
4494             char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4495             send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4496         }
4497         else
4498             send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4499     }
4500     irc_invite(chanserv, invite, channel);
4501     if(argc > 1)
4502         reply("CSMSG_INVITED_USER", argv[1], channel->name);
4503
4504     userList_append(&channel->invited, invite);
4505     chanuser = calloc(1, sizeof(*chanuser));
4506     chanuser->user=invite;
4507     chanuser->chan=channel;
4508     timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4509
4510     return 1;
4511 }
4512
4513 static CHANSERV_FUNC(cmd_inviteme)
4514 {
4515     if(GetUserMode(channel, user))
4516     {
4517         reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4518         return 0;
4519     }
4520     if(channel->channel_info
4521        && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4522     {
4523         reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4524         return 0;
4525     }
4526     irc_invite(cmd->parent->bot, user, channel);
4527     return 1;
4528 }
4529
4530 static CHANSERV_FUNC(cmd_invitemeall)
4531 {
4532     struct handle_info *target = user->handle_info;
4533     struct userData *uData;
4534
4535     if(!target->channels)
4536     {
4537         reply("CSMSG_SQUAT_ACCESS", target->handle);
4538         return 1;
4539     }
4540         
4541     for(uData = target->channels; uData; uData = uData->u_next)
4542     {
4543         struct chanData *cData = uData->channel;
4544         if(uData->access >= cData->lvlOpts[lvlInviteMe])
4545                 {
4546             irc_invite(cmd->parent->bot, user, cData->channel);
4547         }
4548     }
4549     return 1;
4550 }
4551
4552 static void
4553 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4554 {
4555     unsigned int combo;
4556     char buf1[INTERVALLEN], buf2[INTERVALLEN];
4557
4558     /* We display things based on two dimensions:
4559      * - Issue time: present or absent
4560      * - Expiration: revoked, expired, expires in future, or indefinite expiration
4561      * (in order of precedence, so something both expired and revoked
4562      * only counts as revoked)
4563      */
4564     combo = (suspended->issued ? 4 : 0)
4565         + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4566     switch(combo) {
4567     case 0: /* no issue time, indefinite expiration */
4568         reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4569         break;
4570     case 1: /* no issue time, expires in future */
4571         intervalString(buf1, suspended->expires-now, user->handle_info);
4572         reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4573         break;
4574     case 2: /* no issue time, expired */
4575         intervalString(buf1, now-suspended->expires, user->handle_info);
4576         reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4577         break;
4578     case 3: /* no issue time, revoked */
4579         intervalString(buf1, now-suspended->revoked, user->handle_info);
4580         reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4581         break;
4582     case 4: /* issue time set, indefinite expiration */
4583         intervalString(buf1, now-suspended->issued, user->handle_info);
4584         reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4585         break;
4586     case 5: /* issue time set, expires in future */
4587         intervalString(buf1, now-suspended->issued, user->handle_info);
4588         intervalString(buf2, suspended->expires-now, user->handle_info);
4589         reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4590         break;
4591     case 6: /* issue time set, expired */
4592         intervalString(buf1, now-suspended->issued, user->handle_info);
4593         intervalString(buf2, now-suspended->expires, user->handle_info);
4594         reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4595         break;
4596     case 7: /* issue time set, revoked */
4597         intervalString(buf1, now-suspended->issued, user->handle_info);
4598         intervalString(buf2, now-suspended->revoked, user->handle_info);
4599         reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4600         break;
4601     default:
4602         log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4603         return;
4604     }
4605 }
4606
4607 static CHANSERV_FUNC(cmd_info)
4608 {
4609     char modes[MAXLEN], buffer[INTERVALLEN];
4610     struct userData *uData, *owner;
4611     struct chanData *cData;
4612     struct do_not_register *dnr;
4613     struct note *note;
4614     dict_iterator_t it;
4615     int privileged;
4616
4617     cData = channel->channel_info;
4618     reply("CSMSG_CHANNEL_INFO", channel->name);
4619
4620     uData = GetChannelUser(cData, user->handle_info);
4621     if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4622     {
4623         mod_chanmode_format(&cData->modes, modes);
4624         reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4625         reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4626     }
4627
4628     for(it = dict_first(cData->notes); it; it = iter_next(it))
4629     {
4630         int padding;
4631
4632         note = iter_data(it);
4633         if(!note_type_visible_to_user(cData, note->type, user))
4634             continue;
4635
4636         padding = PADLEN - 1 - strlen(iter_key(it));
4637         reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4638     }
4639
4640     if(cData->max_time) {
4641         reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4642     } else {
4643         reply("CSMSG_CHANNEL_MAX", cData->max);
4644     }
4645     for(owner = cData->users; owner; owner = owner->next)
4646         if(owner->access == UL_OWNER)
4647             reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4648     reply("CSMSG_CHANNEL_USERS", cData->userCount);
4649     reply("CSMSG_CHANNEL_BANS", cData->banCount);
4650     reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4651
4652     privileged = IsStaff(user);
4653     if(privileged)
4654         reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4655     if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4656         reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4657
4658     if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4659         chanserv_show_dnrs(user, cmd, channel->name, NULL);
4660
4661     if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4662     {
4663         struct suspended *suspended;
4664         reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4665         for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4666             show_suspension_info(cmd, user, suspended);
4667     }
4668     else if(IsSuspended(cData))
4669     {
4670         reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4671         show_suspension_info(cmd, user, cData->suspended);
4672     }
4673     return 1;
4674 }
4675
4676 static CHANSERV_FUNC(cmd_netinfo)
4677 {
4678     extern unsigned long boot_time;
4679     extern unsigned long burst_length;
4680     char interval[INTERVALLEN];
4681
4682     reply("CSMSG_NETWORK_INFO");
4683     reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4684     reply("CSMSG_NETWORK_USERS", dict_size(clients));
4685     reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4686     reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4687     reply("CSMSG_NETWORK_BANS", banCount);
4688     reply("CSMSG_NETWORK_CHANUSERS", userCount);
4689     reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4690     reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4691     return 1;
4692 }
4693
4694 static void
4695 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4696 {
4697     struct helpfile_table table;
4698     unsigned int nn;
4699     struct userNode *user;
4700     char *nick;
4701
4702     table.length = 0;
4703     table.width = 1;
4704     table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4705     table.contents = alloca(list->used*sizeof(*table.contents));
4706     for(nn=0; nn<list->used; nn++)
4707     {
4708         user = list->list[nn];
4709         if(user->modes & skip_flags)
4710             continue;
4711         if(IsBot(user))
4712             continue;
4713                 if(IsInvi(user))
4714                     continue;
4715         table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4716         if(IsAway(user))
4717         {
4718             nick = alloca(strlen(user->nick)+3);
4719             sprintf(nick, "(%s)", user->nick);
4720         }
4721         else
4722             nick = user->nick;
4723         table.contents[table.length][0] = nick;
4724         table.length++;
4725     }
4726     table_send(chanserv, to->nick, 0, NULL, table);
4727 }
4728
4729 static CHANSERV_FUNC(cmd_ircops)
4730 {
4731     reply("CSMSG_STAFF_OPERS");
4732     send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4733     return 1;
4734 }
4735
4736 static CHANSERV_FUNC(cmd_helpers)
4737 {
4738     reply("CSMSG_STAFF_HELPERS");
4739     send_staff_list(user, &curr_helpers, FLAGS_OPER);
4740     return 1;
4741 }
4742
4743 static CHANSERV_FUNC(cmd_staff)
4744 {
4745     reply("CSMSG_NETWORK_STAFF");
4746     cmd_ircops(CSFUNC_ARGS);
4747     cmd_helpers(CSFUNC_ARGS);
4748     return 1;
4749 }
4750
4751 static CHANSERV_FUNC(cmd_peek)
4752 {
4753     struct modeNode *mn;
4754     char modes[MODELEN];
4755     unsigned int n;
4756     struct helpfile_table table;
4757     int opcount = 0, voicecount = 0, srvcount = 0;
4758
4759     irc_make_chanmode(channel, modes);
4760
4761     reply("CSMSG_PEEK_INFO", channel->name);
4762     reply("CSMSG_PEEK_TOPIC", channel->topic);
4763     reply("CSMSG_PEEK_MODES", modes);
4764
4765     table.length = 0;
4766     table.width = 1;
4767     table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4768     table.contents = alloca(channel->members.used*sizeof(*table.contents));
4769     for(n = 0; n < channel->members.used; n++)
4770     {
4771         mn = channel->members.list[n];
4772         if(IsLocal(mn->user))
4773             srvcount++;
4774         else if(mn->modes & MODE_CHANOP)
4775             opcount++;
4776         else if(mn->modes & MODE_VOICE)
4777             voicecount++;
4778
4779         if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4780             continue;
4781         table.contents[table.length] = alloca(sizeof(**table.contents));
4782         table.contents[table.length][0] = mn->user->nick;
4783         table.length++;
4784     }
4785
4786     reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4787           (channel->members.used - opcount - voicecount - srvcount));
4788
4789     if(table.length)
4790     {
4791         reply("CSMSG_PEEK_OPS");
4792         table_send(chanserv, user->nick, 0, NULL, table);
4793     }
4794     else
4795         reply("CSMSG_PEEK_NO_OPS");
4796     return 1;
4797 }
4798
4799 static MODCMD_FUNC(cmd_wipeinfo)
4800 {
4801     struct handle_info *victim;
4802     struct userData *ud, *actor, *real_actor;
4803     unsigned int override = 0;
4804
4805     REQUIRE_PARAMS(2);
4806     actor = GetChannelUser(channel->channel_info, user->handle_info);
4807     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4808     if(!(victim = modcmd_get_handle_info(user, argv[1])))
4809         return 0;
4810     if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4811     {
4812         reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4813         return 0;
4814     }
4815     if((ud->access >= actor->access) && (ud != actor))
4816     {
4817         reply("MSG_USER_OUTRANKED", victim->handle);
4818         return 0;
4819     }
4820     if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4821         override = CMD_LOG_OVERRIDE;
4822     if(ud->info)
4823         free(ud->info);
4824     ud->info = NULL;
4825     reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4826     return 1 | override;
4827 }
4828
4829 static CHANSERV_FUNC(cmd_resync)
4830 {
4831     struct mod_chanmode *changes;
4832     struct chanData *cData = channel->channel_info;
4833     unsigned int ii, used;
4834
4835     changes = mod_chanmode_alloc(channel->members.used * 2);
4836     for(ii = used = 0; ii < channel->members.used; ++ii)
4837     {
4838         struct modeNode *mn = channel->members.list[ii];
4839         struct userData *uData;
4840
4841         if(IsService(mn->user))
4842             continue;
4843
4844         uData = GetChannelAccess(cData, mn->user->handle_info);
4845         if(!cData->lvlOpts[lvlGiveOps]
4846            || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4847         {
4848             if(!(mn->modes & MODE_CHANOP))
4849             {
4850                 if(!uData || IsUserAutoOp(uData)) 
4851                 {
4852                     changes->args[used].mode = MODE_CHANOP;
4853                     changes->args[used++].u.member = mn;
4854                     if(!(mn->modes & MODE_VOICE))
4855                     {
4856                         changes->args[used].mode = MODE_VOICE;
4857                         changes->args[used++].u.member = mn;
4858                     }
4859                 }
4860             }
4861         }
4862         else if(!cData->lvlOpts[lvlGiveVoice]
4863                 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4864         {
4865             if(mn->modes & MODE_CHANOP)
4866             {
4867                 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4868                 changes->args[used++].u.member = mn;
4869             }
4870             if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
4871             {
4872                 changes->args[used].mode = MODE_VOICE;
4873                 changes->args[used++].u.member = mn;
4874             }
4875         }
4876         else
4877         {
4878             if(mn->modes)
4879             {
4880                 changes->args[used].mode = MODE_REMOVE | mn->modes;
4881                 changes->args[used++].u.member = mn;
4882             }
4883         }
4884     }
4885     changes->argc = used;
4886     modcmd_chanmode_announce(changes);
4887     mod_chanmode_free(changes);
4888     reply("CSMSG_RESYNCED_USERS", channel->name);
4889     return 1;
4890 }
4891
4892 static CHANSERV_FUNC(cmd_seen)
4893 {
4894     struct userData *uData;
4895     struct handle_info *handle;
4896     char seen[INTERVALLEN];
4897
4898     REQUIRE_PARAMS(2);
4899
4900     if(!irccasecmp(argv[1], chanserv->nick))
4901     {
4902         reply("CSMSG_IS_CHANSERV");
4903         return 1;
4904     }
4905
4906     if(!(handle = get_handle_info(argv[1])))
4907     {
4908         reply("MSG_HANDLE_UNKNOWN", argv[1]);
4909         return 0;
4910     }
4911
4912     if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4913     {
4914         reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4915         return 0;
4916     }
4917
4918     if(uData->present)
4919         reply("CSMSG_USER_PRESENT", handle->handle);
4920     else if(uData->seen)
4921         reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4922     else
4923         reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4924
4925     if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4926         reply("CSMSG_USER_VACATION", handle->handle);
4927
4928     return 1;
4929 }
4930
4931 static MODCMD_FUNC(cmd_names)
4932 {
4933     struct userNode *targ;
4934     struct userData *targData;
4935     unsigned int ii, pos;
4936     char buf[400];
4937
4938     for(ii=pos=0; ii<channel->members.used; ++ii)
4939     {
4940         targ = channel->members.list[ii]->user;
4941         targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4942         if(!targData)
4943             continue;
4944         if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4945         {
4946             buf[pos] = 0;
4947             reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4948             pos = 0;
4949         }
4950         buf[pos++] = ' ';
4951         if(IsUserSuspended(targData))
4952             buf[pos++] = 's';
4953         pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4954     }
4955     buf[pos] = 0;
4956     reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4957     reply("CSMSG_END_NAMES", channel->name);
4958     return 1;
4959 }
4960
4961 static int
4962 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4963 {
4964     switch(ntype->visible_type)
4965     {
4966     case NOTE_VIS_ALL: return 1;
4967     case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4968     case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4969     }
4970 }
4971
4972 static int
4973 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4974 {
4975     struct userData *uData;
4976
4977     switch(ntype->set_access_type)
4978     {
4979     case NOTE_SET_CHANNEL_ACCESS:
4980         if(!user->handle_info)
4981             return 0;
4982         if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4983             return 0;
4984         return uData->access >= ntype->set_access.min_ulevel;
4985     case NOTE_SET_CHANNEL_SETTER:
4986         return check_user_level(channel, user, lvlSetters, 1, 0);
4987     case NOTE_SET_PRIVILEGED: default:
4988         return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4989     }
4990 }
4991
4992 static CHANSERV_FUNC(cmd_note)
4993 {
4994     struct chanData *cData;
4995     struct note *note;
4996     struct note_type *ntype;
4997
4998     cData = channel->channel_info;
4999     if(!cData)
5000     {
5001         reply("CSMSG_NOT_REGISTERED", channel->name);
5002         return 0;
5003     }
5004
5005     /* If no arguments, show all visible notes for the channel. */
5006     if(argc < 2)
5007     {
5008         dict_iterator_t it;
5009         unsigned int count;
5010
5011         for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5012         {
5013             note = iter_data(it);
5014             if(!note_type_visible_to_user(cData, note->type, user))
5015                 continue;
5016             if(!count++)
5017                 reply("CSMSG_NOTELIST_HEADER", channel->name);
5018             reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5019         }
5020         if(count)
5021             reply("CSMSG_NOTELIST_END", channel->name);
5022         else
5023             reply("CSMSG_NOTELIST_EMPTY", channel->name);
5024     }
5025     /* If one argument, show the named note. */
5026     else if(argc == 2)
5027     {
5028         if((note = dict_find(cData->notes, argv[1], NULL))
5029            && note_type_visible_to_user(cData, note->type, user))
5030         {
5031             reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5032         }
5033         else if((ntype = dict_find(note_types, argv[1], NULL))
5034                 && note_type_visible_to_user(NULL, ntype, user))
5035         {
5036             reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5037             return 0;
5038         }
5039         else
5040         {
5041             reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5042             return 0;
5043         }
5044     }
5045     /* Assume they're trying to set a note. */
5046     else
5047     {
5048         char *note_text;
5049         ntype = dict_find(note_types, argv[1], NULL);
5050         if(!ntype)
5051         {
5052             reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5053             return 0;
5054         }
5055         else if(note_type_settable_by_user(channel, ntype, user))
5056         {
5057             note_text = unsplit_string(argv+2, argc-2, NULL);
5058             if((note = dict_find(cData->notes, argv[1], NULL)))
5059                 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5060             chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5061             reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5062
5063             if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5064             {
5065                 /* The note is viewable to staff only, so return 0
5066                    to keep the invocation from getting logged (or
5067                    regular users can see it in !events). */
5068                 return 0;
5069             }
5070         }
5071         else
5072         {
5073             reply("CSMSG_NO_ACCESS");
5074             return 0;
5075         }
5076     }
5077     return 1;
5078 }
5079
5080 static CHANSERV_FUNC(cmd_delnote)
5081 {
5082     struct note *note;
5083
5084     REQUIRE_PARAMS(2);
5085     if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5086        || !note_type_settable_by_user(channel, note->type, user))
5087     {
5088         reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5089         return 0;
5090     }
5091     dict_remove(channel->channel_info->notes, note->type->name);
5092     reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5093     return 1;
5094 }
5095
5096 static CHANSERV_FUNC(cmd_events)
5097 {
5098     struct logSearch discrim;
5099     struct logReport report;
5100     unsigned int matches, limit;
5101
5102     limit = (argc > 1) ? atoi(argv[1]) : 10;
5103     if(limit < 1 || limit > 200)
5104         limit = 10;
5105
5106     memset(&discrim, 0, sizeof(discrim));
5107     discrim.masks.bot = chanserv;
5108     discrim.masks.channel_name = channel->name;
5109     if(argc > 2)
5110         discrim.masks.command = argv[2];
5111     discrim.limit = limit;
5112     discrim.max_time = INT_MAX;
5113     discrim.severities = 1 << LOG_COMMAND;
5114     report.reporter = chanserv;
5115     report.user = user;
5116     reply("CSMSG_EVENT_SEARCH_RESULTS");
5117     matches = log_entry_search(&discrim, log_report_entry, &report);
5118     if(matches)
5119         reply("MSG_MATCH_COUNT", matches);
5120     else
5121         reply("MSG_NO_MATCHES");
5122     return 1;
5123 }
5124
5125 static CHANSERV_FUNC(cmd_say)
5126 {
5127     char *msg;
5128     if(channel)
5129     {
5130         REQUIRE_PARAMS(2);
5131         msg = unsplit_string(argv + 1, argc - 1, NULL);
5132         send_channel_message(channel, cmd->parent->bot, "%s", msg);
5133     }
5134     else if(*argv[1] == '*' && argv[1][1] != '\0')
5135     {
5136         struct handle_info *hi;
5137         struct userNode *authed;
5138
5139         REQUIRE_PARAMS(3);
5140         msg = unsplit_string(argv + 2, argc - 2, NULL);
5141
5142         if (!(hi = get_handle_info(argv[1] + 1)))
5143         {
5144             reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5145             return 0;
5146         }
5147
5148         for (authed = hi->users; authed; authed = authed->next_authed)
5149             send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5150     }
5151     else if(GetUserH(argv[1]))
5152     {
5153         REQUIRE_PARAMS(3);
5154         msg = unsplit_string(argv + 2, argc - 2, NULL);
5155         send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5156     }
5157     else
5158     {
5159         reply("MSG_NOT_TARGET_NAME");
5160         return 0;
5161     }
5162     return 1;
5163 }
5164
5165 static CHANSERV_FUNC(cmd_emote)
5166 {
5167     char *msg;
5168     assert(argc >= 2);
5169     if(channel)
5170     {
5171         /* CTCP is so annoying. */
5172         msg = unsplit_string(argv + 1, argc - 1, NULL);
5173         send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5174     }
5175     else if(*argv[1] == '*' && argv[1][1] != '\0')
5176     {
5177         struct handle_info *hi;
5178         struct userNode *authed;
5179
5180         REQUIRE_PARAMS(3);
5181         msg = unsplit_string(argv + 2, argc - 2, NULL);
5182
5183         if (!(hi = get_handle_info(argv[1] + 1)))
5184         {
5185             reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5186             return 0;
5187         }
5188
5189         for (authed = hi->users; authed; authed = authed->next_authed)
5190             send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5191     }
5192     else if(GetUserH(argv[1]))
5193     {
5194         msg = unsplit_string(argv + 2, argc - 2, NULL);
5195         send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5196     }
5197     else
5198     {
5199         reply("MSG_NOT_TARGET_NAME");
5200         return 0;
5201     }
5202     return 1;
5203 }
5204
5205 struct channelList *
5206 chanserv_support_channels(void)
5207 {
5208     return &chanserv_conf.support_channels;
5209 }
5210
5211 static CHANSERV_FUNC(cmd_expire)
5212 {
5213     int channel_count = registered_channels;
5214     expire_channels(chanserv);
5215     reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5216     return 1;
5217 }
5218
5219 static void
5220 chanserv_expire_suspension(void *data)
5221 {
5222     struct suspended *suspended = data;
5223     struct chanNode *channel;
5224     unsigned int ii;
5225
5226     /* Update the channel registration data structure. */
5227     if(!suspended->expires || (now < suspended->expires))
5228         suspended->revoked = now;
5229     channel = suspended->cData->channel;
5230     suspended->cData->channel = channel;
5231     suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5232
5233     /* If appropriate, re-join ChanServ to the channel. */
5234     if(!IsOffChannel(suspended->cData))
5235     {
5236         spamserv_cs_suspend(channel, 0, 0, NULL);
5237         ss_cs_join_channel(channel, 1);
5238     }
5239
5240     /* Mark everyone currently in the channel as present. */
5241     for(ii = 0; ii < channel->members.used; ++ii)
5242     {
5243         struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5244         if(uData)
5245         {
5246             uData->present = 1;
5247             uData->seen = now;
5248         }
5249     }
5250 }
5251
5252 static CHANSERV_FUNC(cmd_csuspend)
5253 {
5254     struct suspended *suspended;
5255     char reason[MAXLEN];
5256     unsigned long expiry, duration;
5257     struct userData *uData;
5258
5259     REQUIRE_PARAMS(3);
5260
5261     if(IsProtected(channel->channel_info))
5262     {
5263         reply("CSMSG_SUSPEND_NODELETE", channel->name);
5264         return 0;
5265     }
5266
5267     if(argv[1][0] == '!')
5268         argv[1]++;
5269     else if(IsSuspended(channel->channel_info))
5270     {
5271         reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5272         show_suspension_info(cmd, user, channel->channel_info->suspended);
5273         return 0;
5274     }
5275
5276     if(!strcmp(argv[1], "0"))
5277         expiry = 0;
5278     else if((duration = ParseInterval(argv[1])))
5279         expiry = now + duration;
5280     else
5281     {
5282         reply("MSG_INVALID_DURATION", argv[1]);
5283         return 0;
5284     }
5285
5286     unsplit_string(argv + 2, argc - 2, reason);
5287
5288     suspended = calloc(1, sizeof(*suspended));
5289     suspended->revoked = 0;
5290     suspended->issued = now;
5291     suspended->suspender = strdup(user->handle_info->handle);
5292     suspended->expires = expiry;
5293     suspended->reason = strdup(reason);
5294     suspended->cData = channel->channel_info;
5295     suspended->previous = suspended->cData->suspended;
5296     suspended->cData->suspended = suspended;
5297
5298     if(suspended->expires)
5299         timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5300
5301     if(IsSuspended(channel->channel_info))
5302     {
5303         suspended->previous->revoked = now;
5304         if(suspended->previous->expires)
5305             timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5306         sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5307         global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5308     }
5309     else
5310     {
5311         /* Mark all users in channel as absent. */
5312         for(uData = channel->channel_info->users; uData; uData = uData->next)
5313         {
5314             if(uData->present)
5315             {
5316                 uData->seen = now;
5317                 uData->present = 0;
5318             }
5319         }
5320
5321         /* Mark the channel as suspended, then part. */
5322         channel->channel_info->flags |= CHANNEL_SUSPENDED;
5323         spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5324         DelChannelUser(chanserv, channel, suspended->reason, 0);
5325         reply("CSMSG_SUSPENDED", channel->name);
5326         sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5327         global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5328     }
5329     return 1;
5330 }
5331
5332 static CHANSERV_FUNC(cmd_cunsuspend)
5333 {
5334     struct suspended *suspended;
5335     char message[MAXLEN];
5336
5337     if(!IsSuspended(channel->channel_info))
5338     {
5339         reply("CSMSG_NOT_SUSPENDED", channel->name);
5340         return 0;
5341     }
5342
5343     suspended = channel->channel_info->suspended;
5344
5345     /* Expire the suspension and join ChanServ to the channel. */
5346     timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5347     chanserv_expire_suspension(suspended);
5348     reply("CSMSG_UNSUSPENDED", channel->name);
5349     sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5350     global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5351     return 1;
5352 }
5353
5354 typedef struct chanservSearch
5355 {
5356     char *name;
5357     char *registrar;
5358
5359     unsigned long unvisited;
5360     unsigned long registered;
5361
5362     unsigned long flags;
5363     unsigned int limit;
5364 } *search_t;
5365
5366 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5367
5368 static search_t
5369 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5370 {
5371     search_t search;
5372     unsigned int i;
5373
5374     search = malloc(sizeof(struct chanservSearch));
5375     memset(search, 0, sizeof(*search));
5376     search->limit = 25;
5377
5378     for(i = 0; i < argc; i++)
5379     {
5380         /* Assume all criteria require arguments. */
5381         if(i == (argc - 1))
5382         {
5383             send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5384             goto fail;
5385         }
5386
5387         if(!irccasecmp(argv[i], "name"))
5388             search->name = argv[++i];
5389         else if(!irccasecmp(argv[i], "registrar"))
5390             search->registrar = argv[++i];
5391         else if(!irccasecmp(argv[i], "unvisited"))
5392             search->unvisited = ParseInterval(argv[++i]);
5393         else if(!irccasecmp(argv[i], "registered"))
5394             search->registered = ParseInterval(argv[++i]);
5395         else if(!irccasecmp(argv[i], "flags"))
5396         {
5397             i++;
5398             if(!irccasecmp(argv[i], "nodelete"))
5399                 search->flags |= CHANNEL_NODELETE;
5400             else if(!irccasecmp(argv[i], "suspended"))
5401                 search->flags |= CHANNEL_SUSPENDED;
5402             else if(!irccasecmp(argv[i], "unreviewed"))
5403                 search->flags |= CHANNEL_UNREVIEWED;
5404             else
5405             {
5406                 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5407                 goto fail;
5408             }
5409         }
5410         else if(!irccasecmp(argv[i], "limit"))
5411             search->limit = strtoul(argv[++i], NULL, 10);
5412         else
5413         {
5414             send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5415             goto fail;
5416         }
5417     }
5418
5419     if(search->name && !strcmp(search->name, "*"))
5420         search->name = 0;
5421     if(search->registrar && !strcmp(search->registrar, "*"))
5422         search->registrar = 0;
5423
5424     return search;
5425   fail:
5426     free(search);
5427     return NULL;
5428 }
5429
5430 static int
5431 chanserv_channel_match(struct chanData *channel, search_t search)
5432 {
5433     const char *name = channel->channel->name;
5434     if((search->name && !match_ircglob(name, search->name)) ||
5435        (search->registrar && !channel->registrar) ||
5436        (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5437        (search->unvisited && (now - channel->visited) < search->unvisited) ||
5438        (search->registered && (now - channel->registered) > search->registered) ||
5439        (search->flags && ((search->flags & channel->flags) != search->flags)))
5440         return 0;
5441
5442     return 1;
5443 }
5444
5445 static unsigned int
5446 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5447 {
5448     struct chanData *channel;
5449     unsigned int matches = 0;
5450
5451     for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5452     {
5453         if(!chanserv_channel_match(channel, search))
5454             continue;
5455         matches++;
5456         smf(channel, data);
5457     }
5458
5459     return matches;
5460 }
5461
5462 static void
5463 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5464 {
5465 }
5466
5467 static void
5468 search_print(struct chanData *channel, void *data)
5469 {
5470     send_message_type(4, data, chanserv, "%s", channel->channel->name);
5471 }
5472
5473 static CHANSERV_FUNC(cmd_search)
5474 {
5475     search_t search;
5476     unsigned int matches;
5477     channel_search_func action;
5478
5479     REQUIRE_PARAMS(3);
5480
5481     if(!irccasecmp(argv[1], "count"))
5482         action = search_count;
5483     else if(!irccasecmp(argv[1], "print"))
5484         action = search_print;
5485     else
5486     {
5487         reply("CSMSG_ACTION_INVALID", argv[1]);
5488         return 0;
5489     }
5490
5491     search = chanserv_search_create(user, argc - 2, argv + 2);
5492     if(!search)
5493         return 0;
5494
5495     if(action == search_count)
5496         search->limit = INT_MAX;
5497
5498     if(action == search_print)
5499         reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5500
5501     matches = chanserv_channel_search(search, action, user);
5502
5503     if(matches)
5504         reply("MSG_MATCH_COUNT", matches);
5505     else
5506         reply("MSG_NO_MATCHES");
5507
5508     free(search);
5509     return 1;
5510 }
5511
5512 static CHANSERV_FUNC(cmd_unvisited)
5513 {
5514     struct chanData *cData;
5515     unsigned long interval = chanserv_conf.channel_expire_delay;
5516     char buffer[INTERVALLEN];
5517     unsigned int limit = 25, matches = 0;
5518
5519     if(argc > 1)
5520     {
5521         interval = ParseInterval(argv[1]);
5522         if(argc > 2)
5523             limit = atoi(argv[2]);
5524     }
5525
5526     intervalString(buffer, interval, user->handle_info);
5527     reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5528
5529     for(cData = channelList; cData && matches < limit; cData = cData->next)
5530     {
5531         if((now - cData->visited) < interval)
5532             continue;
5533
5534         intervalString(buffer, now - cData->visited, user->handle_info);
5535         reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5536         matches++;
5537     }
5538
5539     return 1;
5540 }
5541
5542 static MODCMD_FUNC(chan_opt_defaulttopic)
5543 {
5544     if(argc > 1)
5545     {
5546         char *topic;
5547
5548         if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5549         {
5550             reply("CSMSG_TOPIC_LOCKED", channel->name);
5551             return 0;
5552         }
5553
5554         topic = unsplit_string(argv+1, argc-1, NULL);
5555
5556         free(channel->channel_info->topic);
5557         if(topic[0] == '*' && topic[1] == 0)
5558         {
5559             topic = channel->channel_info->topic = NULL;
5560         }
5561         else
5562         {
5563             topic = channel->channel_info->topic = strdup(topic);
5564             if(channel->channel_info->topic_mask
5565                && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5566                 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5567         }
5568         SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5569     }
5570
5571     if(channel->channel_info->topic)
5572         reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5573     else
5574         reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5575     return 1;
5576 }
5577
5578 static MODCMD_FUNC(chan_opt_topicmask)
5579 {
5580     if(argc > 1)
5581     {
5582         struct chanData *cData = channel->channel_info;
5583         char *mask;
5584
5585         if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5586         {
5587             reply("CSMSG_TOPIC_LOCKED", channel->name);
5588             return 0;
5589         }
5590
5591         mask = unsplit_string(argv+1, argc-1, NULL);
5592
5593         if(cData->topic_mask)
5594             free(cData->topic_mask);
5595         if(mask[0] == '*' && mask[1] == 0)
5596         {
5597             cData->topic_mask = 0;
5598         }
5599         else
5600         {
5601             cData->topic_mask = strdup(mask);
5602             if(!cData->topic)
5603                 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5604             else if(!match_ircglob(cData->topic, cData->topic_mask))
5605                 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5606         }
5607     }
5608
5609     if(channel->channel_info->topic_mask)
5610         reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5611     else
5612         reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5613     return 1;
5614 }
5615
5616 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5617 {
5618     if(argc > 1)
5619     {
5620         char *greeting = unsplit_string(argv+1, argc-1, NULL);
5621         char *previous;
5622
5623         previous = *data;
5624         if(greeting[0] == '*' && greeting[1] == 0)
5625             *data = NULL;
5626         else
5627         {
5628             unsigned int length = strlen(greeting);
5629             if(length > chanserv_conf.greeting_length)
5630             {
5631                 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5632                 return 0;
5633             }
5634             *data = strdup(greeting);
5635         }
5636         if(previous)
5637             free(previous);
5638     }
5639
5640     if(*data)
5641         reply(name, *data);
5642     else
5643         reply(name, user_find_message(user, "MSG_NONE"));
5644     return 1;
5645 }
5646
5647 static MODCMD_FUNC(chan_opt_greeting)
5648 {
5649     return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5650 }
5651
5652 static MODCMD_FUNC(chan_opt_usergreeting)
5653 {
5654     return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5655 }
5656
5657 static MODCMD_FUNC(chan_opt_modes)
5658 {
5659     struct mod_chanmode *new_modes;
5660     char modes[MAXLEN];
5661
5662     if(argc > 1)
5663     {
5664         if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5665         {
5666             reply("CSMSG_NO_ACCESS");
5667             return 0;
5668         }
5669         if(argv[1][0] == '*' && argv[1][1] == 0)
5670         {
5671             memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5672         }
5673         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)))
5674         {
5675             reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5676             return 0;
5677         }
5678         else if(new_modes->argc > 1)
5679         {
5680             reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5681             mod_chanmode_free(new_modes);
5682             return 0;
5683         }
5684         else
5685         {
5686             channel->channel_info->modes = *new_modes;
5687             modcmd_chanmode_announce(new_modes);
5688             mod_chanmode_free(new_modes);
5689         }
5690     }
5691
5692     mod_chanmode_format(&channel->channel_info->modes, modes);
5693     if(modes[0])
5694         reply("CSMSG_SET_MODES", modes);
5695     else
5696         reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5697     return 1;
5698 }
5699
5700 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5701 static int
5702 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5703 {
5704     struct chanData *cData = channel->channel_info;
5705     int value;
5706
5707     if(argc > 1)
5708     {
5709         /* Set flag according to value. */
5710         if(enabled_string(argv[1]))
5711         {
5712             cData->flags |= mask;
5713             value = 1;
5714         }
5715         else if(disabled_string(argv[1]))
5716         {
5717             cData->flags &= ~mask;
5718             value = 0;
5719         }
5720         else
5721         {
5722             reply("MSG_INVALID_BINARY", argv[1]);
5723             return 0;
5724         }
5725     }
5726     else
5727     {
5728         /* Find current option value. */
5729         value = (cData->flags & mask) ? 1 : 0;
5730     }
5731
5732     if(value)
5733         reply(name, user_find_message(user, "MSG_ON"));
5734     else
5735         reply(name, user_find_message(user, "MSG_OFF"));
5736     return 1;
5737 }
5738
5739 static MODCMD_FUNC(chan_opt_nodelete)
5740 {
5741     if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5742     {
5743         reply("MSG_SETTING_PRIVILEGED", argv[0]);
5744         return 0;
5745     }
5746
5747     CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5748 }
5749
5750 static MODCMD_FUNC(chan_opt_dynlimit)
5751 {
5752     CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5753 }
5754
5755 static MODCMD_FUNC(chan_opt_offchannel)
5756 {
5757     struct chanData *cData = channel->channel_info;
5758     int value;
5759
5760     if(argc > 1)
5761     {
5762         /* Set flag according to value. */
5763         if(enabled_string(argv[1]))
5764         {
5765             if(!IsOffChannel(cData))
5766                 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5767             cData->flags |= CHANNEL_OFFCHANNEL;
5768             value = 1;
5769         }
5770         else if(disabled_string(argv[1]))
5771         {
5772             if(IsOffChannel(cData))
5773             {
5774                 struct mod_chanmode change;
5775                 mod_chanmode_init(&change);
5776                 change.argc = 1;
5777                 change.args[0].mode = MODE_CHANOP;
5778                 change.args[0].u.member = AddChannelUser(chanserv, channel);
5779                 mod_chanmode_announce(chanserv, channel, &change);
5780             }
5781             cData->flags &= ~CHANNEL_OFFCHANNEL;
5782             value = 0;
5783         }
5784         else
5785         {
5786             reply("MSG_INVALID_BINARY", argv[1]);
5787             return 0;
5788         }
5789     }
5790     else
5791     {
5792         /* Find current option value. */
5793         value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5794     }
5795
5796     if(value)
5797         reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5798     else
5799         reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5800     return 1;
5801 }
5802
5803 static MODCMD_FUNC(chan_opt_unreviewed)
5804 {
5805     struct chanData *cData = channel->channel_info;
5806     int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5807
5808     if(argc > 1)
5809     {
5810         int new_value;
5811
5812         /* The two directions can have different ACLs. */
5813         if(enabled_string(argv[1]))
5814             new_value = 1;
5815         else if(disabled_string(argv[1]))
5816             new_value = 0;
5817         else
5818         {
5819             reply("MSG_INVALID_BINARY", argv[1]);
5820             return 0;
5821         }
5822
5823         if (new_value != value)
5824         {
5825             struct svccmd *subcmd;
5826             char subcmd_name[32];
5827
5828             snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5829             subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5830             if(!subcmd)
5831             {
5832                 reply("MSG_COMMAND_DISABLED", subcmd_name);
5833                 return 0;
5834             }
5835             else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5836                 return 0;
5837
5838             if (new_value)
5839                 cData->flags |= CHANNEL_UNREVIEWED;
5840             else
5841             {
5842                 free(cData->registrar);
5843                 cData->registrar = strdup(user->handle_info->handle);
5844                 cData->flags &= ~CHANNEL_UNREVIEWED;
5845             }
5846             value = new_value;
5847         }
5848     }
5849
5850     if(value)
5851         reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5852     else
5853         reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5854     return 1;
5855 }
5856
5857 static MODCMD_FUNC(chan_opt_defaults)
5858 {
5859     struct userData *uData;
5860     struct chanData *cData;
5861     const char *confirm;
5862     enum levelOption lvlOpt;
5863     enum charOption chOpt;
5864
5865     cData = channel->channel_info;
5866     uData = GetChannelUser(cData, user->handle_info);
5867     if(!uData || (uData->access < UL_OWNER))
5868     {
5869         reply("CSMSG_OWNER_DEFAULTS", channel->name);
5870         return 0;
5871     }
5872     confirm = make_confirmation_string(uData);
5873     if((argc < 2) || strcmp(argv[1], confirm))
5874     {
5875         reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5876         return 0;
5877     }
5878     cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5879         | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5880     cData->modes = chanserv_conf.default_modes;
5881     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5882         cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5883     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5884         cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5885     reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5886     return 1;
5887 }
5888
5889 static int
5890 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5891 {
5892     struct chanData *cData = channel->channel_info;
5893     struct userData *uData;
5894     unsigned short value;
5895
5896     if(argc > 1)
5897     {
5898         if(!check_user_level(channel, user, option, 1, 1))
5899         {
5900             reply("CSMSG_CANNOT_SET");
5901             return 0;
5902         }
5903         value = user_level_from_name(argv[1], UL_OWNER+1);
5904         if(!value && strcmp(argv[1], "0"))
5905         {
5906             reply("CSMSG_INVALID_ACCESS", argv[1]);
5907             return 0;
5908         }
5909         uData = GetChannelUser(cData, user->handle_info);
5910         if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5911         {
5912             reply("CSMSG_BAD_SETLEVEL");
5913             return 0;
5914         }
5915         switch(option)
5916         {
5917         case lvlGiveVoice:
5918             if(value > cData->lvlOpts[lvlGiveOps])
5919             {
5920                 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5921                 return 0;
5922             }
5923             break;
5924         case lvlGiveOps:
5925             if(value < cData->lvlOpts[lvlGiveVoice])
5926             {
5927                 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5928                 return 0;
5929             }
5930             break;
5931         case lvlSetters:
5932             /* This test only applies to owners, since non-owners
5933              * trying to set an option to above their level get caught
5934              * by the CSMSG_BAD_SETLEVEL test above.
5935              */
5936             if(value > uData->access)
5937             {
5938                 reply("CSMSG_BAD_SETTERS");
5939                 return 0;
5940             }
5941             break;
5942         default:
5943             break;
5944         }
5945         cData->lvlOpts[option] = value;
5946     }
5947     reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5948     return argc > 1;
5949 }
5950
5951 static MODCMD_FUNC(chan_opt_enfops)
5952 {
5953     return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5954 }
5955
5956 static MODCMD_FUNC(chan_opt_giveops)
5957 {
5958     return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5959 }
5960
5961 static MODCMD_FUNC(chan_opt_enfmodes)
5962 {
5963     return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5964 }
5965
5966 static MODCMD_FUNC(chan_opt_enftopic)
5967 {
5968     return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5969 }
5970
5971 static MODCMD_FUNC(chan_opt_pubcmd)
5972 {
5973     return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5974 }
5975
5976 static MODCMD_FUNC(chan_opt_setters)
5977 {
5978     return channel_level_option(lvlSetters, CSFUNC_ARGS);
5979 }
5980
5981 static MODCMD_FUNC(chan_opt_ctcpusers)
5982 {
5983     return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5984 }
5985
5986 static MODCMD_FUNC(chan_opt_userinfo)
5987 {
5988     return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5989 }
5990
5991 static MODCMD_FUNC(chan_opt_givevoice)
5992 {
5993     return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5994 }
5995
5996 static MODCMD_FUNC(chan_opt_topicsnarf)
5997 {
5998     return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5999 }
6000
6001 static MODCMD_FUNC(chan_opt_vote)
6002 {
6003     return channel_level_option(lvlVote, CSFUNC_ARGS);
6004 }
6005
6006 static MODCMD_FUNC(chan_opt_inviteme)
6007 {
6008     return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6009 }
6010
6011 static int
6012 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6013 {
6014     struct chanData *cData = channel->channel_info;
6015     int count = charOptions[option].count, idx;
6016
6017     if(argc > 1)
6018     {
6019         idx = atoi(argv[1]);
6020
6021         if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6022         {
6023             reply("CSMSG_INVALID_NUMERIC", idx);
6024             /* Show possible values. */
6025             for(idx = 0; idx < count; idx++)
6026                 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6027             return 0;
6028         }
6029
6030         cData->chOpts[option] = charOptions[option].values[idx].value;
6031     }
6032     else
6033     {
6034         /* Find current option value. */
6035       find_value:
6036         for(idx = 0;
6037             (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6038             idx++);
6039         if(idx == count)
6040         {
6041             /* Somehow, the option value is corrupt; reset it to the default. */
6042             cData->chOpts[option] = charOptions[option].default_value;
6043             goto find_value;
6044         }
6045     }
6046
6047     reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6048     return 1;
6049 }
6050
6051 static MODCMD_FUNC(chan_opt_protect)
6052 {
6053     return channel_multiple_option(chProtect, CSFUNC_ARGS);
6054 }
6055
6056 static MODCMD_FUNC(chan_opt_toys)
6057 {
6058     return channel_multiple_option(chToys, CSFUNC_ARGS);
6059 }
6060
6061 static MODCMD_FUNC(chan_opt_ctcpreaction)
6062 {
6063     return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6064 }
6065
6066 static MODCMD_FUNC(chan_opt_topicrefresh)
6067 {
6068     return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6069 }
6070
6071 static struct svccmd_list set_shows_list;
6072
6073 static void
6074 handle_svccmd_unbind(struct svccmd *target) {
6075     unsigned int ii;
6076     for(ii=0; ii<set_shows_list.used; ++ii)
6077         if(target == set_shows_list.list[ii])
6078             set_shows_list.used = 0;
6079 }
6080
6081 static CHANSERV_FUNC(cmd_set)
6082 {
6083     struct svccmd *subcmd;
6084     char buf[MAXLEN];
6085     unsigned int ii;
6086
6087     /* Check if we need to (re-)initialize set_shows_list. */
6088     if(!set_shows_list.used)
6089     {
6090         if(!set_shows_list.size)
6091         {
6092             set_shows_list.size = chanserv_conf.set_shows->used;
6093             set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6094         }
6095         for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6096         {
6097             const char *name = chanserv_conf.set_shows->list[ii];
6098             sprintf(buf, "%s %s", argv[0], name);
6099             subcmd = dict_find(cmd->parent->commands, buf, NULL);
6100             if(!subcmd)
6101             {
6102                 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6103                 continue;
6104             }
6105             svccmd_list_append(&set_shows_list, subcmd);
6106         }
6107     }
6108
6109     if(argc < 2)
6110     {
6111         reply("CSMSG_CHANNEL_OPTIONS");
6112         for(ii = 0; ii < set_shows_list.used; ii++)
6113         {
6114             subcmd = set_shows_list.list[ii];
6115             subcmd->command->func(user, channel, 1, argv+1, subcmd);
6116         }
6117         return 1;
6118     }
6119
6120     sprintf(buf, "%s %s", argv[0], argv[1]);
6121     subcmd = dict_find(cmd->parent->commands, buf, NULL);
6122     if(!subcmd)
6123     {
6124         reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6125         return 0;
6126     }
6127     if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6128     {
6129         reply("CSMSG_NO_ACCESS");
6130         return 0;
6131     }
6132
6133     argv[0] = "";
6134     argv[1] = buf;
6135     return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6136 }
6137
6138 static int
6139 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6140 {
6141     struct userData *uData;
6142
6143     uData = GetChannelAccess(channel->channel_info, user->handle_info);
6144     if(!uData)
6145     {
6146         reply("CSMSG_NOT_USER", channel->name);
6147         return 0;
6148     }
6149
6150     if(argc < 2)
6151     {
6152         /* Just show current option value. */
6153     }
6154     else if(enabled_string(argv[1]))
6155     {
6156         uData->flags |= mask;
6157     }
6158     else if(disabled_string(argv[1]))
6159     {
6160         uData->flags &= ~mask;
6161     }
6162     else
6163     {
6164         reply("MSG_INVALID_BINARY", argv[1]);
6165         return 0;
6166     }
6167
6168     reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6169     return 1;
6170 }
6171
6172 static MODCMD_FUNC(user_opt_noautoop)
6173 {
6174     struct userData *uData;
6175
6176     uData = GetChannelAccess(channel->channel_info, user->handle_info);
6177     if(!uData)
6178     {
6179         reply("CSMSG_NOT_USER", channel->name);
6180         return 0;
6181     }
6182     if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6183         return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6184     else
6185         return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6186 }
6187
6188 static MODCMD_FUNC(user_opt_autoinvite)
6189 {
6190     if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6191     {
6192         reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6193     }
6194     return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6195 }
6196
6197 static MODCMD_FUNC(user_opt_info)
6198 {
6199     struct userData *uData;
6200     char *infoline;
6201
6202     uData = GetChannelAccess(channel->channel_info, user->handle_info);
6203
6204     if(!uData)
6205     {
6206         /* If they got past the command restrictions (which require access)
6207          * but fail this test, we have some fool with security override on.
6208          */
6209         reply("CSMSG_NOT_USER", channel->name);
6210         return 0;
6211     }
6212
6213     if(argc > 1)
6214     {
6215         size_t bp;
6216         infoline = unsplit_string(argv + 1, argc - 1, NULL);
6217         if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6218         {
6219             reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6220             return 0;
6221         }
6222         bp = strcspn(infoline, "\001");
6223         if(infoline[bp])
6224         {
6225             reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6226             return 0;
6227         }
6228         if(uData->info)
6229             free(uData->info);
6230         if(infoline[0] == '*' && infoline[1] == 0)
6231             uData->info = NULL;
6232         else
6233             uData->info = strdup(infoline);
6234     }
6235     if(uData->info)
6236         reply("CSMSG_USET_INFO", uData->info);
6237     else
6238         reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6239     return 1;
6240 }
6241
6242 struct svccmd_list uset_shows_list;
6243
6244 static CHANSERV_FUNC(cmd_uset)
6245 {
6246     struct svccmd *subcmd;
6247     char buf[MAXLEN];
6248     unsigned int ii;
6249
6250     /* Check if we need to (re-)initialize uset_shows_list. */
6251     if(!uset_shows_list.used)
6252     {
6253         char *options[] =
6254         {
6255             "NoAutoOp", "AutoInvite", "Info"
6256         };
6257
6258         if(!uset_shows_list.size)
6259         {
6260             uset_shows_list.size = ArrayLength(options);
6261             uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6262         }
6263         for(ii = 0; ii < ArrayLength(options); ii++)
6264         {
6265             const char *name = options[ii];
6266             sprintf(buf, "%s %s", argv[0], name);
6267             subcmd = dict_find(cmd->parent->commands, buf, NULL);
6268             if(!subcmd)
6269             {
6270                 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6271                 continue;
6272             }
6273             svccmd_list_append(&uset_shows_list, subcmd);
6274         }
6275     }
6276
6277     if(argc < 2)
6278     {
6279         /* Do this so options are presented in a consistent order. */
6280         reply("CSMSG_USER_OPTIONS");
6281         for(ii = 0; ii < uset_shows_list.used; ii++)
6282             uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6283         return 1;
6284     }
6285
6286     sprintf(buf, "%s %s", argv[0], argv[1]);
6287     subcmd = dict_find(cmd->parent->commands, buf, NULL);
6288     if(!subcmd)
6289     {
6290         reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6291         return 0;
6292     }
6293
6294     return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6295 }
6296
6297 static CHANSERV_FUNC(cmd_giveownership)
6298 {
6299     struct handle_info *new_owner_hi;
6300     struct userData *new_owner;
6301     struct userData *curr_user;
6302     struct userData *invoker;
6303     struct chanData *cData = channel->channel_info;
6304     struct do_not_register *dnr;
6305     const char *confirm;
6306     unsigned int force;
6307     unsigned short co_access;
6308     char reason[MAXLEN];
6309
6310     REQUIRE_PARAMS(2);
6311     curr_user = GetChannelAccess(cData, user->handle_info);
6312     force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6313     if(!curr_user || (curr_user->access != UL_OWNER))
6314     {
6315         struct userData *owner = NULL;
6316         for(curr_user = channel->channel_info->users;
6317             curr_user;
6318             curr_user = curr_user->next)
6319         {
6320             if(curr_user->access != UL_OWNER)
6321                 continue;
6322             if(owner)
6323             {
6324                 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6325                 return 0;
6326             }
6327             owner = curr_user;
6328         }
6329         curr_user = owner;
6330     }
6331     else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6332     {
6333         char delay[INTERVALLEN];
6334         intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6335         reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6336         return 0;
6337     }
6338     if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6339         return 0;
6340     if(new_owner_hi == user->handle_info)
6341     {
6342         reply("CSMSG_NO_TRANSFER_SELF");
6343         return 0;
6344     }
6345     new_owner = GetChannelAccess(cData, new_owner_hi);
6346     if(!new_owner)
6347     {
6348         if(force)
6349         {
6350             new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6351         }
6352         else
6353         {
6354             reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6355             return 0;
6356         }
6357     }
6358     if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6359     {
6360         reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6361         return 0;
6362     }
6363     if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6364         if(!IsHelping(user))
6365             reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6366         else
6367             chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6368         return 0;
6369     }
6370     invoker = GetChannelUser(cData, user->handle_info);
6371     if(invoker->access <= UL_OWNER)
6372     {
6373         confirm = make_confirmation_string(curr_user);
6374         if((argc < 3) || strcmp(argv[2], confirm))
6375         {
6376             reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6377             return 0;
6378         }
6379     }
6380     if(new_owner->access >= UL_COOWNER)
6381         co_access = new_owner->access;
6382     else
6383         co_access = UL_COOWNER;
6384     new_owner->access = UL_OWNER;
6385     if(curr_user)
6386         curr_user->access = co_access;
6387     cData->ownerTransfer = now;
6388     reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6389     sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6390     global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6391     return 1;
6392 }
6393
6394 static CHANSERV_FUNC(cmd_suspend)
6395 {
6396     struct handle_info *hi;
6397     struct userData *actor, *real_actor, *target;
6398     unsigned int override = 0;
6399
6400     REQUIRE_PARAMS(2);
6401     if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6402     actor = GetChannelUser(channel->channel_info, user->handle_info);
6403     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6404     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6405     {
6406         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6407         return 0;
6408     }
6409     if(target->access >= actor->access)
6410     {
6411         reply("MSG_USER_OUTRANKED", hi->handle);
6412         return 0;
6413     }
6414     if(target->flags & USER_SUSPENDED)
6415     {
6416         reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6417         return 0;
6418     }
6419     if(target->present)
6420     {
6421         target->present = 0;
6422         target->seen = now;
6423     }
6424     if(!real_actor || target->access >= real_actor->access)
6425         override = CMD_LOG_OVERRIDE;
6426     target->flags |= USER_SUSPENDED;
6427     reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6428     return 1 | override;
6429 }
6430
6431 static CHANSERV_FUNC(cmd_unsuspend)
6432 {
6433     struct handle_info *hi;
6434     struct userData *actor, *real_actor, *target;
6435     unsigned int override = 0;
6436
6437     REQUIRE_PARAMS(2);
6438     if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6439     actor = GetChannelUser(channel->channel_info, user->handle_info);
6440     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6441     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6442     {
6443         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6444         return 0;
6445     }
6446     if(target->access >= actor->access)
6447     {
6448         reply("MSG_USER_OUTRANKED", hi->handle);
6449         return 0;
6450     }
6451     if(!(target->flags & USER_SUSPENDED))
6452     {
6453         reply("CSMSG_NOT_SUSPENDED", hi->handle);
6454         return 0;
6455     }
6456     if(!real_actor || target->access >= real_actor->access)
6457         override = CMD_LOG_OVERRIDE;
6458     target->flags &= ~USER_SUSPENDED;
6459     scan_user_presence(target, NULL);
6460     reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6461     return 1 | override;
6462 }
6463
6464 static MODCMD_FUNC(cmd_deleteme)
6465 {
6466     struct handle_info *hi;
6467     struct userData *target;
6468     const char *confirm_string;
6469     unsigned short access_level;
6470     char *channel_name;
6471
6472     hi = user->handle_info;
6473     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6474     {
6475         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6476         return 0;
6477     }
6478     if(target->access == UL_OWNER)
6479     {
6480         reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6481         return 0;
6482     }
6483     confirm_string = make_confirmation_string(target);
6484     if((argc < 2) || strcmp(argv[1], confirm_string))
6485     {
6486         reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6487         return 0;
6488     }
6489     access_level = target->access;
6490     channel_name = strdup(channel->name);
6491     del_channel_user(target, 1);
6492     reply("CSMSG_DELETED_YOU", access_level, channel_name);
6493     free(channel_name);
6494     return 1;
6495 }
6496
6497 static CHANSERV_FUNC(cmd_addvote)
6498 {
6499     struct chanData *cData = channel->channel_info;
6500     struct userData *uData, *target;
6501     struct handle_info *hi;
6502     if (!cData) return 0;
6503     REQUIRE_PARAMS(2);
6504     hi = user->handle_info;
6505     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6506     {
6507         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6508         return 0;
6509     }
6510     if(target->access < 300) {
6511         reply("CSMSG_NO_ACCESS");
6512         return 0;
6513     }
6514     if (cData->vote) {
6515         reply("CSMSG_ADDVOTE_FULL");
6516         return 0;
6517     }
6518     char *msg;
6519     msg = unsplit_string(argv + 1, argc - 1, NULL);
6520     cData->vote = strdup(msg);
6521     cData->vote_start=0;
6522     dict_delete(cData->vote_options);
6523     cData->vote_options = dict_new();
6524     dict_set_free_data(cData->vote_options, free_vote_options);
6525     for(uData = channel->channel_info->users; uData; uData = uData->next)
6526     {
6527         uData->voted = 0;
6528         uData->votefor = 0;
6529     }
6530     reply("CSMSG_ADDVOTE_DONE");
6531     return 1;
6532 }
6533
6534 static CHANSERV_FUNC(cmd_delvote)
6535 {
6536     struct chanData *cData = channel->channel_info;
6537     struct userData *target;
6538     struct handle_info *hi;
6539     if (!cData) return 0;
6540     hi = user->handle_info;
6541     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6542     {
6543         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6544         return 0;
6545     }
6546     if(target->access < 300) {
6547         reply("CSMSG_NO_ACCESS");
6548         return 0;
6549     }
6550     if (!cData->vote) {
6551         reply("CSMSG_NO_VOTE");
6552         return 0;
6553     }
6554     free(cData->vote);
6555     cData->vote = NULL;
6556     reply("CSMSG_DELVOTE_DONE");
6557     return 1;
6558 }
6559
6560 static CHANSERV_FUNC(cmd_addoption)
6561 {
6562     struct chanData *cData = channel->channel_info;
6563     struct userData *target;
6564     struct handle_info *hi;
6565     if (!cData) return 0;
6566     REQUIRE_PARAMS(2);
6567     hi = user->handle_info;
6568     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6569     {
6570         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6571         return 0;
6572     }
6573     if(target->access < 300) {
6574         reply("CSMSG_NO_ACCESS");
6575         return 0;
6576     }
6577     if (!cData->vote) {
6578         reply("CSMSG_NO_VOTE");
6579         return 0;
6580     }
6581     
6582     char *msg;
6583     
6584     msg = unsplit_string(argv + 1, argc - 1, NULL);
6585     
6586     dict_iterator_t it;
6587     unsigned int lastid = 1;
6588     for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6589         struct vote_option *cvOpt = iter_data(it);
6590         if(cvOpt->option_id > lastid)
6591             lastid = cvOpt->option_id;
6592     }
6593     struct vote_option *vOpt;
6594     vOpt = calloc(1, sizeof(*vOpt));
6595     vOpt->name = strdup(msg);
6596     vOpt->option_id = (lastid + 1);
6597     char str[50];
6598     sprintf(str,"%i",(lastid + 1));
6599     vOpt->option_str = strdup(str);
6600     vOpt->voted = 0;
6601     dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6602     
6603     reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6604     return 1;
6605 }
6606
6607 static CHANSERV_FUNC(cmd_deloption)
6608 {
6609     struct chanData *cData = channel->channel_info;
6610     struct userData *uData, *target;
6611     struct handle_info *hi;
6612     if (!cData) return 0;
6613     REQUIRE_PARAMS(2);
6614     hi = user->handle_info;
6615     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6616     {
6617         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6618         return 0;
6619     }
6620     if(target->access < 300) {
6621         reply("CSMSG_NO_ACCESS");
6622         return 0;
6623     }
6624     if (!cData->vote) {
6625         reply("CSMSG_NO_VOTE");
6626         return 0;
6627     }
6628     if(cData->vote_start) {
6629         if(dict_size(cData->vote_options) < 3) {
6630             reply("CSMSG_VOTE_NEED_OPTIONS");
6631             return 0;
6632         }
6633     }
6634     
6635     int find_id = atoi(argv[1]);
6636     int ii = 0;
6637     unsigned int found = 0;
6638     dict_iterator_t it;
6639     
6640     for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6641         ii++;
6642         if (find_id == ii) {
6643             struct vote_option *vOpt = iter_data(it);
6644             found = vOpt->option_id;
6645             char str[50];
6646             sprintf(str,"%i",vOpt->option_id);
6647             dict_remove(cData->vote_options, str);
6648         }
6649     }
6650     
6651     if(found > 0) {
6652         for(uData = channel->channel_info->users; uData; uData = uData->next) {
6653             if(uData->votefor == found) {
6654                 uData->voted = 0;
6655                 uData->votefor = 0;
6656             }
6657         }
6658         reply("CSMSG_DELOPTION_DONE");
6659         return 1;
6660     } else {
6661         reply("CSMSG_DELOPTION_NONE");
6662         return 0;
6663     }
6664 }
6665
6666 static CHANSERV_FUNC(cmd_vote)
6667 {
6668     struct chanData *cData = channel->channel_info;
6669     struct userData *target;
6670     struct handle_info *hi;
6671     unsigned int votedfor = 0;
6672     char *votedfor_str = NULL;
6673     
6674     if (!cData || !cData->vote) {
6675         reply("CSMSG_NO_VOTE");
6676         return 0;
6677     }
6678     if(argc > 1 && cData->vote_start) {
6679         hi = user->handle_info;
6680         if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6681         {
6682             reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6683             return 0;
6684         }
6685         if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6686             reply("CSMSG_NO_ACCESS");
6687             return 0;
6688         }
6689         if(target->voted) {
6690             reply("CSMSG_VOTE_VOTED");
6691             return 0;
6692         }
6693         int find_id = atoi(argv[1]);
6694         int ii = 0;
6695         dict_iterator_t it;
6696         for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6697             ii++;
6698             if (find_id == ii) {
6699                 struct vote_option *vOpt = iter_data(it);
6700                 vOpt->voted++;
6701                 target->voted = 1;
6702                 target->votefor = vOpt->option_id;
6703                 votedfor = vOpt->option_id;
6704                 votedfor_str = vOpt->name;
6705             }
6706         }
6707         if(votedfor == 0) {
6708             reply("CSMSG_VOTE_INVALID");
6709             return 0;
6710         }
6711     }
6712     if (!cData->vote_start) {
6713         reply("CSMSG_VOTE_NOT_STARTED");
6714     }
6715     reply("CSMSG_VOTE_QUESTION",cData->vote);
6716     
6717     unsigned int voteid = 0;
6718     dict_iterator_t it;
6719     
6720     for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6721         struct vote_option *vOpt = iter_data(it);
6722         voteid++;
6723         reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6724     }
6725     if(argc > 1 && cData->vote_start && votedfor_str) {
6726         reply("CSMSG_VOTE_DONE",votedfor_str);
6727     }
6728     return 1;
6729 }
6730
6731 static CHANSERV_FUNC(cmd_startvote)
6732 {
6733     struct chanData *cData = channel->channel_info;
6734     struct userData *target;
6735     struct handle_info *hi;
6736     if (!cData) return 0;
6737     hi = user->handle_info;
6738     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6739     {
6740         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6741         return 0;
6742     }
6743     if(target->access < 300) {
6744         reply("CSMSG_NO_ACCESS");
6745         return 0;
6746     }
6747     if (!cData->vote) {
6748         reply("CSMSG_NO_VOTE");
6749         return 0;
6750     }
6751     if(cData->vote_start) {
6752         reply("CSMSG_STARTVOTE_RUNNING");
6753         return 0;
6754     }
6755     if(dict_size(cData->vote_options) < 2) {
6756         reply("CSMSG_VOTE_NEED_OPTIONS");
6757         return 0;
6758     }
6759     cData->vote_start = 1;
6760     char response[MAXLEN];
6761     sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6762     irc_privmsg(cmd->parent->bot, channel->name, response);
6763     sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6764     irc_privmsg(cmd->parent->bot, channel->name, response);
6765     unsigned int voteid = 0;
6766     dict_iterator_t it;
6767     for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6768         struct vote_option *vOpt = iter_data(it);
6769         voteid++;
6770         sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6771         irc_privmsg(cmd->parent->bot, channel->name, response);
6772     }
6773     sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6774     irc_privmsg(cmd->parent->bot, channel->name, response);
6775     sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6776     irc_privmsg(cmd->parent->bot, channel->name, response);
6777     return 1;
6778 }
6779
6780 static CHANSERV_FUNC(cmd_endvote)
6781 {
6782     struct chanData *cData = channel->channel_info;
6783     struct userData *target;
6784     struct handle_info *hi;
6785     if (!cData) return 0;
6786     hi = user->handle_info;
6787     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6788     {
6789         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6790         return 0;
6791     }
6792     if(target->access < 300) {
6793         reply("CSMSG_NO_ACCESS");
6794         return 0;
6795     }
6796     if (!cData->vote) {
6797         reply("CSMSG_NO_VOTE");
6798         return 0;
6799     }
6800     if(!cData->vote_start) {
6801         reply("CSMSG_ENDVOTE_STOPPED");
6802         return 0;
6803     }
6804     cData->vote_start = 0;
6805     reply("CSMSG_ENDVOTE_DONE");
6806     return 1;
6807 }
6808
6809 static CHANSERV_FUNC(cmd_voteresults)
6810 {
6811     struct chanData *cData = channel->channel_info;
6812     struct userData *target;
6813     struct handle_info *hi;
6814     if (!cData) return 0;
6815     if (!cData->vote) {
6816         reply("CSMSG_NO_VOTE");
6817         return 0;
6818     }
6819     if (argc > 1 && !irccasecmp(argv[1], "*")) {
6820         hi = user->handle_info;
6821         if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6822         {
6823             reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6824             return 0;
6825         }
6826         if(target->access < 300) {
6827             reply("CSMSG_NO_ACCESS");
6828             return 0;
6829         }
6830         char response[MAXLEN];
6831         sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
6832         irc_privmsg(cmd->parent->bot, channel->name, response);
6833         unsigned int voteid = 0;
6834         dict_iterator_t it;
6835         for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6836             struct vote_option *vOpt = iter_data(it);
6837             voteid++;
6838             sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
6839             irc_privmsg(cmd->parent->bot, channel->name, response);
6840         }
6841     } else {
6842         reply("CSMSG_VOTE_QUESTION",cData->vote);
6843         unsigned int voteid = 0;
6844        dict_iterator_t it;
6845         for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6846             struct vote_option *vOpt = iter_data(it);
6847             voteid++;
6848             reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6849         }
6850     }
6851     return 1;
6852 }
6853
6854 static void
6855 chanserv_refresh_topics(UNUSED_ARG(void *data))
6856 {
6857     unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6858     struct chanData *cData;
6859     char opt;
6860
6861     for(cData = channelList; cData; cData = cData->next)
6862     {
6863         if(IsSuspended(cData))
6864             continue;
6865         opt = cData->chOpts[chTopicRefresh];
6866         if(opt == 'n')
6867             continue;
6868         if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6869             continue;
6870         if(cData->topic)
6871             SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6872         cData->last_refresh = refresh_num;
6873     }
6874     timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6875 }
6876
6877 static CHANSERV_FUNC(cmd_unf)
6878 {
6879     if(channel)
6880     {
6881         char response[MAXLEN];
6882         const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6883         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6884         irc_privmsg(cmd->parent->bot, channel->name, response);
6885     }
6886     else
6887         reply("CSMSG_UNF_RESPONSE");
6888     return 1;
6889 }
6890
6891 static CHANSERV_FUNC(cmd_ping)
6892 {
6893     if(channel)
6894     {
6895         char response[MAXLEN];
6896         const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6897         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6898         irc_privmsg(cmd->parent->bot, channel->name, response);
6899     }
6900     else
6901         reply("CSMSG_PING_RESPONSE");
6902     return 1;
6903 }
6904
6905 static CHANSERV_FUNC(cmd_wut)
6906 {
6907     if(channel)
6908     {
6909         char response[MAXLEN];
6910         const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6911         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6912         irc_privmsg(cmd->parent->bot, channel->name, response);
6913     }
6914     else
6915         reply("CSMSG_WUT_RESPONSE");
6916     return 1;
6917 }
6918
6919 static CHANSERV_FUNC(cmd_8ball)
6920 {
6921     unsigned int i, j, accum;
6922     const char *resp;
6923
6924     REQUIRE_PARAMS(2);
6925     accum = 0;
6926     for(i=1; i<argc; i++)
6927         for(j=0; argv[i][j]; j++)
6928             accum = (accum << 5) - accum + toupper(argv[i][j]);
6929     resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6930     if(channel)
6931     {
6932         char response[MAXLEN];
6933         sprintf(response, "\ 2%s\ 2: %s", user->nick, resp);
6934         irc_privmsg(cmd->parent->bot, channel->name, response);
6935     }
6936     else
6937         send_message_type(4, user, cmd->parent->bot, "%s", resp);
6938     return 1;
6939 }
6940
6941 static CHANSERV_FUNC(cmd_d)
6942 {
6943     unsigned long sides, count, modifier, ii, total;
6944     char response[MAXLEN], *sep;
6945     const char *fmt;
6946
6947     REQUIRE_PARAMS(2);
6948     if((count = strtoul(argv[1], &sep, 10)) < 1)
6949         goto no_dice;
6950     if(sep[0] == 0)
6951     {
6952         if(count == 1)
6953             goto no_dice;
6954         sides = count;
6955         count = 1;
6956         modifier = 0;
6957     }
6958     else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6959             && (sides = strtoul(sep+1, &sep, 10)) > 1)
6960     {
6961         if(sep[0] == 0)
6962             modifier = 0;
6963         else if((sep[0] == '-') && isdigit(sep[1]))
6964             modifier = strtoul(sep, NULL, 10);
6965         else if((sep[0] == '+') && isdigit(sep[1]))
6966             modifier = strtoul(sep+1, NULL, 10);
6967         else
6968             goto no_dice;
6969     }
6970     else
6971     {
6972       no_dice:
6973         reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6974         return 0;
6975     }
6976     if(count > 10)
6977     {
6978         reply("CSMSG_BAD_DICE_COUNT", count, 10);
6979         return 0;
6980     }
6981     for(total = ii = 0; ii < count; ++ii)
6982         total += (rand() % sides) + 1;
6983     total += modifier;
6984
6985     if((count > 1) || modifier)
6986     {
6987         fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6988         sprintf(response, fmt, total, count, sides, modifier);
6989     }
6990     else
6991     {
6992         fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6993         sprintf(response, fmt, total, sides);
6994     }
6995     if(channel)
6996         send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6997     else
6998         send_message_type(4, user, cmd->parent->bot, "%s", response);
6999     return 1;
7000 }
7001
7002 static CHANSERV_FUNC(cmd_huggle)
7003 {
7004     /* CTCP must be via PRIVMSG, never notice */
7005     if(channel)
7006         send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7007     else
7008         send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7009     return 1;
7010 }
7011
7012 static void
7013 chanserv_adjust_limit(void *data)
7014 {
7015     struct mod_chanmode change;
7016     struct chanData *cData = data;
7017     struct chanNode *channel = cData->channel;
7018     unsigned int limit;
7019
7020     if(IsSuspended(cData))
7021         return;
7022
7023     cData->limitAdjusted = now;
7024     limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7025     if(cData->modes.modes_set & MODE_LIMIT)
7026     {
7027         if(limit > cData->modes.new_limit)
7028             limit = cData->modes.new_limit;
7029         else if(limit == cData->modes.new_limit)
7030             return;
7031     }
7032
7033     mod_chanmode_init(&change);
7034     change.modes_set = MODE_LIMIT;
7035     change.new_limit = limit;
7036     mod_chanmode_announce(chanserv, channel, &change);
7037 }
7038
7039 static void
7040 handle_new_channel(struct chanNode *channel)
7041 {
7042     struct chanData *cData;
7043
7044     if(!(cData = channel->channel_info))
7045         return;
7046
7047     if(cData->modes.modes_set || cData->modes.modes_clear)
7048         mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7049
7050     if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7051         SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7052 }
7053
7054 void handle_new_channel_created(char *chan, struct userNode *user) {
7055     if(user->handle_info && chanserv_conf.new_channel_authed) {
7056         send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7057     } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7058         send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7059     }
7060     if(chanserv_conf.new_channel_msg)
7061         send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7062 }
7063
7064 /* Welcome to my worst nightmare. Warning: Read (or modify)
7065    the code below at your own risk. */
7066 static int
7067 handle_join(struct modeNode *mNode)
7068 {
7069     struct mod_chanmode change;
7070     struct userNode *user = mNode->user;
7071     struct chanNode *channel = mNode->channel;
7072     struct chanData *cData;
7073     struct userData *uData = NULL;
7074     struct banData *bData;
7075     struct handle_info *handle;
7076     unsigned int modes = 0, info = 0;
7077     char *greeting;
7078     unsigned int i = 0;
7079
7080     if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7081         return 0;
7082
7083     cData = channel->channel_info;
7084     if(channel->members.used > cData->max) {
7085         cData->max = channel->members.used;
7086         cData->max_time = now;
7087     }
7088
7089     for(i = 0; i < channel->invited.used; i++)
7090     {
7091         if(channel->invited.list[i] == user) {
7092             userList_remove(&channel->invited, user);
7093         }
7094     }
7095
7096     /* Check for bans.  If they're joining through a ban, one of two
7097      * cases applies:
7098      *   1: Join during a netburst, by riding the break.  Kick them
7099      *      unless they have ops or voice in the channel.
7100      *   2: They're allowed to join through the ban (an invite in
7101      *   ircu2.10, or a +e on Hybrid, or something).
7102      * If they're not joining through a ban, and the banlist is not
7103      * full, see if they're on the banlist for the channel.  If so,
7104      * kickban them.
7105      */
7106     if(user->uplink->burst && !mNode->modes)
7107     {
7108         unsigned int ii;
7109         for(ii = 0; ii < channel->banlist.used; ii++)
7110         {
7111             if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7112             {
7113                 /* Riding a netburst.  Naughty. */
7114                 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7115                 return 1;
7116             }
7117         }
7118     }
7119
7120     mod_chanmode_init(&change);
7121     change.argc = 1;
7122     if(channel->banlist.used < MAXBANS)
7123     {
7124         /* Not joining through a ban. */
7125         for(bData = cData->bans;
7126             bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7127             bData = bData->next);
7128
7129         if(bData)
7130         {
7131             char kick_reason[MAXLEN];
7132             sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7133
7134             bData->triggered = now;
7135             if(bData != cData->bans)
7136             {
7137                 /* Shuffle the ban to the head of the list. */
7138                 if(bData->next)
7139                     bData->next->prev = bData->prev;
7140                 if(bData->prev)
7141                     bData->prev->next = bData->next;
7142
7143                 bData->prev = NULL;
7144                 bData->next = cData->bans;
7145
7146                 if(cData->bans)
7147                     cData->bans->prev = bData;
7148                 cData->bans = bData;
7149             }
7150
7151             change.args[0].mode = MODE_BAN;
7152             change.args[0].u.hostmask = bData->mask;
7153             mod_chanmode_announce(chanserv, channel, &change);
7154             KickChannelUser(user, channel, chanserv, kick_reason);
7155             return 1;
7156         }
7157     }
7158
7159     /* ChanServ will not modify the limits in join-flooded channels,
7160        or when there are enough slots left below the limit. */
7161     if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7162        && !channel->join_flooded
7163        && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7164     {
7165         /* The user count has begun "bumping" into the channel limit,
7166            so set a timer to raise the limit a bit. Any previous
7167            timers are removed so three incoming users within the delay
7168            results in one limit change, not three. */
7169
7170         timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7171         timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7172     }
7173
7174     if(channel->join_flooded)
7175     {
7176         /* don't automatically give ops or voice during a join flood */
7177     }
7178     else if(cData->lvlOpts[lvlGiveOps] == 0)
7179         modes |= MODE_CHANOP;
7180     else if(cData->lvlOpts[lvlGiveVoice] == 0)
7181         modes |= MODE_VOICE;
7182
7183     greeting = cData->greeting;
7184     if(user->handle_info)
7185     {
7186         handle = user->handle_info;
7187
7188         if(IsHelper(user) && !IsHelping(user))
7189         {
7190             unsigned int ii;
7191             for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7192             {
7193                 if(channel == chanserv_conf.support_channels.list[ii])
7194                 {
7195                     HANDLE_SET_FLAG(user->handle_info, HELPING);
7196                     break;
7197                 }
7198             }
7199         }
7200
7201         uData = GetTrueChannelAccess(cData, handle);
7202         if(uData && !IsUserSuspended(uData))
7203         {
7204             /* Ops and above were handled by the above case. */
7205             if(IsUserAutoOp(uData))
7206             {
7207                 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7208                     modes |= MODE_CHANOP;
7209                 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7210                     modes |= MODE_VOICE;
7211             }
7212             if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7213                 cData->visited = now;
7214             if(cData->user_greeting)
7215                 greeting = cData->user_greeting;
7216             if(uData->info
7217                && (uData->access >= cData->lvlOpts[lvlUserInfo])
7218                && ((now - uData->seen) >= chanserv_conf.info_delay)
7219                && !uData->present)
7220                 info = 1;
7221             uData->seen = now;
7222             uData->present = 1;
7223         }
7224     }
7225
7226     /* If user joining normally (not during burst), apply op or voice,
7227      * and send greeting/userinfo as appropriate.
7228      */
7229     if(!user->uplink->burst)
7230     {
7231         if(modes)
7232         {
7233             if(modes & MODE_CHANOP)
7234                 modes &= ~MODE_VOICE;
7235             change.args[0].mode = modes;
7236             change.args[0].u.member = mNode;
7237             mod_chanmode_announce(chanserv, channel, &change);
7238         }
7239         if(greeting)
7240             send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7241         if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7242             send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7243     }
7244     return 0;
7245 }
7246
7247 static void
7248 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7249 {
7250     struct mod_chanmode change;
7251     struct userData *channel;
7252     unsigned int ii, jj;
7253
7254     if(!user->handle_info)
7255         return;
7256
7257     mod_chanmode_init(&change);
7258     change.argc = 1;
7259     for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7260     {
7261         struct chanNode *cn;
7262         struct modeNode *mn;
7263         if(IsUserSuspended(channel)
7264            || IsSuspended(channel->channel)
7265            || !(cn = channel->channel->channel))
7266             continue;
7267
7268         mn = GetUserMode(cn, user);
7269         if(!mn)
7270         {
7271             if(!IsUserSuspended(channel)
7272                && IsUserAutoInvite(channel)
7273                && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7274                && !self->burst
7275                && !user->uplink->burst)
7276                 irc_invite(chanserv, user, cn);
7277             continue;
7278         }
7279
7280         if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7281             channel->channel->visited = now;
7282
7283         if(IsUserAutoOp(channel))
7284         {
7285             if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7286                 change.args[0].mode = MODE_CHANOP;
7287             else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7288                 change.args[0].mode = MODE_VOICE;
7289             else
7290                 change.args[0].mode = 0;
7291             change.args[0].u.member = mn;
7292             if(change.args[0].mode)
7293                 mod_chanmode_announce(chanserv, cn, &change);
7294         }
7295
7296         channel->seen = now;
7297         channel->present = 1;
7298     }
7299
7300     for(ii = 0; ii < user->channels.used; ++ii)
7301     {
7302         struct chanNode *chan = user->channels.list[ii]->channel;
7303         struct banData *ban;
7304
7305         if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7306            || !chan->channel_info
7307            || IsSuspended(chan->channel_info))
7308             continue;
7309         for(jj = 0; jj < chan->banlist.used; ++jj)
7310             if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7311                 break;
7312         if(jj < chan->banlist.used)
7313             continue;
7314         for(ban = chan->channel_info->bans; ban; ban = ban->next)
7315         {
7316             char kick_reason[MAXLEN];
7317             if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7318                 continue;
7319             change.args[0].mode = MODE_BAN;
7320             change.args[0].u.hostmask = ban->mask;
7321             mod_chanmode_announce(chanserv, chan, &change);
7322             sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7323             KickChannelUser(user, chan, chanserv, kick_reason);
7324             ban->triggered = now;
7325             break;
7326         }
7327     }
7328
7329     if(IsSupportHelper(user))
7330     {
7331         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7332         {
7333             if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7334             {
7335                 HANDLE_SET_FLAG(user->handle_info, HELPING);
7336                 break;
7337             }
7338         }
7339     }
7340 }
7341
7342 static void
7343 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7344 {
7345     struct chanData *cData;
7346     struct userData *uData;
7347
7348     cData = mn->channel->channel_info;
7349     if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7350         return;
7351
7352     if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7353     {
7354         /* Allow for a bit of padding so that the limit doesn't
7355            track the user count exactly, which could get annoying. */
7356         if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7357         {
7358             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7359             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7360         }
7361     }
7362
7363     if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7364     {
7365         scan_user_presence(uData, mn->user);
7366         uData->seen = now;
7367         if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7368             cData->visited = now;
7369     }
7370
7371     if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7372     {
7373         unsigned int ii;
7374         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7375             struct chanNode *channel;
7376             struct userNode *exclude;
7377             /* When looking at the channel that is being /part'ed, we
7378              * have to skip over the client that is leaving.  For
7379              * other channels, we must not do that.
7380              */
7381             channel = chanserv_conf.support_channels.list[ii];
7382             exclude = (channel == mn->channel) ? mn->user : NULL;
7383             if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7384                 break;
7385         }
7386         if(ii == chanserv_conf.support_channels.used)
7387             HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7388     }
7389 }
7390
7391 static void
7392 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7393 {
7394     struct userData *uData;
7395
7396     if(!channel->channel_info || !kicker || IsService(kicker)
7397        || (kicker == victim) || IsSuspended(channel->channel_info)
7398        || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7399         return;
7400
7401     if(protect_user(victim, kicker, channel->channel_info))
7402     {
7403         const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7404         KickChannelUser(kicker, channel, chanserv, reason);
7405     }
7406
7407     if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7408         uData->seen = now;
7409 }
7410
7411 static int
7412 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7413 {
7414     struct chanData *cData;
7415
7416     if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7417         return 0;
7418
7419     cData = channel->channel_info;
7420     if(bad_topic(channel, user, channel->topic))
7421     {
7422         send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7423         if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7424             SetChannelTopic(channel, chanserv, old_topic, 1);
7425         else if(cData->topic)
7426             SetChannelTopic(channel, chanserv, cData->topic, 1);
7427         return 1;
7428     }
7429     /* With topicsnarf, grab the topic and save it as the default topic. */
7430     if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7431     {
7432         free(cData->topic);
7433         cData->topic = strdup(channel->topic);
7434     }
7435     return 0;
7436 }
7437
7438 static void
7439 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7440 {
7441     struct mod_chanmode *bounce = NULL;
7442     unsigned int bnc, ii;
7443     char deopped = 0;
7444
7445     if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7446         return;
7447
7448     if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7449        && mode_lock_violated(&channel->channel_info->modes, change))
7450     {
7451         char correct[MAXLEN];
7452         bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7453         mod_chanmode_format(&channel->channel_info->modes, correct);
7454         send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7455     }
7456     for(ii = bnc = 0; ii < change->argc; ++ii)
7457     {
7458         if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7459         {
7460             const struct userNode *victim = change->args[ii].u.member->user;
7461             if(!protect_user(victim, user, channel->channel_info))
7462                 continue;
7463             if(!bounce)
7464                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7465             if(!deopped)
7466             {
7467                 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7468                 bounce->args[bnc].u.member = GetUserMode(channel, user);
7469                 if(bounce->args[bnc].u.member)
7470                     bnc++;
7471                 deopped = 1;
7472             }
7473             bounce->args[bnc].mode = MODE_CHANOP;
7474             bounce->args[bnc].u.member = change->args[ii].u.member;
7475             bnc++;
7476             send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7477         }
7478         else if(change->args[ii].mode & MODE_CHANOP)
7479         {
7480             const struct userNode *victim = change->args[ii].u.member->user;
7481             if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7482                 continue;
7483             if(!bounce)
7484                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7485             bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7486             bounce->args[bnc].u.member = change->args[ii].u.member;
7487             bnc++;
7488         }
7489         else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7490         {
7491             const char *ban = change->args[ii].u.hostmask;
7492             if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7493                 continue;
7494             if(!bounce)
7495                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7496             bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7497             bounce->args[bnc].u.hostmask = strdup(ban);
7498             bnc++;
7499             send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7500         }
7501     }
7502     if(bounce)
7503     {
7504         if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7505             mod_chanmode_announce(chanserv, channel, bounce);
7506         for(ii = 0; ii < change->argc; ++ii)
7507             if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7508                 free((char*)bounce->args[ii].u.hostmask);
7509         mod_chanmode_free(bounce);
7510     }
7511 }
7512
7513 static void
7514 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7515 {
7516     struct chanNode *channel;
7517     struct banData *bData;
7518     struct mod_chanmode change;
7519     unsigned int ii, jj;
7520     char kick_reason[MAXLEN];
7521
7522     mod_chanmode_init(&change);
7523     change.argc = 1;
7524     change.args[0].mode = MODE_BAN;
7525     for(ii = 0; ii < user->channels.used; ++ii)
7526     {
7527         channel = user->channels.list[ii]->channel;
7528         /* Need not check for bans if they're opped or voiced. */
7529         if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7530             continue;
7531         /* Need not check for bans unless channel registration is active. */
7532         if(!channel->channel_info || IsSuspended(channel->channel_info))
7533             continue;
7534         /* Look for a matching ban already on the channel. */
7535         for(jj = 0; jj < channel->banlist.used; ++jj)
7536             if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7537                 break;
7538         /* Need not act if we found one. */
7539         if(jj < channel->banlist.used)
7540             continue;
7541         /* Look for a matching ban in this channel. */
7542         for(bData = channel->channel_info->bans; bData; bData = bData->next)
7543         {
7544             if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7545                 continue;
7546             change.args[0].u.hostmask = bData->mask;
7547             mod_chanmode_announce(chanserv, channel, &change);
7548             sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7549             KickChannelUser(user, channel, chanserv, kick_reason);
7550             bData->triggered = now;
7551             break; /* we don't need to check any more bans in the channel */
7552         }
7553     }
7554 }
7555
7556 static void handle_rename(struct handle_info *handle, const char *old_handle)
7557 {
7558     struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7559
7560     if(dnr)
7561     {
7562         dict_remove2(handle_dnrs, old_handle, 1);
7563         safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7564         dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7565     }
7566 }
7567
7568 static void
7569 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7570 {
7571     struct userNode *h_user;
7572
7573     if(handle->channels)
7574     {
7575         for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7576             send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7577
7578         while(handle->channels)
7579             del_channel_user(handle->channels, 1);
7580     }
7581 }
7582
7583 static void
7584 handle_server_link(UNUSED_ARG(struct server *server))
7585 {
7586     struct chanData *cData;
7587
7588     for(cData = channelList; cData; cData = cData->next)
7589     {
7590         if(!IsSuspended(cData))
7591             cData->may_opchan = 1;
7592         if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7593            && !cData->channel->join_flooded
7594            && ((cData->channel->limit - cData->channel->members.used)
7595                < chanserv_conf.adjust_threshold))
7596         {
7597             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7598             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7599         }
7600     }
7601 }
7602
7603 static void
7604 chanserv_conf_read(void)
7605 {
7606     dict_t conf_node;
7607     const char *str;
7608     char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7609     struct mod_chanmode *change;
7610     struct string_list *strlist;
7611     struct chanNode *chan;
7612     unsigned int ii;
7613
7614     if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7615     {
7616         log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7617         return;
7618     }
7619     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7620         UnlockChannel(chanserv_conf.support_channels.list[ii]);
7621     chanserv_conf.support_channels.used = 0;
7622     if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7623     {
7624         for(ii = 0; ii < strlist->used; ++ii)
7625         {
7626             const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7627             if(!str2)
7628                 str2 = "+nt";
7629             chan = AddChannel(strlist->list[ii], now, str2, NULL);
7630             LockChannel(chan);
7631             channelList_append(&chanserv_conf.support_channels, chan);
7632         }
7633     }
7634     else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7635     {
7636         const char *str2;
7637         str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7638         if(!str2)
7639             str2 = "+nt";
7640         chan = AddChannel(str, now, str2, NULL);
7641         LockChannel(chan);
7642         channelList_append(&chanserv_conf.support_channels, chan);
7643     }
7644     str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7645     chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7646     str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7647     chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7648     str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7649     chanserv_conf.greeting_length = str ? atoi(str) : 200;
7650     str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7651     chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7652     str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7653     chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7654     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7655     chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7656     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7657     chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7658     str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7659     chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7660     str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7661     chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7662     str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7663     chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7664     str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7665     chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7666     str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7667     chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7668     str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7669     chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7670     str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7671     if(chanserv && str)
7672         NickChange(chanserv, str, 0);
7673     str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7674     chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7675     str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7676     chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7677     str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7678     chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7679     str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7680     chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7681     str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7682     chanserv_conf.max_owned = str ? atoi(str) : 5;
7683     str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7684     chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7685     str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7686     chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7687     str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7688     chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7689     str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7690     chanserv_conf.new_channel_authed = (str && *str) ? str : NULL;
7691     str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7692     chanserv_conf.new_channel_unauthed = (str && *str) ? str : NULL;
7693     str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7694     chanserv_conf.new_channel_msg = (str && *str) ? str : NULL;
7695     str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7696     if(!str)
7697         str = "+nt";
7698     safestrncpy(mode_line, str, sizeof(mode_line));
7699     ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7700     if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7701        && (change->argc < 2))
7702     {
7703         chanserv_conf.default_modes = *change;
7704         mod_chanmode_free(change);
7705     }
7706     free_string_list(chanserv_conf.set_shows);
7707     strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7708     if(strlist)
7709         strlist = string_list_copy(strlist);
7710     else
7711     {
7712         static const char *list[] = {
7713             /* free form text */
7714             "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7715             /* options based on user level */
7716             "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7717             "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7718             /* multiple choice options */
7719             "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7720             /* binary options */
7721             "DynLimit", "NoDelete", "expire", "Vote",
7722             /* delimiter */
7723             NULL
7724         };
7725         strlist = alloc_string_list(ArrayLength(list)-1);
7726         for(ii=0; list[ii]; ii++)
7727             string_list_append(strlist, strdup(list[ii]));
7728     }
7729     chanserv_conf.set_shows = strlist;
7730     /* We don't look things up now, in case the list refers to options
7731      * defined by modules initialized after this point.  Just mark the
7732      * function list as invalid, so it will be initialized.
7733      */
7734     set_shows_list.used = 0;
7735     free_string_list(chanserv_conf.eightball);
7736     strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7737     if(strlist)
7738     {
7739         strlist = string_list_copy(strlist);
7740     }
7741     else
7742     {
7743         strlist = alloc_string_list(4);
7744         string_list_append(strlist, strdup("Yes."));
7745         string_list_append(strlist, strdup("No."));
7746         string_list_append(strlist, strdup("Maybe so."));
7747     }
7748     chanserv_conf.eightball = strlist;
7749     free_string_list(chanserv_conf.old_ban_names);
7750     strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7751     if(strlist)
7752         strlist = string_list_copy(strlist);
7753     else
7754         strlist = alloc_string_list(2);
7755     chanserv_conf.old_ban_names = strlist;
7756     str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7757     off_channel = str ? atoi(str) : 0;
7758 }
7759
7760 static void
7761 chanserv_note_type_read(const char *key, struct record_data *rd)
7762 {
7763     dict_t obj;
7764     struct note_type *ntype;
7765     const char *str;
7766
7767     if(!(obj = GET_RECORD_OBJECT(rd)))
7768     {
7769         log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7770         return;
7771     }
7772     if(!(ntype = chanserv_create_note_type(key)))
7773     {
7774         log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7775         return;
7776     }
7777
7778     /* Figure out set access */
7779     if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7780     {
7781         ntype->set_access_type = NOTE_SET_PRIVILEGED;
7782         ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7783     }
7784     else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7785     {
7786         ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7787         ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7788     }
7789     else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7790     {
7791         ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7792     }
7793     else
7794     {
7795         log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7796         ntype->set_access_type = NOTE_SET_PRIVILEGED;
7797         ntype->set_access.min_opserv = 0;
7798     }
7799
7800     /* Figure out visibility */
7801     if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7802         ntype->visible_type = NOTE_VIS_PRIVILEGED;
7803     else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7804         ntype->visible_type = NOTE_VIS_PRIVILEGED;
7805     else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7806         ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7807     else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7808         ntype->visible_type = NOTE_VIS_ALL;
7809     else
7810         ntype->visible_type = NOTE_VIS_PRIVILEGED;
7811
7812     str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7813     ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7814 }
7815
7816 static void
7817 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7818 {
7819     struct vote_option *vOpt;
7820     char *str;
7821     
7822     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7823     {
7824         log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
7825         return;
7826     }
7827     
7828     vOpt = calloc(1, sizeof(*vOpt));
7829     vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
7830     str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
7831     vOpt->voted = str ? atoi(str) : 0;
7832     vOpt->option_id = str ? atoi(key) : 0;
7833     vOpt->option_str = strdup(key);
7834     dict_insert(chan->vote_options,vOpt->option_str,vOpt);
7835 }
7836
7837 static void
7838 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7839 {
7840     struct handle_info *handle;
7841     struct userData *uData;
7842     char *seen, *inf, *flags, *voted, *votefor;
7843     unsigned long last_seen;
7844     unsigned short access_level;
7845
7846     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7847     {
7848         log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7849         return;
7850     }
7851
7852     access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7853     if(access_level > UL_OWNER)
7854     {
7855         log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7856         return;
7857     }
7858
7859     inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7860     seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7861     last_seen = seen ? strtoul(seen, NULL, 0) : now;
7862     flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7863     voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
7864     votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
7865     handle = get_handle_info(key);
7866     if(!handle)
7867     {
7868         log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7869         return;
7870     }
7871
7872     uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7873     uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7874     if(chan->vote) {
7875         uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
7876         uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
7877     } else {
7878         uData->voted = 0;
7879         uData->votefor = 0;
7880     }
7881 }
7882
7883 static void
7884 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7885 {
7886     char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7887     unsigned long set_time, triggered_time, expires_time;
7888
7889     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7890     {
7891         log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7892         return;
7893     }
7894
7895     set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7896     triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7897     s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7898     s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7899     owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7900     reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7901     if (!reason || !owner)
7902         return;
7903
7904     set_time = set ? strtoul(set, NULL, 0) : now;
7905     triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7906     if(s_expires)
7907         expires_time = strtoul(s_expires, NULL, 0);
7908     else if(s_duration)
7909         expires_time = set_time + atoi(s_duration);
7910     else
7911         expires_time = 0;
7912
7913     if(!reason || (expires_time && (expires_time < now)))
7914         return;
7915
7916     add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7917 }
7918
7919 static struct suspended *
7920 chanserv_read_suspended(dict_t obj)
7921 {
7922     struct suspended *suspended = calloc(1, sizeof(*suspended));
7923     char *str;
7924     dict_t previous;
7925
7926     str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7927     suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7928     str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7929     suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7930     str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7931     suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7932     suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7933     suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7934     previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7935     suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7936     return suspended;
7937 }
7938
7939 static int
7940 chanserv_channel_read(const char *key, struct record_data *hir)
7941 {
7942     struct suspended *suspended;
7943     struct mod_chanmode *modes;
7944     struct chanNode *cNode;
7945     struct chanData *cData;
7946     struct dict *channel, *obj;
7947     char *str, *argv[10];
7948     dict_iterator_t it;
7949     unsigned int argc;
7950
7951     channel = hir->d.object;
7952
7953     str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7954     if(!str)
7955         str = "<unknown>";
7956     cNode = AddChannel(key, now, NULL, NULL);
7957     if(!cNode)
7958     {
7959         log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7960         return 0;
7961     }
7962     cData = register_channel(cNode, str);
7963     if(!cData)
7964     {
7965         log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7966         return 0;
7967     }
7968
7969     if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7970     {
7971         enum levelOption lvlOpt;
7972         enum charOption chOpt;
7973
7974         if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7975             cData->flags = atoi(str);
7976
7977         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7978         {
7979             str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7980             if(str)
7981                 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7982             else if(levelOptions[lvlOpt].old_flag)
7983             {
7984                 if(cData->flags & levelOptions[lvlOpt].old_flag)
7985                     cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7986                 else
7987                     cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7988             }
7989         }
7990
7991         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7992         {
7993             if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7994                 continue;
7995             cData->chOpts[chOpt] = str[0];
7996         }
7997     }
7998     else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7999     {
8000         enum levelOption lvlOpt;
8001         enum charOption chOpt;
8002         unsigned int count;
8003
8004         cData->flags = base64toint(str, 5);
8005         count = strlen(str += 5);
8006         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8007         {
8008             unsigned short lvl;
8009             if(levelOptions[lvlOpt].old_flag)
8010             {
8011                 if(cData->flags & levelOptions[lvlOpt].old_flag)
8012                     lvl = levelOptions[lvlOpt].flag_value;
8013                 else
8014                     lvl = levelOptions[lvlOpt].default_value;
8015             }
8016             else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8017             {
8018             case 'c': lvl = UL_COOWNER; break;
8019             case 'm': lvl = UL_MASTER; break;
8020             case 'n': lvl = UL_OWNER+1; break;
8021             case 'o': lvl = UL_OP; break;
8022             case 'p': lvl = UL_PEON; break;
8023             case 'w': lvl = UL_OWNER; break;
8024             default: lvl = 0; break;
8025             }
8026             cData->lvlOpts[lvlOpt] = lvl;
8027         }
8028         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8029             cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8030     }
8031
8032     if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8033     {
8034         cData->expiry = atoi(str);
8035         if(cData->expiry > 0) {
8036             if(cData->expiry > now) {
8037                 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8038             } else {
8039                 timeq_add(1, chanserv_expire_channel, cData);
8040             }
8041         }
8042     } else {
8043         cData->expiry = 0;
8044     }
8045
8046     if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8047     {
8048         suspended = chanserv_read_suspended(obj);
8049         cData->suspended = suspended;
8050         suspended->cData = cData;
8051         /* We could use suspended->expires and suspended->revoked to
8052          * set the CHANNEL_SUSPENDED flag, but we don't. */
8053     }
8054     else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8055     {
8056         suspended = calloc(1, sizeof(*suspended));
8057         suspended->issued = 0;
8058         suspended->revoked = 0;
8059         suspended->suspender = strdup(str);
8060         str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8061         suspended->expires = str ? atoi(str) : 0;
8062         str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8063         suspended->reason = strdup(str ? str : "No reason");
8064         suspended->previous = NULL;
8065         cData->suspended = suspended;
8066         suspended->cData = cData;
8067     }
8068     else
8069     {
8070         cData->flags &= ~CHANNEL_SUSPENDED;
8071         suspended = NULL; /* to squelch a warning */
8072     }
8073
8074     if(IsSuspended(cData)) {
8075         if(suspended->expires > now)
8076             timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8077         else if(suspended->expires)
8078             cData->flags &= ~CHANNEL_SUSPENDED;
8079     }
8080
8081     if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8082         struct mod_chanmode change;
8083         mod_chanmode_init(&change);
8084         change.argc = 1;
8085         change.args[0].mode = MODE_CHANOP;
8086         change.args[0].u.member = AddChannelUser(chanserv, cNode);
8087         mod_chanmode_announce(chanserv, cNode, &change);
8088     }
8089
8090     str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8091     cData->registered = str ? strtoul(str, NULL, 0) : now;
8092     str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8093     cData->visited = str ? strtoul(str, NULL, 0) : now;
8094     str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8095     cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8096     str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8097     cData->max = str ? atoi(str) : 0;
8098     str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8099     cData->max_time = str ? atoi(str) : 0;
8100     str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8101     cData->greeting = str ? strdup(str) : NULL;
8102     str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8103     cData->user_greeting = str ? strdup(str) : NULL;
8104     str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8105     cData->topic_mask = str ? strdup(str) : NULL;
8106     str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8107     cData->topic = str ? strdup(str) : NULL;
8108
8109     str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8110     if(str) {
8111         cData->vote = str ? strdup(str) : NULL;
8112         dict_delete(cData->vote_options);
8113         cData->vote_options = dict_new();
8114         dict_set_free_data(cData->vote_options, free_vote_options);
8115         str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8116         cData->vote_start = str ? atoi(str) : 0;
8117         obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8118         for(it = dict_first(obj); it; it = iter_next(it)) {
8119             vote_option_read_helper(iter_key(it), iter_data(it), cData);
8120         }
8121     }
8122
8123     if(!IsSuspended(cData)
8124        && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8125        && (argc = split_line(str, 0, ArrayLength(argv), argv))
8126        && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8127         cData->modes = *modes;
8128         if(off_channel > 0)
8129           cData->modes.modes_set |= MODE_REGISTERED;
8130         if(cData->modes.argc > 1)
8131             cData->modes.argc = 1;
8132         mod_chanmode_announce(chanserv, cNode, &cData->modes);
8133         mod_chanmode_free(modes);
8134     }
8135
8136     obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8137     for(it = dict_first(obj); it; it = iter_next(it))
8138         user_read_helper(iter_key(it), iter_data(it), cData);
8139
8140     if(!cData->users && !IsProtected(cData))
8141     {
8142         log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8143         unregister_channel(cData, "has empty user list.");
8144         return 0;
8145     }
8146
8147     obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8148     for(it = dict_first(obj); it; it = iter_next(it))
8149         ban_read_helper(iter_key(it), iter_data(it), cData);
8150
8151     obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8152     for(it = dict_first(obj); it; it = iter_next(it))
8153     {
8154         struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8155         struct record_data *rd = iter_data(it);
8156         const char *note, *setter;
8157
8158         if(rd->type != RECDB_OBJECT)
8159         {
8160             log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8161         }
8162         else if(!ntype)
8163         {
8164             log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8165         }
8166         else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8167         {
8168             log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8169         }
8170         else
8171         {
8172             setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8173             if(!setter) setter = "<unknown>";
8174             chanserv_add_channel_note(cData, ntype, setter, note);
8175         }
8176     }
8177
8178     return 0;
8179 }
8180
8181 static void
8182 chanserv_dnr_read(const char *key, struct record_data *hir)
8183 {
8184     const char *setter, *reason, *str;
8185     struct do_not_register *dnr;
8186     unsigned long expiry;
8187
8188     setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8189     if(!setter)
8190     {
8191         log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8192         return;
8193     }
8194     reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8195     if(!reason)
8196     {
8197         log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8198         return;
8199     }
8200     str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8201     expiry = str ? strtoul(str, NULL, 0) : 0;
8202     if(expiry && expiry <= now)
8203         return;
8204     dnr = chanserv_add_dnr(key, setter, expiry, reason);
8205     if(!dnr)
8206         return;
8207     str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8208     if(str)
8209         dnr->set = atoi(str);
8210     else
8211         dnr->set = 0;
8212 }
8213
8214 static int
8215 chanserv_saxdb_read(struct dict *database)
8216 {
8217     struct dict *section;
8218     dict_iterator_t it;
8219
8220     if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8221         for(it = dict_first(section); it; it = iter_next(it))
8222             chanserv_note_type_read(iter_key(it), iter_data(it));
8223
8224     if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8225         for(it = dict_first(section); it; it = iter_next(it))
8226             chanserv_channel_read(iter_key(it), iter_data(it));
8227
8228     if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8229         for(it = dict_first(section); it; it = iter_next(it))
8230             chanserv_dnr_read(iter_key(it), iter_data(it));
8231
8232     return 0;
8233 }
8234
8235 static int
8236 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8237 {
8238     int high_present = 0;
8239     saxdb_start_record(ctx, KEY_USERS, 1);
8240     for(; uData; uData = uData->next)
8241     {
8242         if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8243             high_present = 1;
8244         saxdb_start_record(ctx, uData->handle->handle, 0);
8245         saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8246         saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8247         if(uData->flags)
8248             saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8249         if(uData->channel->vote && uData->voted)
8250             saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8251         if(uData->channel->vote && uData->votefor)
8252             saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8253         if(uData->info)
8254             saxdb_write_string(ctx, KEY_INFO, uData->info);
8255         saxdb_end_record(ctx);
8256     }
8257     saxdb_end_record(ctx);
8258     return high_present;
8259 }
8260
8261 static void
8262 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8263 {
8264     if(!bData)
8265         return;
8266     saxdb_start_record(ctx, KEY_BANS, 1);
8267     for(; bData; bData = bData->next)
8268     {
8269         saxdb_start_record(ctx, bData->mask, 0);
8270         saxdb_write_int(ctx, KEY_SET, bData->set);
8271         if(bData->triggered)
8272             saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8273         if(bData->expires)
8274             saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8275         if(bData->owner[0])
8276             saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8277         if(bData->reason)
8278             saxdb_write_string(ctx, KEY_REASON, bData->reason);
8279         saxdb_end_record(ctx);
8280     }
8281     saxdb_end_record(ctx);
8282 }
8283
8284 static void
8285 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8286 {
8287     saxdb_start_record(ctx, name, 0);
8288     saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8289     saxdb_write_string(ctx, KEY_REASON, susp->reason);
8290     if(susp->issued)
8291         saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8292     if(susp->expires)
8293         saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8294     if(susp->revoked)
8295         saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8296     if(susp->previous)
8297         chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8298     saxdb_end_record(ctx);
8299 }
8300
8301 static void
8302 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8303 {
8304     char buf[MAXLEN];
8305     int high_present;
8306     enum levelOption lvlOpt;
8307     enum charOption chOpt;
8308     dict_iterator_t it;
8309
8310     saxdb_start_record(ctx, channel->channel->name, 1);
8311
8312     saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8313     saxdb_write_int(ctx, KEY_MAX, channel->max);
8314     saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8315     if(channel->topic)
8316         saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8317     if(channel->registrar)
8318         saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8319     if(channel->greeting)
8320         saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8321     if(channel->user_greeting)
8322         saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8323     if(channel->topic_mask)
8324         saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8325     if(channel->suspended)
8326         chanserv_write_suspended(ctx, "suspended", channel->suspended);
8327     if(channel->expiry)
8328         saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8329
8330     if(channel->vote) {
8331         saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8332         if(channel->vote_start)
8333             saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8334         if (dict_size(channel->vote_options)) {
8335             saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8336             for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8337                 struct vote_option *vOpt = iter_data(it);
8338                 char str[50];
8339                 sprintf(str,"%i",vOpt->option_id);
8340                 saxdb_start_record(ctx, str, 0);
8341                 if(vOpt->voted)
8342                     saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8343                 if(vOpt->name)
8344                     saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8345                 saxdb_end_record(ctx);
8346             }
8347             saxdb_end_record(ctx);
8348         }
8349     }
8350
8351     saxdb_start_record(ctx, KEY_OPTIONS, 0);
8352     saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8353     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8354         saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8355     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8356     {
8357         buf[0] = channel->chOpts[chOpt];
8358         buf[1] = '\0';
8359         saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8360     }
8361     saxdb_end_record(ctx);
8362
8363     if(channel->modes.modes_set || channel->modes.modes_clear)
8364     {
8365         mod_chanmode_format(&channel->modes, buf);
8366         saxdb_write_string(ctx, KEY_MODES, buf);
8367     }
8368
8369     high_present = chanserv_write_users(ctx, channel->users);
8370     chanserv_write_bans(ctx, channel->bans);
8371
8372     if(dict_size(channel->notes))
8373     {
8374         dict_iterator_t it;
8375
8376         saxdb_start_record(ctx, KEY_NOTES, 1);
8377         for(it = dict_first(channel->notes); it; it = iter_next(it))
8378         {
8379             struct note *note = iter_data(it);
8380             saxdb_start_record(ctx, iter_key(it), 0);
8381             saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8382             saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8383             saxdb_end_record(ctx);
8384         }
8385         saxdb_end_record(ctx);
8386     }
8387
8388     if(channel->ownerTransfer)
8389         saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8390     saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8391     saxdb_end_record(ctx);
8392 }
8393
8394 static void
8395 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8396 {
8397     const char *str;
8398
8399     saxdb_start_record(ctx, ntype->name, 0);
8400     switch(ntype->set_access_type)
8401     {
8402     case NOTE_SET_CHANNEL_ACCESS:
8403         saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8404         break;
8405     case NOTE_SET_CHANNEL_SETTER:
8406         saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8407         break;
8408     case NOTE_SET_PRIVILEGED: default:
8409         saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8410         break;
8411     }
8412     switch(ntype->visible_type)
8413     {
8414     case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8415     case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8416     case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8417     }
8418     saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8419     saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8420     saxdb_end_record(ctx);
8421 }
8422
8423 static void
8424 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8425 {
8426     struct do_not_register *dnr;
8427     dict_iterator_t it, next;
8428
8429     for(it = dict_first(dnrs); it; it = next)
8430     {
8431         next = iter_next(it);
8432         dnr = iter_data(it);
8433         if(dnr->expires && dnr->expires <= now)
8434         {
8435             dict_remove(dnrs, iter_key(it));
8436             continue;
8437         }
8438         saxdb_start_record(ctx, dnr->chan_name, 0);
8439         if(dnr->set)
8440             saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8441         if(dnr->expires)
8442             saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8443         saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8444         saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8445         saxdb_end_record(ctx);
8446     }
8447 }
8448
8449 static int
8450 chanserv_saxdb_write(struct saxdb_context *ctx)
8451 {
8452     dict_iterator_t it;
8453     struct chanData *channel;
8454
8455     /* Notes */
8456     saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8457     for(it = dict_first(note_types); it; it = iter_next(it))
8458         chanserv_write_note_type(ctx, iter_data(it));
8459     saxdb_end_record(ctx);
8460
8461     /* DNRs */
8462     saxdb_start_record(ctx, KEY_DNR, 1);
8463     write_dnrs_helper(ctx, handle_dnrs);
8464     write_dnrs_helper(ctx, plain_dnrs);
8465     write_dnrs_helper(ctx, mask_dnrs);
8466     saxdb_end_record(ctx);
8467
8468     /* Channels */
8469     saxdb_start_record(ctx, KEY_CHANNELS, 1);
8470     for(channel = channelList; channel; channel = channel->next)
8471         chanserv_write_channel(ctx, channel);
8472     saxdb_end_record(ctx);
8473
8474     return 0;
8475 }
8476
8477 static void
8478 chanserv_db_cleanup(void) {
8479     unsigned int ii;
8480     unreg_part_func(handle_part);
8481     while(channelList)
8482         unregister_channel(channelList, "terminating.");
8483     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8484         UnlockChannel(chanserv_conf.support_channels.list[ii]);
8485     free(chanserv_conf.support_channels.list);
8486     dict_delete(handle_dnrs);
8487     dict_delete(plain_dnrs);
8488     dict_delete(mask_dnrs);
8489     dict_delete(note_types);
8490     free_string_list(chanserv_conf.eightball);
8491     free_string_list(chanserv_conf.old_ban_names);
8492     free_string_list(chanserv_conf.set_shows);
8493     free(set_shows_list.list);
8494     free(uset_shows_list.list);
8495     while(helperList)
8496     {
8497         struct userData *helper = helperList;
8498         helperList = helperList->next;
8499         free(helper);
8500     }
8501 }
8502
8503 #if defined(GCC_VARMACROS)
8504 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8505 #elif defined(C99_VARMACROS)
8506 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8507 #endif
8508 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8509 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8510
8511 void
8512 init_chanserv(const char *nick)
8513 {
8514     CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8515     conf_register_reload(chanserv_conf_read);
8516
8517     if(nick)
8518     {
8519         reg_server_link_func(handle_server_link);
8520         reg_new_channel_func(handle_new_channel);
8521         reg_join_func(handle_join);
8522         reg_part_func(handle_part);
8523         reg_kick_func(handle_kick);
8524         reg_topic_func(handle_topic);
8525         reg_mode_change_func(handle_mode);
8526         reg_nick_change_func(handle_nick_change);
8527         reg_auth_func(handle_auth);
8528     }
8529
8530     reg_handle_rename_func(handle_rename);
8531     reg_unreg_func(handle_unreg);
8532
8533     handle_dnrs = dict_new();
8534     dict_set_free_data(handle_dnrs, free);
8535     plain_dnrs = dict_new();
8536     dict_set_free_data(plain_dnrs, free);
8537     mask_dnrs = dict_new();
8538     dict_set_free_data(mask_dnrs, free);
8539
8540     reg_svccmd_unbind_func(handle_svccmd_unbind);
8541     chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8542     DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8543     DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8544     DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8545     DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8546     modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8547     modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8548     modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8549     DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8550     DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8551     DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8552     DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8553     DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8554
8555     DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8556     DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8557
8558     DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8559     DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8560     DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8561     DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8562     DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8563
8564     DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8565     DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8566     DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8567     DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8568     DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8569
8570     DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8571     DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8572     DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8573     DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8574
8575     DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8576     DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8577     DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8578     DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8579     DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8580     DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8581     DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8582     DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8583
8584     DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8585     DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8586     DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8587     DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8588     DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8589     DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8590     DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8591     DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8592     DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8593     DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8594     DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8595     DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8596     DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8597     DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8598     DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8599
8600     DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8601     DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8602     DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8603     DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8604     DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8605
8606     DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8607     DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8608
8609     DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8610     DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8611     DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8612     DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8613     DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8614     DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8615     DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8616     DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8617     DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8618     DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8619     DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8620
8621     DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8622     DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8623
8624     DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8625     DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8626     DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8627     DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8628
8629     DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8630     DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8631     DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8632     DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8633     DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8634
8635     DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8636     DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8637     DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8638     DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8639     DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8640     DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8641     
8642     DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8643     DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8644     DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8645     DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8646     DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8647     DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8648     DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8649     DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8650
8651     DEFINE_COMMAND(opme, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
8652     
8653     /* Channel options */
8654     DEFINE_CHANNEL_OPTION(defaulttopic);
8655     DEFINE_CHANNEL_OPTION(topicmask);
8656     DEFINE_CHANNEL_OPTION(greeting);
8657     DEFINE_CHANNEL_OPTION(usergreeting);
8658     DEFINE_CHANNEL_OPTION(modes);
8659     DEFINE_CHANNEL_OPTION(enfops);
8660     DEFINE_CHANNEL_OPTION(giveops);
8661     DEFINE_CHANNEL_OPTION(protect);
8662     DEFINE_CHANNEL_OPTION(enfmodes);
8663     DEFINE_CHANNEL_OPTION(enftopic);
8664     DEFINE_CHANNEL_OPTION(pubcmd);
8665     DEFINE_CHANNEL_OPTION(givevoice);
8666     DEFINE_CHANNEL_OPTION(userinfo);
8667     DEFINE_CHANNEL_OPTION(dynlimit);
8668     DEFINE_CHANNEL_OPTION(topicsnarf);
8669     DEFINE_CHANNEL_OPTION(vote);
8670     DEFINE_CHANNEL_OPTION(nodelete);
8671     DEFINE_CHANNEL_OPTION(toys);
8672     DEFINE_CHANNEL_OPTION(setters);
8673     DEFINE_CHANNEL_OPTION(topicrefresh);
8674     DEFINE_CHANNEL_OPTION(ctcpusers);
8675     DEFINE_CHANNEL_OPTION(ctcpreaction);
8676     DEFINE_CHANNEL_OPTION(inviteme);
8677     DEFINE_CHANNEL_OPTION(unreviewed);
8678     modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8679     modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8680     modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8681     if(off_channel > 1)
8682         DEFINE_CHANNEL_OPTION(offchannel);
8683     modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8684
8685     /* Alias set topic to set defaulttopic for compatibility. */
8686     modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8687
8688     /* User options */
8689     DEFINE_USER_OPTION(noautoop);
8690     DEFINE_USER_OPTION(autoinvite);
8691     DEFINE_USER_OPTION(info);
8692
8693     /* Alias uset autovoice to uset autoop. */
8694     modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8695
8696     note_types = dict_new();
8697     dict_set_free_data(note_types, chanserv_deref_note_type);
8698     if(nick)
8699     {
8700         const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8701         chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8702         service_register(chanserv)->trigger = '!';
8703         reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8704     }
8705     saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8706
8707     if(chanserv_conf.channel_expire_frequency)
8708         timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8709
8710     if(chanserv_conf.dnr_expire_frequency)
8711         timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8712
8713     if(chanserv_conf.refresh_period)
8714     {
8715         unsigned long next_refresh;
8716         next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8717         timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8718     }
8719
8720     reg_exit_func(chanserv_db_cleanup);
8721     message_register_table(msgtab);
8722 }