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