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