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