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