Fix DNR expiration and expiration-based DNR searches.
[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, next;
1401     struct do_not_register *dnr;
1402
1403     for(it = dict_first(handle_dnrs); it; it = next)
1404     {
1405         dnr = iter_data(it);
1406         next = iter_next(it);
1407         if(dnr->expires && dnr->expires <= now)
1408             dict_remove(handle_dnrs, dnr->chan_name + 1);
1409     }
1410     for(it = dict_first(plain_dnrs); it; it = next)
1411     {
1412         dnr = iter_data(it);
1413         next = iter_next(it);
1414         if(dnr->expires && dnr->expires <= now)
1415             dict_remove(plain_dnrs, dnr->chan_name + 1);
1416     }
1417     for(it = dict_first(mask_dnrs); it; it = next)
1418     {
1419         dnr = iter_data(it);
1420         next = iter_next(it);
1421         if(dnr->expires && dnr->expires <= now)
1422             dict_remove(mask_dnrs, dnr->chan_name + 1);
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 < search->min_expires)
1738              || (search->max_expires
1739                  && ((dnr->expires == 0)
1740                      || (dnr->expires > search->max_expires)))
1741              || (search->chan_mask
1742                  && !match_ircglob(dnr->chan_name, search->chan_mask))
1743              || (search->setter_mask
1744                  && !match_ircglob(dnr->setter, search->setter_mask))
1745              || (search->reason_mask
1746                  && !match_ircglob(dnr->reason, search->reason_mask)));
1747 }
1748
1749 static struct dnr_search *
1750 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1751 {
1752     struct dnr_search *discrim;
1753     unsigned int ii;
1754
1755     discrim = calloc(1, sizeof(*discrim));
1756     discrim->source = user;
1757     discrim->chan_mask = NULL;
1758     discrim->setter_mask = NULL;
1759     discrim->reason_mask = NULL;
1760     discrim->max_set = INT_MAX;
1761     discrim->limit = 50;
1762
1763     for(ii=0; ii<argc; ++ii)
1764     {
1765         if(ii == argc - 1)
1766         {
1767             reply("MSG_MISSING_PARAMS", argv[ii]);
1768             goto fail;
1769         }
1770         else if(0 == irccasecmp(argv[ii], "channel"))
1771         {
1772             discrim->chan_mask = argv[++ii];
1773         }
1774         else if(0 == irccasecmp(argv[ii], "setter"))
1775         {
1776             discrim->setter_mask = argv[++ii];
1777         }
1778         else if(0 == irccasecmp(argv[ii], "reason"))
1779         {
1780             discrim->reason_mask = argv[++ii];
1781         }
1782         else if(0 == irccasecmp(argv[ii], "limit"))
1783         {
1784             discrim->limit = strtoul(argv[++ii], NULL, 0);
1785         }
1786         else if(0 == irccasecmp(argv[ii], "set"))
1787         {
1788             const char *cmp = argv[++ii];
1789             if(cmp[0] == '<') {
1790                 if(cmp[1] == '=')
1791                     discrim->min_set = now - ParseInterval(cmp + 2);
1792                 else
1793                     discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1794             } else if(cmp[0] == '=') {
1795                 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1796             } else if(cmp[0] == '>') {
1797                 if(cmp[1] == '=')
1798                     discrim->max_set = now - ParseInterval(cmp + 2);
1799                 else
1800                     discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1801             } else {
1802                 discrim->max_set = now - (ParseInterval(cmp) - 1);
1803             }
1804         }
1805         else if(0 == irccasecmp(argv[ii], "expires"))
1806         {
1807             const char *cmp = argv[++ii];
1808             if(cmp[0] == '<') {
1809                 if(cmp[1] == '=')
1810                     discrim->max_expires = now + ParseInterval(cmp + 2);
1811                 else
1812                     discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1813             } else if(cmp[0] == '=') {
1814                 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1815             } else if(cmp[0] == '>') {
1816                 if(cmp[1] == '=')
1817                     discrim->min_expires = now + ParseInterval(cmp + 2);
1818                 else
1819                     discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1820             } else {
1821                 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1822             }
1823         }
1824         else
1825         {
1826             reply("MSG_INVALID_CRITERIA", argv[ii]);
1827             goto fail;
1828         }
1829     }
1830     return discrim;
1831
1832   fail:
1833     free(discrim);
1834     return NULL;
1835 }
1836
1837 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1838
1839 static unsigned int
1840 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1841 {
1842     struct do_not_register *dnr;
1843     dict_iterator_t next;
1844     dict_iterator_t it;
1845     unsigned int count;
1846     int target_fixed;
1847
1848     /* Initialize local variables. */
1849     count = 0;
1850     target_fixed = 0;
1851     if(discrim->chan_mask)
1852     {
1853         int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1854         if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1855             target_fixed = 1;
1856     }
1857
1858     if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1859     {
1860         /* Check against account-based DNRs. */
1861         dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1862         if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1863             dsf(dnr, data);
1864     }
1865     else if(target_fixed)
1866     {
1867         /* Check against channel-based DNRs. */
1868         dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
1869         if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1870             dsf(dnr, data);
1871     }
1872     else
1873     {
1874         /* Exhaustively search account DNRs. */
1875         for(it = dict_first(handle_dnrs); it; it = next)
1876         {
1877             next = iter_next(it);
1878             dnr = iter_data(it);
1879             if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1880                 break;
1881         }
1882
1883         /* Do the same for channel DNRs. */
1884         for(it = dict_first(plain_dnrs); it; it = next)
1885         {
1886             next = iter_next(it);
1887             dnr = iter_data(it);
1888             if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1889                 break;
1890         }
1891
1892         /* Do the same for wildcarded channel DNRs. */
1893         for(it = dict_first(mask_dnrs); it; it = next)
1894         {
1895             next = iter_next(it);
1896             dnr = iter_data(it);
1897             if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1898                 break;
1899         }
1900     }
1901     return count;
1902 }
1903
1904 static int
1905 dnr_remove_func(struct do_not_register *match, void *extra)
1906 {
1907     struct userNode *user;
1908     char *chan_name;
1909
1910     chan_name = alloca(strlen(match->chan_name) + 1);
1911     strcpy(chan_name, match->chan_name);
1912     user = extra;
1913     if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1914        || dict_remove(plain_dnrs, chan_name)
1915        || dict_remove(mask_dnrs, chan_name))
1916     {
1917         send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
1918     }
1919     return 0;
1920 }
1921
1922 static int
1923 dnr_count_func(struct do_not_register *match, void *extra)
1924 {
1925     return 0; (void)match; (void)extra;
1926 }
1927
1928 static MODCMD_FUNC(cmd_dnrsearch)
1929 {
1930     struct dnr_search *discrim;
1931     dnr_search_func action;
1932     struct svccmd *subcmd;
1933     unsigned int matches;
1934     char buf[MAXLEN];
1935
1936     sprintf(buf, "dnrsearch %s", argv[1]);
1937     subcmd = dict_find(cmd->parent->commands, buf, NULL);
1938     if(!subcmd)
1939     {
1940         reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1941         return 0;
1942     }
1943     if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
1944         return 0;
1945     if(!irccasecmp(argv[1], "print"))
1946         action = dnr_print_func;
1947     else if(!irccasecmp(argv[1], "remove"))
1948         action = dnr_remove_func;
1949     else if(!irccasecmp(argv[1], "count"))
1950         action = dnr_count_func;
1951     else
1952     {
1953         reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1954         return 0;
1955     }
1956
1957     discrim = dnr_search_create(user, cmd, argc-2, argv+2);
1958     if(!discrim)
1959         return 0;
1960
1961     if(action == dnr_print_func)
1962         reply("CSMSG_DNR_SEARCH_RESULTS");
1963     matches = dnr_search(discrim, action, user);
1964     if(matches)
1965         reply("MSG_MATCH_COUNT", matches);
1966     else
1967         reply("MSG_NO_MATCHES");
1968     free(discrim);
1969     return 1;
1970 }
1971
1972 unsigned int
1973 chanserv_get_owned_count(struct handle_info *hi)
1974 {
1975     struct userData *cList;
1976     unsigned int owned;
1977
1978     for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1979         if(cList->access == UL_OWNER)
1980             owned++;
1981     return owned;
1982 }
1983
1984 static CHANSERV_FUNC(cmd_register)
1985 {
1986     struct handle_info *handle;
1987     struct chanData *cData;
1988     struct modeNode *mn;
1989     char reason[MAXLEN];
1990     char *chan_name;
1991     unsigned int new_channel, force=0;
1992     struct do_not_register *dnr;
1993
1994     if(channel)
1995     {
1996         if(channel->channel_info)
1997         {
1998             reply("CSMSG_ALREADY_REGGED", channel->name);
1999             return 0;
2000         }
2001
2002         if(channel->bad_channel)
2003         {
2004             reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2005             return 0;
2006         }
2007
2008         if(!IsHelping(user)
2009            && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2010         {
2011             reply("CSMSG_MUST_BE_OPPED", channel->name);
2012             return 0;
2013         }
2014
2015         new_channel = 0;
2016         chan_name = channel->name;
2017     }
2018     else
2019     {
2020         if((argc < 2) || !IsChannelName(argv[1]))
2021         {
2022             reply("MSG_NOT_CHANNEL_NAME");
2023             return 0;
2024         }
2025
2026         if(opserv_bad_channel(argv[1]))
2027         {
2028             reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2029             return 0;
2030         }
2031
2032         new_channel = 1;
2033         chan_name = argv[1];
2034     }
2035
2036     if(argc >= (new_channel+2))
2037     {
2038         if(!IsHelping(user))
2039         {
2040             reply("CSMSG_PROXY_FORBIDDEN");
2041             return 0;
2042         }
2043
2044         if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2045             return 0;
2046         force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2047         dnr = chanserv_is_dnr(chan_name, handle);
2048     }
2049     else
2050     {
2051         handle = user->handle_info;
2052         dnr = chanserv_is_dnr(chan_name, handle);
2053     }
2054     if(dnr && !force)
2055     {
2056         if(!IsHelping(user))
2057             reply("CSMSG_DNR_CHANNEL", chan_name);
2058         else
2059             chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2060         return 0;
2061     }
2062
2063     if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2064     {
2065         reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2066         return 0;
2067     }
2068
2069     if(new_channel)
2070         channel = AddChannel(argv[1], now, NULL, NULL);
2071
2072     cData = register_channel(channel, user->handle_info->handle);
2073     scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2074     cData->modes = chanserv_conf.default_modes;
2075     if(off_channel > 0)
2076         cData->modes.modes_set |= MODE_REGISTERED;
2077     if (IsOffChannel(cData))
2078     {
2079         mod_chanmode_announce(chanserv, channel, &cData->modes);
2080     }
2081     else
2082     {
2083         struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2084         change->args[change->argc].mode = MODE_CHANOP;
2085         change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2086         change->argc++;
2087         mod_chanmode_announce(chanserv, channel, change);
2088         mod_chanmode_free(change);
2089     }
2090
2091     /* Initialize the channel's max user record. */
2092     cData->max = channel->members.used;
2093
2094     if(handle != user->handle_info)
2095         reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2096     else
2097         reply("CSMSG_REG_SUCCESS", channel->name);
2098
2099     sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2100     global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2101     return 1;
2102 }
2103
2104 static const char *
2105 make_confirmation_string(struct userData *uData)
2106 {
2107     static char strbuf[16];
2108     char *src;
2109     unsigned int accum;
2110
2111     accum = 0;
2112     for(src = uData->handle->handle; *src; )
2113         accum = accum * 31 + toupper(*src++);
2114     if(uData->channel)
2115         for(src = uData->channel->channel->name; *src; )
2116             accum = accum * 31 + toupper(*src++);
2117     sprintf(strbuf, "%08x", accum);
2118     return strbuf;
2119 }
2120
2121 static CHANSERV_FUNC(cmd_unregister)
2122 {
2123     char *name;
2124     char reason[MAXLEN];
2125     struct chanData *cData;
2126     struct userData *uData;
2127
2128     cData = channel->channel_info;
2129     if(!cData)
2130     {
2131         reply("CSMSG_NOT_REGISTERED", channel->name);
2132         return 0;
2133     }
2134
2135     uData = GetChannelUser(cData, user->handle_info);
2136     if(!uData || (uData->access < UL_OWNER))
2137     {
2138         reply("CSMSG_NO_ACCESS");
2139         return 0;
2140     }
2141
2142     if(IsProtected(cData))
2143     {
2144         reply("CSMSG_UNREG_NODELETE", channel->name);
2145         return 0;
2146     }
2147
2148     if(!IsHelping(user))
2149     {
2150         const char *confirm_string;
2151         if(IsSuspended(cData))
2152         {
2153             reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2154             return 0;
2155         }
2156         confirm_string = make_confirmation_string(uData);
2157         if((argc < 2) || strcmp(argv[1], confirm_string))
2158         {
2159             reply("CSMSG_CONFIRM_UNREG", confirm_string);
2160             return 0;
2161         }
2162     }
2163
2164     sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2165     name = strdup(channel->name);
2166     unregister_channel(cData, reason);
2167     reply("CSMSG_UNREG_SUCCESS", name);
2168     free(name);
2169     return 1;
2170 }
2171
2172 static CHANSERV_FUNC(cmd_move)
2173 {
2174     struct mod_chanmode change;
2175     struct chanNode *target;
2176     struct modeNode *mn;
2177     struct userData *uData;
2178     char reason[MAXLEN];
2179     struct do_not_register *dnr;
2180
2181     REQUIRE_PARAMS(2);
2182
2183     if(IsProtected(channel->channel_info))
2184     {
2185         reply("CSMSG_MOVE_NODELETE", channel->name);
2186         return 0;
2187     }
2188
2189     if(!IsChannelName(argv[1]))
2190     {
2191         reply("MSG_NOT_CHANNEL_NAME");
2192         return 0;
2193     }
2194
2195     if(opserv_bad_channel(argv[1]))
2196     {
2197         reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2198         return 0;
2199     }
2200
2201     if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2202     {
2203         for(uData = channel->channel_info->users; uData; uData = uData->next)
2204         {
2205             if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2206             {
2207                 if(!IsHelping(user))
2208                     reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2209                 else
2210                     chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2211                 return 0;
2212             }
2213         }
2214     }
2215
2216     mod_chanmode_init(&change);
2217     if(!(target = GetChannel(argv[1])))
2218     {
2219         target = AddChannel(argv[1], now, NULL, NULL);
2220         if(!IsSuspended(channel->channel_info))
2221             AddChannelUser(chanserv, target);
2222     }
2223     else if(target->channel_info)
2224     {
2225         reply("CSMSG_ALREADY_REGGED", target->name);
2226         return 0;
2227     }
2228     else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2229             && !IsHelping(user))
2230     {
2231         reply("CSMSG_MUST_BE_OPPED", target->name);
2232         return 0;
2233     }
2234     else if(!IsSuspended(channel->channel_info))
2235     {
2236         change.argc = 1;
2237         change.args[0].mode = MODE_CHANOP;
2238         change.args[0].u.member = AddChannelUser(chanserv, target);
2239         mod_chanmode_announce(chanserv, target, &change);
2240     }
2241
2242     if(off_channel > 0)
2243     {
2244         /* Clear MODE_REGISTERED from old channel, add it to new. */
2245         change.argc = 0;
2246         change.modes_clear = MODE_REGISTERED;
2247         mod_chanmode_announce(chanserv, channel, &change);
2248         change.modes_clear = 0;
2249         change.modes_set = MODE_REGISTERED;
2250         mod_chanmode_announce(chanserv, target, &change);
2251     }
2252
2253     /* Move the channel_info to the target channel; it
2254        shouldn't be necessary to clear timeq callbacks
2255        for the old channel. */
2256     target->channel_info = channel->channel_info;
2257     target->channel_info->channel = target;
2258     channel->channel_info = NULL;
2259
2260     reply("CSMSG_MOVE_SUCCESS", target->name);
2261
2262     sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2263     if(!IsSuspended(target->channel_info))
2264     {
2265         char reason2[MAXLEN];
2266         sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2267         DelChannelUser(chanserv, channel, reason2, 0);
2268     }
2269     UnlockChannel(channel);
2270     LockChannel(target);
2271     global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2272     return 1;
2273 }
2274
2275 static void
2276 merge_users(struct chanData *source, struct chanData *target)
2277 {
2278     struct userData *suData, *tuData, *next;
2279     dict_iterator_t it;
2280     dict_t merge;
2281
2282     merge = dict_new();
2283
2284     /* Insert the source's users into the scratch area. */
2285     for(suData = source->users; suData; suData = suData->next)
2286         dict_insert(merge, suData->handle->handle, suData);
2287
2288     /* Iterate through the target's users, looking for
2289        users common to both channels. The lower access is
2290        removed from either the scratch area or target user
2291        list. */
2292     for(tuData = target->users; tuData; tuData = next)
2293     {
2294         struct userData *choice;
2295
2296         next = tuData->next;
2297
2298         /* If a source user exists with the same handle as a target
2299            channel's user, resolve the conflict by removing one. */
2300         suData = dict_find(merge, tuData->handle->handle, NULL);
2301         if(!suData)
2302             continue;
2303
2304         /* Pick the data we want to keep. */
2305         /* If the access is the same, use the later seen time. */
2306         if(suData->access == tuData->access)
2307             choice = (suData->seen > tuData->seen) ? suData : tuData;
2308         else /* Otherwise, keep the higher access level. */
2309             choice = (suData->access > tuData->access) ? suData : tuData;
2310
2311         /* Remove the user that wasn't picked. */
2312         if(choice == tuData)
2313         {
2314             dict_remove(merge, suData->handle->handle);
2315             del_channel_user(suData, 0);
2316         }
2317         else
2318             del_channel_user(tuData, 0);
2319     }
2320
2321     /* Move the remaining users to the target channel. */
2322     for(it = dict_first(merge); it; it = iter_next(it))
2323     {
2324         suData = iter_data(it);
2325
2326         /* Insert the user into the target channel's linked list. */
2327         suData->prev = NULL;
2328         suData->next = target->users;
2329         suData->channel = target;
2330
2331         if(target->users)
2332             target->users->prev = suData;
2333         target->users = suData;
2334
2335         /* Update the user counts for the target channel; the
2336            source counts are left alone. */
2337         target->userCount++;
2338     }
2339
2340     /* Possible to assert (source->users == NULL) here. */
2341     source->users = NULL;
2342     dict_delete(merge);
2343 }
2344
2345 static void
2346 merge_bans(struct chanData *source, struct chanData *target)
2347 {
2348     struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2349
2350     /* Hold on to the original head of the target ban list
2351        to avoid comparing source bans with source bans. */
2352     tFront = target->bans;
2353
2354     /* Perform a totally expensive O(n*m) merge, ick. */
2355     for(sbData = source->bans; sbData; sbData = sNext)
2356     {
2357         /* Flag to track whether the ban's been moved
2358            to the destination yet. */
2359         int moved = 0;
2360
2361         /* Possible to assert (sbData->prev == NULL) here. */
2362         sNext = sbData->next;
2363
2364         for(tbData = tFront; tbData; tbData = tNext)
2365         {
2366             tNext = tbData->next;
2367
2368             /* Perform two comparisons between each source
2369                and target ban, conflicts are resolved by
2370                keeping the broader ban and copying the later
2371                expiration and triggered time. */
2372             if(match_ircglobs(tbData->mask, sbData->mask))
2373             {
2374                 /* There is a broader ban in the target channel that
2375                    overrides one in the source channel; remove the
2376                    source ban and break. */
2377                 if(sbData->expires > tbData->expires)
2378                     tbData->expires = sbData->expires;
2379                 if(sbData->triggered > tbData->triggered)
2380                     tbData->triggered = sbData->triggered;
2381                 del_channel_ban(sbData);
2382                 break;
2383             }
2384             else if(match_ircglobs(sbData->mask, tbData->mask))
2385             {
2386                 /* There is a broader ban in the source channel that
2387                    overrides one in the target channel; remove the
2388                    target ban, fall through and move the source over. */
2389                 if(tbData->expires > sbData->expires)
2390                     sbData->expires = tbData->expires;
2391                 if(tbData->triggered > sbData->triggered)
2392                     sbData->triggered = tbData->triggered;
2393                 if(tbData == tFront)
2394                     tFront = tNext;
2395                 del_channel_ban(tbData);
2396             }
2397
2398             /* Source bans can override multiple target bans, so
2399                we allow a source to run through this loop multiple
2400                times, but we can only move it once. */
2401             if(moved)
2402                 continue;
2403             moved = 1;
2404
2405             /* Remove the source ban from the source ban list. */
2406             if(sbData->next)
2407                 sbData->next->prev = sbData->prev;
2408
2409             /* Modify the source ban's associated channel. */
2410             sbData->channel = target;
2411
2412             /* Insert the ban into the target channel's linked list. */
2413             sbData->prev = NULL;
2414             sbData->next = target->bans;
2415
2416             if(target->bans)
2417                 target->bans->prev = sbData;
2418             target->bans = sbData;
2419
2420             /* Update the user counts for the target channel. */
2421             target->banCount++;
2422         }
2423     }
2424
2425     /* Possible to assert (source->bans == NULL) here. */
2426     source->bans = NULL;
2427 }
2428
2429 static void
2430 merge_data(struct chanData *source, struct chanData *target)
2431 {
2432     /* Use more recent visited and owner-transfer time; use older
2433      * registered time.  Bitwise or may_opchan.  Use higher max.
2434      * Do not touch last_refresh, ban count or user counts.
2435      */
2436     if(source->visited > target->visited)
2437         target->visited = source->visited;
2438     if(source->registered < target->registered)
2439         target->registered = source->registered;
2440     if(source->ownerTransfer > target->ownerTransfer)
2441         target->ownerTransfer = source->ownerTransfer;
2442     if(source->may_opchan)
2443         target->may_opchan = 1;
2444     if(source->max > target->max)
2445         target->max = source->max;
2446 }
2447
2448 static void
2449 merge_channel(struct chanData *source, struct chanData *target)
2450 {
2451     merge_users(source, target);
2452     merge_bans(source, target);
2453     merge_data(source, target);
2454 }
2455
2456 static CHANSERV_FUNC(cmd_merge)
2457 {
2458     struct userData *target_user;
2459     struct chanNode *target;
2460     char reason[MAXLEN];
2461
2462     REQUIRE_PARAMS(2);
2463
2464     /* Make sure the target channel exists and is registered to the user
2465        performing the command. */
2466     if(!(target = GetChannel(argv[1])))
2467     {
2468         reply("MSG_INVALID_CHANNEL");
2469         return 0;
2470     }
2471
2472     if(!target->channel_info)
2473     {
2474         reply("CSMSG_NOT_REGISTERED", target->name);
2475         return 0;
2476     }
2477
2478     if(IsProtected(channel->channel_info))
2479     {
2480         reply("CSMSG_MERGE_NODELETE");
2481         return 0;
2482     }
2483
2484     if(IsSuspended(target->channel_info))
2485     {
2486         reply("CSMSG_MERGE_SUSPENDED");
2487         return 0;
2488     }
2489
2490     if(channel == target)
2491     {
2492         reply("CSMSG_MERGE_SELF");
2493         return 0;
2494     }
2495
2496     target_user = GetChannelUser(target->channel_info, user->handle_info);
2497     if(!target_user || (target_user->access < UL_OWNER))
2498     {
2499         reply("CSMSG_MERGE_NOT_OWNER");
2500         return 0;
2501     }
2502
2503     /* Merge the channel structures and associated data. */
2504     merge_channel(channel->channel_info, target->channel_info);
2505     sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2506     unregister_channel(channel->channel_info, reason);
2507     reply("CSMSG_MERGE_SUCCESS", target->name);
2508     return 1;
2509 }
2510
2511 static CHANSERV_FUNC(cmd_opchan)
2512 {
2513     struct mod_chanmode change;
2514     if(!IsHelping(user) && !channel->channel_info->may_opchan)
2515     {
2516         reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2517         return 0;
2518     }
2519     channel->channel_info->may_opchan = 0;
2520     mod_chanmode_init(&change);
2521     change.argc = 1;
2522     change.args[0].mode = MODE_CHANOP;
2523     change.args[0].u.member = GetUserMode(channel, chanserv);
2524     mod_chanmode_announce(chanserv, channel, &change);
2525     reply("CSMSG_OPCHAN_DONE", channel->name);
2526     return 1;
2527 }
2528
2529 static CHANSERV_FUNC(cmd_adduser)
2530 {
2531     struct userData *actee;
2532     struct userData *actor, *real_actor;
2533     struct handle_info *handle;
2534     unsigned short access, override = 0;
2535
2536     REQUIRE_PARAMS(3);
2537
2538     if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2539     {
2540         reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2541         return 0;
2542     }
2543
2544     access = user_level_from_name(argv[2], UL_OWNER);
2545     if(!access)
2546     {
2547         reply("CSMSG_INVALID_ACCESS", argv[2]);
2548         return 0;
2549     }
2550
2551     actor = GetChannelUser(channel->channel_info, user->handle_info);
2552     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2553
2554     if(actor->access <= access)
2555     {
2556         reply("CSMSG_NO_BUMP_ACCESS");
2557         return 0;
2558     }
2559
2560     /* Trying to add someone with equal/more access? */
2561     if (!real_actor || real_actor->access <= access)
2562         override = CMD_LOG_OVERRIDE;
2563
2564     if(!(handle = modcmd_get_handle_info(user, argv[1])))
2565         return 0;
2566
2567     if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2568     {
2569         reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2570         return 0;
2571     }
2572
2573     actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2574     scan_user_presence(actee, NULL);
2575     reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2576     return 1 | override;
2577 }
2578
2579 static CHANSERV_FUNC(cmd_clvl)
2580 {
2581     struct handle_info *handle;
2582     struct userData *victim;
2583     struct userData *actor, *real_actor;
2584     unsigned short new_access, override = 0;
2585     int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2586
2587     REQUIRE_PARAMS(3);
2588
2589     actor = GetChannelUser(channel->channel_info, user->handle_info);
2590     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2591
2592     if(!(handle = modcmd_get_handle_info(user, argv[1])))
2593         return 0;
2594
2595     if(handle == user->handle_info && !privileged)
2596     {
2597         reply("CSMSG_NO_SELF_CLVL");
2598         return 0;
2599     }
2600
2601     if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2602     {
2603         reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2604         return 0;
2605     }
2606
2607     if(actor->access <= victim->access && !privileged)
2608     {
2609         reply("MSG_USER_OUTRANKED", handle->handle);
2610         return 0;
2611     }
2612
2613     new_access = user_level_from_name(argv[2], UL_OWNER);
2614
2615     if(!new_access)
2616     {
2617         reply("CSMSG_INVALID_ACCESS", argv[2]);
2618         return 0;
2619     }
2620
2621     if(new_access >= actor->access && !privileged)
2622     {
2623         reply("CSMSG_NO_BUMP_ACCESS");
2624         return 0;
2625     }
2626
2627     /* Trying to clvl a equal/higher user? */
2628     if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2629         override = CMD_LOG_OVERRIDE;
2630     /* Trying to clvl someone to equal/higher access? */
2631     if(!real_actor || new_access >= real_actor->access)
2632         override = CMD_LOG_OVERRIDE;
2633     /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2634      * If they lower their own access it's not a big problem.
2635      */
2636
2637     victim->access = new_access;
2638     reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2639     return 1 | override;
2640 }
2641
2642 static CHANSERV_FUNC(cmd_deluser)
2643 {
2644     struct handle_info *handle;
2645     struct userData *victim;
2646     struct userData *actor, *real_actor;
2647     unsigned short access, override = 0;
2648     char *chan_name;
2649
2650     REQUIRE_PARAMS(2);
2651
2652     actor = GetChannelUser(channel->channel_info, user->handle_info);
2653     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2654
2655     if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2656         return 0;
2657
2658     if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2659     {
2660         reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2661         return 0;
2662     }
2663
2664     if(argc > 2)
2665     {
2666         access = user_level_from_name(argv[1], UL_OWNER);
2667         if(!access)
2668         {
2669             reply("CSMSG_INVALID_ACCESS", argv[1]);
2670             return 0;
2671         }
2672         if(access != victim->access)
2673         {
2674             reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2675             return 0;
2676         }
2677     }
2678     else
2679     {
2680         access = victim->access;
2681     }
2682
2683     if((actor->access <= victim->access) && !IsHelping(user))
2684     {
2685         reply("MSG_USER_OUTRANKED", victim->handle->handle);
2686         return 0;
2687     }
2688
2689     /* If people delete themselves it is an override, but they
2690      * could've used deleteme so we don't log it as an override
2691      */
2692     if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2693         override = CMD_LOG_OVERRIDE;
2694
2695     chan_name = strdup(channel->name);
2696     del_channel_user(victim, 1);
2697     reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2698     free(chan_name);
2699     return 1 | override;
2700 }
2701
2702 static int
2703 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2704 {
2705     struct userData *actor, *real_actor, *uData, *next;
2706     unsigned int override = 0;
2707
2708     actor = GetChannelUser(channel->channel_info, user->handle_info);
2709     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2710
2711     if(min_access > max_access)
2712     {
2713         reply("CSMSG_BAD_RANGE", min_access, max_access);
2714         return 0;
2715     }
2716
2717     if((actor->access <= max_access) && !IsHelping(user))
2718     {
2719         reply("CSMSG_NO_ACCESS");
2720         return 0;
2721     }
2722
2723     if(!real_actor || real_actor->access <= max_access)
2724         override = CMD_LOG_OVERRIDE;
2725
2726     for(uData = channel->channel_info->users; uData; uData = next)
2727     {
2728         next = uData->next;
2729
2730         if((uData->access >= min_access)
2731            && (uData->access <= max_access)
2732            && match_ircglob(uData->handle->handle, mask))
2733             del_channel_user(uData, 1);
2734     }
2735
2736     reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2737     return 1 | override;
2738 }
2739
2740 static CHANSERV_FUNC(cmd_mdelowner)
2741 {
2742     return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2743 }
2744
2745 static CHANSERV_FUNC(cmd_mdelcoowner)
2746 {
2747     return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2748 }
2749
2750 static CHANSERV_FUNC(cmd_mdelmaster)
2751 {
2752     return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2753 }
2754
2755 static CHANSERV_FUNC(cmd_mdelop)
2756 {
2757     return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2758 }
2759
2760 static CHANSERV_FUNC(cmd_mdelpeon)
2761 {
2762     return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2763 }
2764
2765 static int
2766 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2767 {
2768     struct banData *bData, *next;
2769     char interval[INTERVALLEN];
2770     unsigned int count;
2771     time_t limit;
2772
2773     count = 0;
2774     limit = now - duration;
2775     for(bData = channel->channel_info->bans; bData; bData = next)
2776     {
2777         next = bData->next;
2778
2779         if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2780             continue;
2781
2782         del_channel_ban(bData);
2783         count++;
2784     }
2785
2786     intervalString(interval, duration, user->handle_info);
2787     send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2788     return 1;
2789 }
2790
2791 static int
2792 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2793 {
2794     struct userData *actor, *uData, *next;
2795     char interval[INTERVALLEN];
2796     unsigned int count;
2797     time_t limit;
2798
2799     actor = GetChannelAccess(channel->channel_info, user->handle_info);
2800     if(min_access > max_access)
2801     {
2802         send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2803         return 0;
2804     }
2805
2806     if(!actor || actor->access <= max_access)
2807     {
2808         send_message(user, chanserv, "CSMSG_NO_ACCESS");
2809         return 0;
2810     }
2811
2812     count = 0;
2813     limit = now - duration;
2814     for(uData = channel->channel_info->users; uData; uData = next)
2815     {
2816         next = uData->next;
2817
2818         if((uData->seen > limit)
2819            || uData->present
2820            || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2821             continue;
2822
2823         if(((uData->access >= min_access) && (uData->access <= max_access))
2824            || (!max_access && (uData->access < actor->access)))
2825         {
2826             del_channel_user(uData, 1);
2827             count++;
2828         }
2829     }
2830
2831     if(!max_access)
2832     {
2833         min_access = 1;
2834         max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2835     }
2836     send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2837     return 1;
2838 }
2839
2840 static CHANSERV_FUNC(cmd_trim)
2841 {
2842     unsigned long duration;
2843     unsigned short min_level, max_level;
2844     int vacation;
2845
2846     REQUIRE_PARAMS(3);
2847
2848     vacation = argc > 3 && !strcmp(argv[3], "vacation");
2849     duration = ParseInterval(argv[2]);
2850     if(duration < 60)
2851     {
2852         reply("CSMSG_CANNOT_TRIM");
2853         return 0;
2854     }
2855
2856     if(!irccasecmp(argv[1], "bans"))
2857     {
2858         cmd_trim_bans(user, channel, duration);
2859         return 1;
2860     }
2861     else if(!irccasecmp(argv[1], "users"))
2862     {
2863         cmd_trim_users(user, channel, 0, 0, duration, vacation);
2864         return 1;
2865     }
2866     else if(parse_level_range(&min_level, &max_level, argv[1]))
2867     {
2868         cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2869         return 1;
2870     }
2871     else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2872     {
2873         cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2874         return 1;
2875     }
2876     else
2877     {
2878         reply("CSMSG_INVALID_TRIM", argv[1]);
2879         return 0;
2880     }
2881 }
2882
2883 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2884    to the user. cmd_all takes advantage of this. */
2885 static CHANSERV_FUNC(cmd_up)
2886 {
2887     struct mod_chanmode change;
2888     struct userData *uData;
2889     const char *errmsg;
2890
2891     mod_chanmode_init(&change);
2892     change.argc = 1;
2893     change.args[0].u.member = GetUserMode(channel, user);
2894     if(!change.args[0].u.member)
2895     {
2896         if(argc)
2897             reply("MSG_CHANNEL_ABSENT", channel->name);
2898         return 0;
2899     }
2900
2901     uData = GetChannelAccess(channel->channel_info, user->handle_info);
2902     if(!uData)
2903     {
2904         if(argc)
2905             reply("CSMSG_GODMODE_UP", argv[0]);
2906         return 0;
2907     }
2908     else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2909     {
2910         change.args[0].mode = MODE_CHANOP;
2911         errmsg = "CSMSG_ALREADY_OPPED";
2912     }
2913     else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2914     {
2915         change.args[0].mode = MODE_VOICE;
2916         errmsg = "CSMSG_ALREADY_VOICED";
2917     }
2918     else
2919     {
2920         if(argc)
2921             reply("CSMSG_NO_ACCESS");
2922         return 0;
2923     }
2924     change.args[0].mode &= ~change.args[0].u.member->modes;
2925     if(!change.args[0].mode)
2926     {
2927         if(argc)
2928             reply(errmsg, channel->name);
2929         return 0;
2930     }
2931     modcmd_chanmode_announce(&change);
2932     return 1;
2933 }
2934
2935 static CHANSERV_FUNC(cmd_down)
2936 {
2937     struct mod_chanmode change;
2938
2939     mod_chanmode_init(&change);
2940     change.argc = 1;
2941     change.args[0].u.member = GetUserMode(channel, user);
2942     if(!change.args[0].u.member)
2943     {
2944         if(argc)
2945             reply("MSG_CHANNEL_ABSENT", channel->name);
2946         return 0;
2947     }
2948
2949     if(!change.args[0].u.member->modes)
2950     {
2951         if(argc)
2952             reply("CSMSG_ALREADY_DOWN", channel->name);
2953         return 0;
2954     }
2955
2956     change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2957     modcmd_chanmode_announce(&change);
2958     return 1;
2959 }
2960
2961 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)
2962 {
2963     struct userData *cList;
2964
2965     for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2966     {
2967         if(IsSuspended(cList->channel)
2968            || IsUserSuspended(cList)
2969            || !GetUserMode(cList->channel->channel, user))
2970             continue;
2971
2972         mcmd(user, cList->channel->channel, 0, NULL, cmd);
2973     }
2974
2975     return 1;
2976 }
2977
2978 static CHANSERV_FUNC(cmd_upall)
2979 {
2980     return cmd_all(CSFUNC_ARGS, cmd_up);
2981 }
2982
2983 static CHANSERV_FUNC(cmd_downall)
2984 {
2985     return cmd_all(CSFUNC_ARGS, cmd_down);
2986 }
2987
2988 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2989 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2990
2991 static int
2992 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)
2993 {
2994     unsigned int ii, valid;
2995     struct userNode *victim;
2996     struct mod_chanmode *change;
2997
2998     change = mod_chanmode_alloc(argc - 1);
2999
3000     for(ii=valid=0; ++ii < argc; )
3001     {
3002         if(!(victim = GetUserH(argv[ii])))
3003             continue;
3004         change->args[valid].mode = mode;
3005         change->args[valid].u.member = GetUserMode(channel, victim);
3006         if(!change->args[valid].u.member)
3007             continue;
3008         if(validate && !validate(user, channel, victim))
3009             continue;
3010         valid++;
3011     }
3012
3013     change->argc = valid;
3014     if(valid < (argc-1))
3015         reply("CSMSG_PROCESS_FAILED");
3016     if(valid)
3017     {
3018         modcmd_chanmode_announce(change);
3019         reply(action, channel->name);
3020     }
3021     mod_chanmode_free(change);
3022     return 1;
3023 }
3024
3025 static CHANSERV_FUNC(cmd_op)
3026 {
3027     return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3028 }
3029
3030 static CHANSERV_FUNC(cmd_deop)
3031 {
3032     return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3033 }
3034
3035 static CHANSERV_FUNC(cmd_voice)
3036 {
3037     return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3038 }
3039
3040 static CHANSERV_FUNC(cmd_devoice)
3041 {
3042     return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3043 }
3044
3045 static int
3046 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3047 {
3048     unsigned int ii;
3049
3050     if(victimCount)
3051         *victimCount = 0;
3052     for(ii=0; ii<channel->members.used; ii++)
3053     {
3054         struct modeNode *mn = channel->members.list[ii];
3055
3056         if(IsService(mn->user))
3057             continue;
3058
3059         if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3060             continue;
3061
3062         if(protect_user(mn->user, user, channel->channel_info))
3063             return 1;
3064
3065         if(victims)
3066             victims[(*victimCount)++] = mn;
3067     }
3068     return 0;
3069 }
3070
3071 static int
3072 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3073 {
3074     struct userNode *victim;
3075     struct modeNode **victims;
3076     unsigned int offset, n, victimCount, duration = 0;
3077     char *reason = "Bye.", *ban, *name;
3078     char interval[INTERVALLEN];
3079
3080     offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3081     REQUIRE_PARAMS(offset);
3082     if(argc > offset)
3083     {
3084         reason = unsplit_string(argv + offset, argc - offset, NULL);
3085         if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3086         {
3087             /* Truncate the reason to a length of TOPICLEN, as
3088                the ircd does; however, leave room for an ellipsis
3089                and the kicker's nick. */
3090             sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3091         }
3092     }
3093
3094     if((victim = GetUserH(argv[1])))
3095     {
3096         victims = alloca(sizeof(victims[0]));
3097         victims[0] = GetUserMode(channel, victim);
3098         /* XXX: The comparison with ACTION_KICK is just because all
3099          * other actions can work on users outside the channel, and we
3100          * want to allow those (e.g.  unbans) in that case.  If we add
3101          * some other ejection action for in-channel users, change
3102          * this too. */
3103         victimCount = victims[0] ? 1 : 0;
3104
3105         if(IsService(victim))
3106         {
3107             reply("MSG_SERVICE_IMMUNE", victim->nick);
3108             return 0;
3109         }
3110
3111         if((action == ACTION_KICK) && !victimCount)
3112         {
3113             reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3114             return 0;
3115         }
3116
3117         if(protect_user(victim, user, channel->channel_info))
3118         {
3119             reply("CSMSG_USER_PROTECTED", victim->nick);
3120             return 0;
3121         }
3122
3123         ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3124         name = victim->nick;
3125     }
3126     else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3127     {
3128         struct handle_info *hi;
3129         char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3130         const char *accountname = argv[1] + 1;
3131
3132         if(!(hi = get_handle_info(accountname)))
3133         {
3134             reply("MSG_HANDLE_UNKNOWN", accountname);
3135             return 0;
3136         }
3137
3138         snprintf(banmask, sizeof(banmask), "*!*@%s.*", hi->handle);
3139         victims = alloca(sizeof(victims[0]) * channel->members.used);
3140
3141         if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3142         {
3143             reply("CSMSG_MASK_PROTECTED", banmask);
3144             return 0;
3145         }
3146
3147         if((action == ACTION_KICK) && (victimCount == 0))
3148         {
3149             reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3150             return 0;
3151         }
3152
3153         name = ban = strdup(banmask);
3154     }
3155     else
3156     {
3157         if(!is_ircmask(argv[1]))
3158         {
3159             reply("MSG_NICK_UNKNOWN", argv[1]);
3160             return 0;
3161         }
3162
3163         victims = alloca(sizeof(victims[0]) * channel->members.used);
3164
3165         if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3166         {
3167             reply("CSMSG_MASK_PROTECTED", argv[1]);
3168             return 0;
3169         }
3170
3171         if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3172         {
3173             reply("CSMSG_LAME_MASK", argv[1]);
3174             return 0;
3175         }
3176
3177         if((action == ACTION_KICK) && (victimCount == 0))
3178         {
3179             reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3180             return 0;
3181         }
3182
3183         name = ban = strdup(argv[1]);
3184     }
3185
3186     /* Truncate the ban in place if necessary; we must ensure
3187        that 'ban' is a valid ban mask before sanitizing it. */
3188     sanitize_ircmask(ban);
3189
3190     if(action & ACTION_ADD_BAN)
3191     {
3192         struct banData *bData, *next;
3193
3194         if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3195         {
3196             reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3197             free(ban);
3198             return 0;
3199         }
3200
3201         if(action & ACTION_ADD_TIMED_BAN)
3202         {
3203             duration = ParseInterval(argv[2]);
3204
3205             if(duration < 15)
3206             {
3207                 reply("CSMSG_DURATION_TOO_LOW");
3208                 free(ban);
3209                 return 0;
3210             }
3211             else if(duration > (86400 * 365 * 2))
3212             {
3213                 reply("CSMSG_DURATION_TOO_HIGH");
3214                 free(ban);
3215                 return 0;
3216             }
3217         }
3218
3219         for(bData = channel->channel_info->bans; bData; bData = next)
3220         {
3221             if(match_ircglobs(bData->mask, ban))
3222             {
3223                 int exact = !irccasecmp(bData->mask, ban);
3224
3225                 /* The ban is redundant; there is already a ban
3226                    with the same effect in place. */
3227                 if(exact)
3228                 {
3229                     if(bData->reason)
3230                         free(bData->reason);
3231                     bData->reason = strdup(reason);
3232                     safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3233                     if(cmd)
3234                         reply("CSMSG_REASON_CHANGE", ban);
3235                     if(!bData->expires)
3236                         goto post_add_ban;
3237                 }
3238                 if(exact && bData->expires)
3239                 {
3240                     int reset = 0;
3241
3242                     /* If the ban matches an existing one exactly,
3243                        extend the expiration time if the provided
3244                        duration is longer. */
3245                     if(duration && ((time_t)(now + duration) > bData->expires))
3246                     {
3247                         bData->expires = now + duration;
3248                         reset = 1;
3249                     }
3250                     else if(!duration)
3251                     {
3252                         bData->expires = 0;
3253                         reset = 1;
3254                     }
3255
3256                     if(reset)
3257                     {
3258                         /* Delete the expiration timeq entry and
3259                            requeue if necessary. */
3260                         timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3261
3262                         if(bData->expires)
3263                             timeq_add(bData->expires, expire_ban, bData);
3264
3265                         if(!cmd)
3266                         {
3267                             /* automated kickban */
3268                         }
3269                         else if(duration)
3270                             reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3271                         else
3272                             reply("CSMSG_BAN_ADDED", name, channel->name);
3273
3274                         goto post_add_ban;
3275                     }
3276                 }
3277                 if(cmd)
3278                     reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3279
3280                 free(ban);
3281                 return 0;
3282             }
3283
3284             next = bData->next;
3285             if(match_ircglobs(ban, bData->mask))
3286             {
3287                 /* The ban we are adding makes previously existing
3288                    bans redundant; silently remove them. */
3289                 del_channel_ban(bData);
3290             }
3291         }
3292
3293         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);
3294         free(ban);
3295         name = ban = strdup(bData->mask);
3296     }
3297     else if(ban)
3298     {
3299         for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3300         {
3301             extern const char *hidden_host_suffix;
3302             const char *old_name = chanserv_conf.old_ban_names->list[n];
3303             char *new_mask;
3304             unsigned int l1, l2;
3305
3306             l1 = strlen(ban);
3307             l2 = strlen(old_name);
3308             if(l2+2 > l1)
3309                 continue;
3310             if(irccasecmp(ban + l1 - l2, old_name))
3311                 continue;
3312             new_mask = malloc(MAXLEN);
3313             sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3314             free(ban);
3315             name = ban = new_mask;
3316         }
3317     }
3318
3319     post_add_ban:
3320     if(action & ACTION_BAN)
3321     {
3322         unsigned int exists;
3323         struct mod_chanmode *change;
3324
3325         if(channel->banlist.used >= MAXBANS)
3326         {
3327             if(cmd)
3328                 reply("CSMSG_BANLIST_FULL", channel->name);
3329             free(ban);
3330             return 0;
3331         }
3332
3333         exists = ChannelBanExists(channel, ban);
3334         change = mod_chanmode_alloc(victimCount + 1);
3335         for(n = 0; n < victimCount; ++n)
3336         {
3337             change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3338             change->args[n].u.member = victims[n];
3339         }
3340         if(!exists)
3341         {
3342             change->args[n].mode = MODE_BAN;
3343             change->args[n++].u.hostmask = ban;
3344         }
3345         change->argc = n;
3346         if(cmd)
3347             modcmd_chanmode_announce(change);
3348         else
3349             mod_chanmode_announce(chanserv, channel, change);
3350         mod_chanmode_free(change);
3351
3352         if(exists && (action == ACTION_BAN))
3353         {
3354             if(cmd)
3355                 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3356             free(ban);
3357             return 0;
3358         }
3359     }
3360
3361     if(action & ACTION_KICK)
3362     {
3363         char kick_reason[MAXLEN];
3364         sprintf(kick_reason, "(%s) %s", user->nick, reason);
3365
3366         for(n = 0; n < victimCount; n++)
3367             KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3368     }
3369
3370     if(!cmd)
3371     {
3372         /* No response, since it was automated. */
3373     }
3374     else if(action & ACTION_ADD_BAN)
3375     {
3376         if(duration)
3377             reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3378         else
3379             reply("CSMSG_BAN_ADDED", name, channel->name);
3380     }
3381     else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3382         reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3383     else if(action & ACTION_BAN)
3384         reply("CSMSG_BAN_DONE", name, channel->name);
3385     else if(action & ACTION_KICK && victimCount)
3386         reply("CSMSG_KICK_DONE", name, channel->name);
3387
3388     free(ban);
3389     return 1;
3390 }
3391
3392 static CHANSERV_FUNC(cmd_kickban)
3393 {
3394     return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3395 }
3396
3397 static CHANSERV_FUNC(cmd_kick)
3398 {
3399     return eject_user(CSFUNC_ARGS, ACTION_KICK);
3400 }
3401
3402 static CHANSERV_FUNC(cmd_ban)
3403 {
3404     return eject_user(CSFUNC_ARGS, ACTION_BAN);
3405 }
3406
3407 static CHANSERV_FUNC(cmd_addban)
3408 {
3409     return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3410 }
3411
3412 static CHANSERV_FUNC(cmd_addtimedban)
3413 {
3414     return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3415 }
3416
3417 static struct mod_chanmode *
3418 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3419 {
3420     struct mod_chanmode *change;
3421     unsigned char *match;
3422     unsigned int ii, count;
3423
3424     match = alloca(bans->used);
3425     if(actee)
3426     {
3427         for(ii = count = 0; ii < bans->used; ++ii)
3428         {
3429             match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3430                                           MATCH_USENICK | MATCH_VISIBLE);
3431             if(match[ii])
3432                 count++;
3433         }
3434     }
3435     else
3436     {
3437         for(ii = count = 0; ii < bans->used; ++ii)
3438         {
3439             match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3440             if(match[ii])
3441                 count++;
3442         }
3443     }
3444     if(!count)
3445         return NULL;
3446     change = mod_chanmode_alloc(count);
3447     for(ii = count = 0; ii < bans->used; ++ii)
3448     {
3449         if(!match[ii])
3450             continue;
3451         change->args[count].mode = MODE_REMOVE | MODE_BAN;
3452         change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3453     }
3454     assert(count == change->argc);
3455     return change;
3456 }
3457
3458 static int
3459 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3460 {
3461     struct userNode *actee;
3462     char *mask = NULL;
3463     int acted = 0;
3464
3465     REQUIRE_PARAMS(2);
3466
3467     /* may want to allow a comma delimited list of users... */
3468     if(!(actee = GetUserH(argv[1])))
3469     {
3470         if(!is_ircmask(argv[1]) && *argv[1] == '*')
3471         {
3472             char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3473             const char *accountname = argv[1] + 1;
3474
3475             snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3476             mask = strdup(banmask);
3477         }
3478         else if(!is_ircmask(argv[1]))
3479         {
3480             reply("MSG_NICK_UNKNOWN", argv[1]);
3481             return 0;
3482         }
3483         else
3484         {
3485             mask = strdup(argv[1]);
3486         }
3487     }
3488
3489     /* We don't sanitize the mask here because ircu
3490        doesn't do it. */
3491     if(action & ACTION_UNBAN)
3492     {
3493         struct mod_chanmode *change;
3494         change = find_matching_bans(&channel->banlist, actee, mask);
3495         if(change)
3496         {
3497             unsigned int ii;
3498
3499             modcmd_chanmode_announce(change);
3500             for(ii = 0; ii < change->argc; ++ii)
3501                 free((char*)change->args[ii].u.hostmask);
3502             mod_chanmode_free(change);
3503             acted = 1;
3504         }
3505     }
3506
3507     if(action & ACTION_DEL_BAN)
3508     {
3509         struct banData *ban, *next;
3510
3511         ban = channel->channel_info->bans;
3512         while(ban)
3513         {
3514             if(actee)
3515                 for( ; ban && !user_matches_glob(actee, ban->mask,
3516                                                  MATCH_USENICK | MATCH_VISIBLE);
3517                      ban = ban->next);
3518             else
3519                 for( ; ban && !match_ircglobs(mask, ban->mask);
3520                      ban = ban->next);
3521             if(!ban)
3522                 break;
3523             next = ban->next;
3524             del_channel_ban(ban);
3525             ban = next;
3526             acted = 1;
3527         }
3528     }
3529
3530     if(!acted)
3531         reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3532     else
3533         reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3534     if(mask)
3535         free(mask);
3536     return 1;
3537 }
3538
3539 static CHANSERV_FUNC(cmd_unban)
3540 {
3541     return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3542 }
3543
3544 static CHANSERV_FUNC(cmd_delban)
3545 {
3546     /* it doesn't necessarily have to remove the channel ban - may want
3547        to make that an option. */
3548     return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3549 }
3550
3551 static CHANSERV_FUNC(cmd_unbanme)
3552 {
3553     struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3554     long flags = ACTION_UNBAN;
3555
3556     /* remove permanent bans if the user has the proper access. */
3557     if(uData->access >= UL_MASTER)
3558         flags |= ACTION_DEL_BAN;
3559
3560     argv[1] = user->nick;
3561     return unban_user(user, channel, 2, argv, cmd, flags);
3562 }
3563
3564 static CHANSERV_FUNC(cmd_unbanall)
3565 {
3566     struct mod_chanmode *change;
3567     unsigned int ii;
3568
3569     if(!channel->banlist.used)
3570     {
3571         reply("CSMSG_NO_BANS", channel->name);
3572         return 0;
3573     }
3574
3575     change = mod_chanmode_alloc(channel->banlist.used);
3576     for(ii=0; ii<channel->banlist.used; ii++)
3577     {
3578         change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3579         change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3580     }
3581     modcmd_chanmode_announce(change);
3582     for(ii = 0; ii < change->argc; ++ii)
3583         free((char*)change->args[ii].u.hostmask);
3584     mod_chanmode_free(change);
3585     reply("CSMSG_BANS_REMOVED", channel->name);
3586     return 1;
3587 }
3588
3589 static CHANSERV_FUNC(cmd_open)
3590 {
3591     struct mod_chanmode *change;
3592     unsigned int ii;
3593
3594     change = find_matching_bans(&channel->banlist, user, NULL);
3595     if(!change)
3596         change = mod_chanmode_alloc(0);
3597     change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3598     if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3599        && channel->channel_info->modes.modes_set)
3600         change->modes_clear &= ~channel->channel_info->modes.modes_set;
3601     modcmd_chanmode_announce(change);
3602     reply("CSMSG_CHANNEL_OPENED", channel->name);
3603     for(ii = 0; ii < change->argc; ++ii)
3604         free((char*)change->args[ii].u.hostmask);
3605     mod_chanmode_free(change);
3606     return 1;
3607 }
3608
3609 static CHANSERV_FUNC(cmd_myaccess)
3610 {
3611     static struct string_buffer sbuf;
3612     struct handle_info *target_handle;
3613     struct userData *uData;
3614
3615     if(argc < 2)
3616         target_handle = user->handle_info;
3617     else if(!IsHelping(user))
3618     {
3619         reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3620         return 0;
3621     }
3622     else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3623         return 0;
3624
3625     if(!target_handle->channels)
3626     {
3627         reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3628         return 1;
3629     }
3630
3631     reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3632     for(uData = target_handle->channels; uData; uData = uData->u_next)
3633     {
3634         struct chanData *cData = uData->channel;
3635
3636         if(uData->access > UL_OWNER)
3637             continue;
3638         if(IsProtected(cData)
3639            && (target_handle != user->handle_info)
3640            && !GetTrueChannelAccess(cData, user->handle_info))
3641             continue;
3642         sbuf.used = 0;
3643         string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3644         if(uData->flags != USER_AUTO_OP)
3645             string_buffer_append(&sbuf, ',');
3646         if(IsUserSuspended(uData))
3647             string_buffer_append(&sbuf, 's');
3648         if(IsUserAutoOp(uData))
3649         {
3650             if(uData->access >= cData->lvlOpts[lvlGiveOps])
3651                 string_buffer_append(&sbuf, 'o');
3652             else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3653                 string_buffer_append(&sbuf, 'v');
3654         }
3655         if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3656             string_buffer_append(&sbuf, 'i');
3657         if(uData->info)
3658             string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3659         else
3660             string_buffer_append_string(&sbuf, ")]");
3661         string_buffer_append(&sbuf, '\0');
3662         send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3663     }
3664
3665     return 1;
3666 }
3667
3668 static CHANSERV_FUNC(cmd_access)
3669 {
3670     struct userNode *target;
3671     struct handle_info *target_handle;
3672     struct userData *uData;
3673     int helping;
3674     char prefix[MAXLEN];
3675
3676     if(argc < 2)
3677     {
3678         target = user;
3679         target_handle = target->handle_info;
3680     }
3681     else if((target = GetUserH(argv[1])))
3682     {
3683         target_handle = target->handle_info;
3684     }
3685     else if(argv[1][0] == '*')
3686     {
3687         if(!(target_handle = get_handle_info(argv[1]+1)))
3688         {
3689             reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3690             return 0;
3691         }
3692     }
3693     else
3694     {
3695         reply("MSG_NICK_UNKNOWN", argv[1]);
3696         return 0;
3697     }
3698
3699     assert(target || target_handle);
3700
3701     if(target == chanserv)
3702     {
3703         reply("CSMSG_IS_CHANSERV");
3704         return 1;
3705     }
3706
3707     if(!target_handle)
3708     {
3709         if(IsOper(target))
3710         {
3711             reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3712             return 0;
3713         }
3714         if(target != user)
3715         {
3716             reply("MSG_USER_AUTHENTICATE", target->nick);
3717             return 0;
3718         }
3719         reply("MSG_AUTHENTICATE");
3720         return 0;
3721     }
3722
3723     if(target)
3724     {
3725         const char *epithet = NULL, *type = NULL;
3726         if(IsOper(target))
3727         {
3728             epithet = chanserv_conf.irc_operator_epithet;
3729             type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3730         }
3731         else if(IsNetworkHelper(target))
3732         {
3733             epithet = chanserv_conf.network_helper_epithet;
3734             type = user_find_message(user, "CSMSG_UC_H_TITLE");
3735         }
3736         else if(IsSupportHelper(target))
3737         {
3738             epithet = chanserv_conf.support_helper_epithet;
3739             type = user_find_message(user, "CSMSG_LC_H_TITLE");
3740         }
3741         if(epithet)
3742         {
3743             if(target_handle->epithet)
3744                 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3745             else if(epithet)
3746                 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3747         }
3748         sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3749     }
3750     else
3751     {
3752         sprintf(prefix, "%s", target_handle->handle);
3753     }
3754
3755     if(!channel->channel_info)
3756     {
3757         reply("CSMSG_NOT_REGISTERED", channel->name);
3758         return 1;
3759     }
3760
3761     helping = HANDLE_FLAGGED(target_handle, HELPING)
3762         && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3763     if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3764     {
3765         reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3766         /* To prevent possible information leaks, only show infolines
3767          * if the requestor is in the channel or it's their own
3768          * handle. */
3769         if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3770         {
3771             send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3772         }
3773         /* Likewise, only say it's suspended if the user has active
3774          * access in that channel or it's their own entry. */
3775         if(IsUserSuspended(uData)
3776            && (GetChannelUser(channel->channel_info, user->handle_info)
3777                || (user->handle_info == uData->handle)))
3778         {
3779             reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3780         }
3781     }
3782     else
3783     {
3784         reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3785     }
3786
3787     return 1;
3788 }
3789
3790 static void
3791 zoot_list(struct listData *list)
3792 {
3793     struct userData *uData;
3794     unsigned int start, curr, highest, lowest;
3795     struct helpfile_table tmp_table;
3796     const char **temp, *msg;
3797
3798     if(list->table.length == 1)
3799     {
3800         if(list->search)
3801             send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3802         else
3803             send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3804         msg = user_find_message(list->user, "MSG_NONE");
3805         send_message_type(4, list->user, list->bot, "  %s", msg);
3806     }
3807     tmp_table.width = list->table.width;
3808     tmp_table.flags = list->table.flags;
3809     list->table.contents[0][0] = " ";
3810     highest = list->highest;
3811     if(list->lowest != 0)
3812         lowest = list->lowest;
3813     else if(highest < 100)
3814         lowest = 1;
3815     else
3816         lowest = highest - 100;
3817     for(start = curr = 1; curr < list->table.length; )
3818     {
3819         uData = list->users[curr-1];
3820         list->table.contents[curr++][0] = " ";
3821         if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3822         {
3823             if(list->search)
3824                 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3825             else
3826                 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3827             temp = list->table.contents[--start];
3828             list->table.contents[start] = list->table.contents[0];
3829             tmp_table.contents = list->table.contents + start;
3830             tmp_table.length = curr - start;
3831             table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3832             list->table.contents[start] = temp;
3833             start = curr;
3834             highest = lowest - 1;
3835             lowest = (highest < 100) ? 0 : (highest - 99);
3836         }
3837     }
3838 }
3839
3840 static void
3841 def_list(struct listData *list)
3842 {
3843     const char *msg;
3844     if(list->search)
3845         send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3846     else
3847         send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3848     table_send(list->bot, list->user->nick, 0, NULL, list->table);
3849     if(list->table.length == 1)
3850     {
3851         msg = user_find_message(list->user, "MSG_NONE");
3852         send_message_type(4, list->user, list->bot, "  %s", msg);
3853     }
3854 }
3855
3856 static int
3857 userData_access_comp(const void *arg_a, const void *arg_b)
3858 {
3859     const struct userData *a = *(struct userData**)arg_a;
3860     const struct userData *b = *(struct userData**)arg_b;
3861     int res;
3862     if(a->access != b->access)
3863         res = b->access - a->access;
3864     else
3865         res = irccasecmp(a->handle->handle, b->handle->handle);
3866     return res;
3867 }
3868
3869 static int
3870 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3871 {
3872     void (*send_list)(struct listData *);
3873     struct userData *uData;
3874     struct listData lData;
3875     unsigned int matches;
3876     const char **ary;
3877
3878     lData.user = user;
3879     lData.bot = cmd->parent->bot;
3880     lData.channel = channel;
3881     lData.lowest = lowest;
3882     lData.highest = highest;
3883     lData.search = (argc > 1) ? argv[1] : NULL;
3884     send_list = def_list;
3885     (void)zoot_list; /* since it doesn't show user levels */
3886
3887     if(user->handle_info)
3888     {
3889         switch(user->handle_info->userlist_style)
3890         {
3891         case HI_STYLE_DEF: send_list = def_list; break;
3892         case HI_STYLE_ZOOT: send_list = def_list; break;
3893         }
3894     }
3895
3896     lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3897     matches = 0;
3898     for(uData = channel->channel_info->users; uData; uData = uData->next)
3899     {
3900         if((uData->access < lowest)
3901            || (uData->access > highest)
3902            || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3903             continue;
3904         lData.users[matches++] = uData;
3905     }
3906     qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3907
3908     lData.table.length = matches+1;
3909     lData.table.width = 4;
3910     lData.table.flags = TABLE_NO_FREE;
3911     lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3912     ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3913     lData.table.contents[0] = ary;
3914     ary[0] = "Access";
3915     ary[1] = "Account";
3916     ary[2] = "Last Seen";
3917     ary[3] = "Status";
3918     for(matches = 1; matches < lData.table.length; ++matches)
3919     {
3920         struct userData *uData = lData.users[matches-1];
3921         char seen[INTERVALLEN];
3922
3923         ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3924         lData.table.contents[matches] = ary;
3925         ary[0] = strtab(uData->access);
3926         ary[1] = uData->handle->handle;
3927         if(uData->present)
3928             ary[2] = "Here";
3929         else if(!uData->seen)
3930             ary[2] = "Never";
3931         else
3932             ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3933         ary[2] = strdup(ary[2]);
3934         if(IsUserSuspended(uData))
3935             ary[3] = "Suspended";
3936         else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3937             ary[3] = "Vacation";
3938         else
3939             ary[3] = "Normal";
3940     }
3941     send_list(&lData);
3942     for(matches = 1; matches < lData.table.length; ++matches)
3943     {
3944         free((char*)lData.table.contents[matches][2]);
3945         free(lData.table.contents[matches]);
3946     }
3947     free(lData.table.contents[0]);
3948     free(lData.table.contents);
3949     return 1;
3950 }
3951
3952 static CHANSERV_FUNC(cmd_users)
3953 {
3954     return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3955 }
3956
3957 static CHANSERV_FUNC(cmd_wlist)
3958 {
3959     return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3960 }
3961
3962 static CHANSERV_FUNC(cmd_clist)
3963 {
3964     return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3965 }
3966
3967 static CHANSERV_FUNC(cmd_mlist)
3968 {
3969     return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3970 }
3971
3972 static CHANSERV_FUNC(cmd_olist)
3973 {
3974     return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3975 }
3976
3977 static CHANSERV_FUNC(cmd_plist)
3978 {
3979     return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3980 }
3981
3982 static CHANSERV_FUNC(cmd_bans)
3983 {
3984     struct userNode *search_u = NULL;
3985     struct helpfile_table tbl;
3986     unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
3987     char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3988     const char *msg_never, *triggered, *expires;
3989     struct banData *ban, **bans;
3990
3991     if(argc < 2)
3992         search = NULL;
3993     else if(strchr(search = argv[1], '!'))
3994     {
3995         search = argv[1];
3996         search_wilds = search[strcspn(search, "?*")];
3997     }
3998     else if(!(search_u = GetUserH(search)))
3999         reply("MSG_NICK_UNKNOWN", search);
4000
4001     bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4002
4003     for(ban = channel->channel_info->bans; ban; ban = ban->next)
4004     {
4005         if(search_u)
4006         {
4007             if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4008                 continue;
4009         }
4010         else if(search)
4011         {
4012             if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4013                 continue;
4014         }
4015         bans[matches++] = ban;
4016         if(ban->expires)
4017             timed = 1;
4018     }
4019
4020     tbl.length = matches + 1;
4021     tbl.width = 4 + timed;
4022     tbl.flags = 0;
4023     tbl.flags = TABLE_NO_FREE;
4024     tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4025     tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4026     tbl.contents[0][0] = "Mask";
4027     tbl.contents[0][1] = "Set By";
4028     tbl.contents[0][2] = "Triggered";
4029     if(timed)
4030     {
4031         tbl.contents[0][3] = "Expires";
4032         tbl.contents[0][4] = "Reason";
4033     }
4034     else
4035         tbl.contents[0][3] = "Reason";
4036     if(!matches)
4037     {
4038         table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4039         reply("MSG_NONE");
4040         free(tbl.contents[0]);
4041         free(tbl.contents);
4042         return 0;
4043     }
4044
4045     msg_never = user_find_message(user, "MSG_NEVER");
4046     for(ii = 0; ii < matches; )
4047     {
4048         ban = bans[ii];
4049
4050         if(!timed)
4051             expires = "";
4052         else if(ban->expires)
4053             expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4054         else
4055             expires = msg_never;
4056
4057         if(ban->triggered)
4058             triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4059         else
4060             triggered = msg_never;
4061
4062         tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4063         tbl.contents[ii][0] = ban->mask;
4064         tbl.contents[ii][1] = ban->owner;
4065         tbl.contents[ii][2] = strdup(triggered);
4066         if(timed)
4067         {
4068             tbl.contents[ii][3] = strdup(expires);
4069             tbl.contents[ii][4] = ban->reason;
4070         }
4071         else
4072             tbl.contents[ii][3] = ban->reason;
4073     }
4074     table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4075     reply("MSG_MATCH_COUNT", matches);
4076     for(ii = 1; ii < tbl.length; ++ii)
4077     {
4078         free((char*)tbl.contents[ii][2]);
4079         if(timed)
4080             free((char*)tbl.contents[ii][3]);
4081         free(tbl.contents[ii]);
4082     }
4083     free(tbl.contents[0]);
4084     free(tbl.contents);
4085     return 1;
4086 }
4087
4088 static int
4089 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4090 {
4091     struct chanData *cData = channel->channel_info;
4092     if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4093         return 0;
4094     if(cData->topic_mask)
4095         return !match_ircglob(new_topic, cData->topic_mask);
4096     else if(cData->topic)
4097         return irccasecmp(new_topic, cData->topic);
4098     else
4099         return 0;
4100 }
4101
4102 static CHANSERV_FUNC(cmd_topic)
4103 {
4104     struct chanData *cData;
4105     char *topic;
4106
4107     cData = channel->channel_info;
4108     if(argc < 2)
4109     {
4110         if(cData->topic)
4111         {
4112             SetChannelTopic(channel, chanserv, cData->topic, 1);
4113             reply("CSMSG_TOPIC_SET", cData->topic);
4114             return 1;
4115         }
4116
4117         reply("CSMSG_NO_TOPIC", channel->name);
4118         return 0;
4119     }
4120
4121     topic = unsplit_string(argv + 1, argc - 1, NULL);
4122     /* If they say "!topic *", use an empty topic. */
4123     if((topic[0] == '*') && (topic[1] == 0))
4124         topic[0] = 0;
4125     if(bad_topic(channel, user, topic))
4126     {
4127         char *topic_mask = cData->topic_mask;
4128         if(topic_mask)
4129         {
4130             char new_topic[TOPICLEN+1], tchar;
4131             int pos=0, starpos=-1, dpos=0, len;
4132
4133             while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4134             {
4135                 switch(tchar)
4136                 {
4137                 case '*':
4138                     if(starpos != -1)
4139                         goto bad_mask;
4140                     len = strlen(topic);
4141                     if((dpos + len) > TOPICLEN)
4142                         len = TOPICLEN + 1 - dpos;
4143                     memcpy(new_topic+dpos, topic, len);
4144                     dpos += len;
4145                     starpos = pos;
4146                     break;
4147                 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4148                 default: new_topic[dpos++] = tchar; break;
4149                 }
4150             }
4151             if((dpos > TOPICLEN) || tchar)
4152             {
4153             bad_mask:
4154                 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4155                 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4156                 return 0;
4157             }
4158             new_topic[dpos] = 0;
4159             SetChannelTopic(channel, chanserv, new_topic, 1);
4160         } else {
4161             reply("CSMSG_TOPIC_LOCKED", channel->name);
4162             return 0;
4163         }
4164     }
4165     else
4166         SetChannelTopic(channel, chanserv, topic, 1);
4167
4168     if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4169     {
4170         /* Grab the topic and save it as the default topic. */
4171         free(cData->topic);
4172         cData->topic = strdup(channel->topic);
4173     }
4174
4175     return 1;
4176 }
4177
4178 static CHANSERV_FUNC(cmd_mode)
4179 {
4180     struct userData *uData;
4181     struct mod_chanmode *change;
4182     short base_oplevel;
4183
4184     if(argc < 2)
4185     {
4186         change = &channel->channel_info->modes;
4187         if(change->modes_set || change->modes_clear) {
4188             modcmd_chanmode_announce(change);
4189             reply("CSMSG_DEFAULTED_MODES", channel->name);
4190         } else
4191             reply("CSMSG_NO_MODES", channel->name);
4192         return 1;
4193     }
4194
4195     uData = GetChannelUser(channel->channel_info, user->handle_info);
4196     if (!uData)
4197         base_oplevel = MAXOPLEVEL;
4198     else if (uData->access >= UL_OWNER)
4199         base_oplevel = 1;
4200     else
4201         base_oplevel = 1 + UL_OWNER - uData->access;
4202     change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
4203     if(!change)
4204     {
4205         reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4206         return 0;
4207     }
4208
4209     if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4210        && mode_lock_violated(&channel->channel_info->modes, change))
4211     {
4212         char modes[MAXLEN];
4213         mod_chanmode_format(&channel->channel_info->modes, modes);
4214         reply("CSMSG_MODE_LOCKED", modes, channel->name);
4215         return 0;
4216     }
4217
4218     modcmd_chanmode_announce(change);
4219     mod_chanmode_free(change);
4220     reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
4221     return 1;
4222 }
4223
4224 static CHANSERV_FUNC(cmd_invite)
4225 {
4226     struct userData *uData;
4227     struct userNode *invite;
4228
4229     uData = GetChannelUser(channel->channel_info, user->handle_info);
4230
4231     if(argc > 1)
4232     {
4233         if(!(invite = GetUserH(argv[1])))
4234         {
4235             reply("MSG_NICK_UNKNOWN", argv[1]);
4236             return 0;
4237         }
4238     }
4239     else
4240         invite = user;
4241
4242     if(GetUserMode(channel, invite))
4243     {
4244         reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4245         return 0;
4246     }
4247
4248     if(user != invite)
4249     {
4250         if(argc > 2)
4251         {
4252             char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4253             send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4254         }
4255         else
4256             send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4257     }
4258     irc_invite(chanserv, invite, channel);
4259     if(argc > 1)
4260         reply("CSMSG_INVITED_USER", argv[1], channel->name);
4261
4262     return 1;
4263 }
4264
4265 static CHANSERV_FUNC(cmd_inviteme)
4266 {
4267     if(GetUserMode(channel, user))
4268     {
4269         reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4270         return 0;
4271     }
4272     if(channel->channel_info
4273        && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4274     {
4275         reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4276         return 0;
4277     }
4278     irc_invite(cmd->parent->bot, user, channel);
4279     return 1;
4280 }
4281
4282 static void
4283 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4284 {
4285     unsigned int combo;
4286     char buf1[INTERVALLEN], buf2[INTERVALLEN];
4287
4288     /* We display things based on two dimensions:
4289      * - Issue time: present or absent
4290      * - Expiration: revoked, expired, expires in future, or indefinite expiration
4291      * (in order of precedence, so something both expired and revoked
4292      * only counts as revoked)
4293      */
4294     combo = (suspended->issued ? 4 : 0)
4295         + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4296     switch(combo) {
4297     case 0: /* no issue time, indefinite expiration */
4298         reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4299         break;
4300     case 1: /* no issue time, expires in future */
4301         intervalString(buf1, suspended->expires-now, user->handle_info);
4302         reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4303         break;
4304     case 2: /* no issue time, expired */
4305         intervalString(buf1, now-suspended->expires, user->handle_info);
4306         reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4307         break;
4308     case 3: /* no issue time, revoked */
4309         intervalString(buf1, now-suspended->revoked, user->handle_info);
4310         reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4311         break;
4312     case 4: /* issue time set, indefinite expiration */
4313         intervalString(buf1, now-suspended->issued, user->handle_info);
4314         reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4315         break;
4316     case 5: /* issue time set, expires in future */
4317         intervalString(buf1, now-suspended->issued, user->handle_info);
4318         intervalString(buf2, suspended->expires-now, user->handle_info);
4319         reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4320         break;
4321     case 6: /* issue time set, expired */
4322         intervalString(buf1, now-suspended->issued, user->handle_info);
4323         intervalString(buf2, now-suspended->expires, user->handle_info);
4324         reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4325         break;
4326     case 7: /* issue time set, revoked */
4327         intervalString(buf1, now-suspended->issued, user->handle_info);
4328         intervalString(buf2, now-suspended->revoked, user->handle_info);
4329         reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4330         break;
4331     default:
4332         log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4333         return;
4334     }
4335 }
4336
4337 static CHANSERV_FUNC(cmd_info)
4338 {
4339     char modes[MAXLEN], buffer[INTERVALLEN];
4340     struct userData *uData, *owner;
4341     struct chanData *cData;
4342     struct do_not_register *dnr;
4343     struct note *note;
4344     dict_iterator_t it;
4345     int privileged;
4346
4347     cData = channel->channel_info;
4348     reply("CSMSG_CHANNEL_INFO", channel->name);
4349
4350     uData = GetChannelUser(cData, user->handle_info);
4351     if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4352     {
4353         mod_chanmode_format(&cData->modes, modes);
4354         reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4355         reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4356     }
4357
4358     for(it = dict_first(cData->notes); it; it = iter_next(it))
4359     {
4360         int padding;
4361
4362         note = iter_data(it);
4363         if(!note_type_visible_to_user(cData, note->type, user))
4364             continue;
4365
4366         padding = PADLEN - 1 - strlen(iter_key(it));
4367         reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4368     }
4369
4370     reply("CSMSG_CHANNEL_MAX", cData->max);
4371     for(owner = cData->users; owner; owner = owner->next)
4372         if(owner->access == UL_OWNER)
4373             reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4374     reply("CSMSG_CHANNEL_USERS", cData->userCount);
4375     reply("CSMSG_CHANNEL_BANS", cData->banCount);
4376     reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4377
4378     privileged = IsStaff(user);
4379     if(privileged)
4380         reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4381     if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4382         reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4383
4384     if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4385         chanserv_show_dnrs(user, cmd, channel->name, NULL);
4386
4387     if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4388     {
4389         struct suspended *suspended;
4390         reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4391         for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4392             show_suspension_info(cmd, user, suspended);
4393     }
4394     else if(IsSuspended(cData))
4395     {
4396         reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4397         show_suspension_info(cmd, user, cData->suspended);
4398     }
4399     return 1;
4400 }
4401
4402 static CHANSERV_FUNC(cmd_netinfo)
4403 {
4404     extern time_t boot_time;
4405     extern unsigned long burst_length;
4406     char interval[INTERVALLEN];
4407
4408     reply("CSMSG_NETWORK_INFO");
4409     reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4410     reply("CSMSG_NETWORK_USERS", dict_size(clients));
4411     reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4412     reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4413     reply("CSMSG_NETWORK_BANS", banCount);
4414     reply("CSMSG_NETWORK_CHANUSERS", userCount);
4415     reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4416     reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4417     return 1;
4418 }
4419
4420 static void
4421 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4422 {
4423     struct helpfile_table table;
4424     unsigned int nn;
4425     struct userNode *user;
4426     char *nick;
4427
4428     table.length = 0;
4429     table.width = 1;
4430     table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4431     table.contents = alloca(list->used*sizeof(*table.contents));
4432     for(nn=0; nn<list->used; nn++)
4433     {
4434         user = list->list[nn];
4435         if(user->modes & skip_flags)
4436             continue;
4437         if(IsBot(user))
4438             continue;
4439         table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4440         if(IsAway(user))
4441         {
4442             nick = alloca(strlen(user->nick)+3);
4443             sprintf(nick, "(%s)", user->nick);
4444         }
4445         else
4446             nick = user->nick;
4447         table.contents[table.length][0] = nick;
4448         table.length++;
4449     }
4450     table_send(chanserv, to->nick, 0, NULL, table);
4451 }
4452
4453 static CHANSERV_FUNC(cmd_ircops)
4454 {
4455     reply("CSMSG_STAFF_OPERS");
4456     send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4457     return 1;
4458 }
4459
4460 static CHANSERV_FUNC(cmd_helpers)
4461 {
4462     reply("CSMSG_STAFF_HELPERS");
4463     send_staff_list(user, &curr_helpers, FLAGS_OPER);
4464     return 1;
4465 }
4466
4467 static CHANSERV_FUNC(cmd_staff)
4468 {
4469     reply("CSMSG_NETWORK_STAFF");
4470     cmd_ircops(CSFUNC_ARGS);
4471     cmd_helpers(CSFUNC_ARGS);
4472     return 1;
4473 }
4474
4475 static CHANSERV_FUNC(cmd_peek)
4476 {
4477     struct modeNode *mn;
4478     char modes[MODELEN];
4479     unsigned int n;
4480     struct helpfile_table table;
4481
4482     irc_make_chanmode(channel, modes);
4483
4484     reply("CSMSG_PEEK_INFO", channel->name);
4485     reply("CSMSG_PEEK_TOPIC", channel->topic);
4486     reply("CSMSG_PEEK_MODES", modes);
4487     reply("CSMSG_PEEK_USERS", channel->members.used);
4488
4489     table.length = 0;
4490     table.width = 1;
4491     table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4492     table.contents = alloca(channel->members.used*sizeof(*table.contents));
4493     for(n = 0; n < channel->members.used; n++)
4494     {
4495         mn = channel->members.list[n];
4496         if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4497             continue;
4498         table.contents[table.length] = alloca(sizeof(**table.contents));
4499         table.contents[table.length][0] = mn->user->nick;
4500         table.length++;
4501     }
4502     if(table.length)
4503     {
4504         reply("CSMSG_PEEK_OPS");
4505         table_send(chanserv, user->nick, 0, NULL, table);
4506     }
4507     else
4508         reply("CSMSG_PEEK_NO_OPS");
4509     return 1;
4510 }
4511
4512 static MODCMD_FUNC(cmd_wipeinfo)
4513 {
4514     struct handle_info *victim;
4515     struct userData *ud, *actor, *real_actor;
4516     unsigned int override = 0;
4517
4518     REQUIRE_PARAMS(2);
4519     actor = GetChannelUser(channel->channel_info, user->handle_info);
4520     real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4521     if(!(victim = modcmd_get_handle_info(user, argv[1])))
4522         return 0;
4523     if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4524     {
4525         reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4526         return 0;
4527     }
4528     if((ud->access >= actor->access) && (ud != actor))
4529     {
4530         reply("MSG_USER_OUTRANKED", victim->handle);
4531         return 0;
4532     }
4533     if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4534         override = CMD_LOG_OVERRIDE;
4535     if(ud->info)
4536         free(ud->info);
4537     ud->info = NULL;
4538     reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4539     return 1 | override;
4540 }
4541
4542 static CHANSERV_FUNC(cmd_resync)
4543 {
4544     struct mod_chanmode *changes;
4545     struct chanData *cData = channel->channel_info;
4546     unsigned int ii, used;
4547
4548     changes = mod_chanmode_alloc(channel->members.used * 2);
4549     for(ii = used = 0; ii < channel->members.used; ++ii)
4550     {
4551         struct modeNode *mn = channel->members.list[ii];
4552         struct userData *uData;
4553
4554         if(IsService(mn->user))
4555             continue;
4556
4557         uData = GetChannelAccess(cData, mn->user->handle_info);
4558         if(!cData->lvlOpts[lvlGiveOps]
4559            || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4560         {
4561             if(!(mn->modes & MODE_CHANOP))
4562             {
4563                 changes->args[used].mode = MODE_CHANOP;
4564                 changes->args[used++].u.member = mn;
4565             }
4566         }
4567         else if(!cData->lvlOpts[lvlGiveVoice]
4568                 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4569         {
4570             if(mn->modes & MODE_CHANOP)
4571             {
4572                 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4573                 changes->args[used++].u.member = mn;
4574             }
4575             if(!(mn->modes & MODE_VOICE))
4576             {
4577                 changes->args[used].mode = MODE_VOICE;
4578                 changes->args[used++].u.member = mn;
4579             }
4580         }
4581         else
4582         {
4583             if(mn->modes)
4584             {
4585                 changes->args[used].mode = MODE_REMOVE | mn->modes;
4586                 changes->args[used++].u.member = mn;
4587             }
4588         }
4589     }
4590     changes->argc = used;
4591     modcmd_chanmode_announce(changes);
4592     mod_chanmode_free(changes);
4593     reply("CSMSG_RESYNCED_USERS", channel->name);
4594     return 1;
4595 }
4596
4597 static CHANSERV_FUNC(cmd_seen)
4598 {
4599     struct userData *uData;
4600     struct handle_info *handle;
4601     char seen[INTERVALLEN];
4602
4603     REQUIRE_PARAMS(2);
4604
4605     if(!irccasecmp(argv[1], chanserv->nick))
4606     {
4607         reply("CSMSG_IS_CHANSERV");
4608         return 1;
4609     }
4610
4611     if(!(handle = get_handle_info(argv[1])))
4612     {
4613         reply("MSG_HANDLE_UNKNOWN", argv[1]);
4614         return 0;
4615     }
4616
4617     if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4618     {
4619         reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4620         return 0;
4621     }
4622
4623     if(uData->present)
4624         reply("CSMSG_USER_PRESENT", handle->handle);
4625     else if(uData->seen)
4626         reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4627     else
4628         reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4629
4630     if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4631         reply("CSMSG_USER_VACATION", handle->handle);
4632
4633     return 1;
4634 }
4635
4636 static MODCMD_FUNC(cmd_names)
4637 {
4638     struct userNode *targ;
4639     struct userData *targData;
4640     unsigned int ii, pos;
4641     char buf[400];
4642
4643     for(ii=pos=0; ii<channel->members.used; ++ii)
4644     {
4645         targ = channel->members.list[ii]->user;
4646         targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4647         if(!targData)
4648             continue;
4649         if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4650         {
4651             buf[pos] = 0;
4652             reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4653             pos = 0;
4654         }
4655         buf[pos++] = ' ';
4656         if(IsUserSuspended(targData))
4657             buf[pos++] = 's';
4658         pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4659     }
4660     buf[pos] = 0;
4661     reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4662     reply("CSMSG_END_NAMES", channel->name);
4663     return 1;
4664 }
4665
4666 static int
4667 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4668 {
4669     switch(ntype->visible_type)
4670     {
4671     case NOTE_VIS_ALL: return 1;
4672     case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4673     case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4674     }
4675 }
4676
4677 static int
4678 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4679 {
4680     struct userData *uData;
4681
4682     switch(ntype->set_access_type)
4683     {
4684     case NOTE_SET_CHANNEL_ACCESS:
4685         if(!user->handle_info)
4686             return 0;
4687         if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4688             return 0;
4689         return uData->access >= ntype->set_access.min_ulevel;
4690     case NOTE_SET_CHANNEL_SETTER:
4691         return check_user_level(channel, user, lvlSetters, 1, 0);
4692     case NOTE_SET_PRIVILEGED: default:
4693         return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4694     }
4695 }
4696
4697 static CHANSERV_FUNC(cmd_note)
4698 {
4699     struct chanData *cData;
4700     struct note *note;
4701     struct note_type *ntype;
4702
4703     cData = channel->channel_info;
4704     if(!cData)
4705     {
4706         reply("CSMSG_NOT_REGISTERED", channel->name);
4707         return 0;
4708     }
4709
4710     /* If no arguments, show all visible notes for the channel. */
4711     if(argc < 2)
4712     {
4713         dict_iterator_t it;
4714         unsigned int count;
4715
4716         for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4717         {
4718             note = iter_data(it);
4719             if(!note_type_visible_to_user(cData, note->type, user))
4720                 continue;
4721             if(!count++)
4722                 reply("CSMSG_NOTELIST_HEADER", channel->name);
4723             reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4724         }
4725         if(count)
4726             reply("CSMSG_NOTELIST_END", channel->name);
4727         else
4728             reply("CSMSG_NOTELIST_EMPTY", channel->name);
4729     }
4730     /* If one argument, show the named note. */
4731     else if(argc == 2)
4732     {
4733         if((note = dict_find(cData->notes, argv[1], NULL))
4734            && note_type_visible_to_user(cData, note->type, user))
4735         {
4736             reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4737         }
4738         else if((ntype = dict_find(note_types, argv[1], NULL))
4739                 && note_type_visible_to_user(NULL, ntype, user))
4740         {
4741             reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4742             return 0;
4743         }
4744         else
4745         {
4746             reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4747             return 0;
4748         }
4749     }
4750     /* Assume they're trying to set a note. */
4751     else
4752     {
4753         char *note_text;
4754         ntype = dict_find(note_types, argv[1], NULL);
4755         if(!ntype)
4756         {
4757             reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4758             return 0;
4759         }
4760         else if(note_type_settable_by_user(channel, ntype, user))
4761         {
4762             note_text = unsplit_string(argv+2, argc-2, NULL);
4763             if((note = dict_find(cData->notes, argv[1], NULL)))
4764                 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4765             chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4766             reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4767
4768             if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4769             {
4770                 /* The note is viewable to staff only, so return 0
4771                    to keep the invocation from getting logged (or
4772                    regular users can see it in !events). */
4773                 return 0;
4774             }
4775         }
4776         else
4777         {
4778             reply("CSMSG_NO_ACCESS");
4779             return 0;
4780         }
4781     }
4782     return 1;
4783 }
4784
4785 static CHANSERV_FUNC(cmd_delnote)
4786 {
4787     struct note *note;
4788
4789     REQUIRE_PARAMS(2);
4790     if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4791        || !note_type_settable_by_user(channel, note->type, user))
4792     {
4793         reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4794         return 0;
4795     }
4796     dict_remove(channel->channel_info->notes, note->type->name);
4797     reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4798     return 1;
4799 }
4800
4801 static CHANSERV_FUNC(cmd_events)
4802 {
4803     struct logSearch discrim;
4804     struct logReport report;
4805     unsigned int matches, limit;
4806
4807     limit = (argc > 1) ? atoi(argv[1]) : 10;
4808     if(limit < 1 || limit > 200)
4809         limit = 10;
4810
4811     memset(&discrim, 0, sizeof(discrim));
4812     discrim.masks.bot = chanserv;
4813     discrim.masks.channel_name = channel->name;
4814     if(argc > 2)
4815         discrim.masks.command = argv[2];
4816     discrim.limit = limit;
4817     discrim.max_time = INT_MAX;
4818     discrim.severities = 1 << LOG_COMMAND;
4819     report.reporter = chanserv;
4820     report.user = user;
4821     reply("CSMSG_EVENT_SEARCH_RESULTS");
4822     matches = log_entry_search(&discrim, log_report_entry, &report);
4823     if(matches)
4824         reply("MSG_MATCH_COUNT", matches);
4825     else
4826         reply("MSG_NO_MATCHES");
4827     return 1;
4828 }
4829
4830 static CHANSERV_FUNC(cmd_say)
4831 {
4832     char *msg;
4833     if(channel)
4834     {
4835         REQUIRE_PARAMS(2);
4836         msg = unsplit_string(argv + 1, argc - 1, NULL);
4837         send_channel_message(channel, cmd->parent->bot, "%s", msg);
4838     }
4839     else if(GetUserH(argv[1]))
4840     {
4841         REQUIRE_PARAMS(3);
4842         msg = unsplit_string(argv + 2, argc - 2, NULL);
4843         send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4844     }
4845     else
4846     {
4847         reply("MSG_NOT_TARGET_NAME");
4848         return 0;
4849     }
4850     return 1;
4851 }
4852
4853 static CHANSERV_FUNC(cmd_emote)
4854 {
4855     char *msg;
4856     assert(argc >= 2);
4857     if(channel)
4858     {
4859         /* CTCP is so annoying. */
4860         msg = unsplit_string(argv + 1, argc - 1, NULL);
4861         send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4862     }
4863     else if(GetUserH(argv[1]))
4864     {
4865         msg = unsplit_string(argv + 2, argc - 2, NULL);
4866         send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4867     }
4868     else
4869     {
4870         reply("MSG_NOT_TARGET_NAME");
4871         return 0;
4872     }
4873     return 1;
4874 }
4875
4876 struct channelList *
4877 chanserv_support_channels(void)
4878 {
4879     return &chanserv_conf.support_channels;
4880 }
4881
4882 static CHANSERV_FUNC(cmd_expire)
4883 {
4884     int channel_count = registered_channels;
4885     expire_channels(NULL);
4886     reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4887     return 1;
4888 }
4889
4890 static void
4891 chanserv_expire_suspension(void *data)
4892 {
4893     struct suspended *suspended = data;
4894     struct chanNode *channel;
4895
4896     if(!suspended->expires || (now < suspended->expires))
4897         suspended->revoked = now;
4898     channel = suspended->cData->channel;
4899     suspended->cData->channel = channel;
4900     suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4901     if(!IsOffChannel(suspended->cData))
4902     {
4903         struct mod_chanmode change;
4904         mod_chanmode_init(&change);
4905         change.argc = 1;
4906         change.args[0].mode = MODE_CHANOP;
4907         change.args[0].u.member = AddChannelUser(chanserv, channel);
4908         mod_chanmode_announce(chanserv, channel, &change);
4909     }
4910 }
4911
4912 static CHANSERV_FUNC(cmd_csuspend)
4913 {
4914     struct suspended *suspended;
4915     char reason[MAXLEN];
4916     time_t expiry, duration;
4917     struct userData *uData;
4918
4919     REQUIRE_PARAMS(3);
4920
4921     if(IsProtected(channel->channel_info))
4922     {
4923         reply("CSMSG_SUSPEND_NODELETE", channel->name);
4924         return 0;
4925     }
4926
4927     if(argv[1][0] == '!')
4928         argv[1]++;
4929     else if(IsSuspended(channel->channel_info))
4930     {
4931         reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4932         show_suspension_info(cmd, user, channel->channel_info->suspended);
4933         return 0;
4934     }
4935
4936     if(!strcmp(argv[1], "0"))
4937         expiry = 0;
4938     else if((duration = ParseInterval(argv[1])))
4939         expiry = now + duration;
4940     else
4941     {
4942         reply("MSG_INVALID_DURATION", argv[1]);
4943         return 0;
4944     }
4945
4946     unsplit_string(argv + 2, argc - 2, reason);
4947
4948     suspended = calloc(1, sizeof(*suspended));
4949     suspended->revoked = 0;
4950     suspended->issued = now;
4951     suspended->suspender = strdup(user->handle_info->handle);
4952     suspended->expires = expiry;
4953     suspended->reason = strdup(reason);
4954     suspended->cData = channel->channel_info;
4955     suspended->previous = suspended->cData->suspended;
4956     suspended->cData->suspended = suspended;
4957
4958     if(suspended->expires)
4959         timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4960
4961     if(IsSuspended(channel->channel_info))
4962     {
4963         suspended->previous->revoked = now;
4964         if(suspended->previous->expires)
4965             timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4966         sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4967         global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4968     }
4969     else
4970     {
4971         /* Mark all users in channel as absent. */
4972         for(uData = channel->channel_info->users; uData; uData = uData->next)
4973         {
4974             if(uData->present)
4975             {
4976                 uData->seen = now;
4977                 uData->present = 0;
4978             }
4979         }
4980
4981         /* Mark the channel as suspended, then part. */
4982         channel->channel_info->flags |= CHANNEL_SUSPENDED;
4983         DelChannelUser(chanserv, channel, suspended->reason, 0);
4984         reply("CSMSG_SUSPENDED", channel->name);
4985         sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4986         global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4987     }
4988     return 1;
4989 }
4990
4991 static CHANSERV_FUNC(cmd_cunsuspend)
4992 {
4993     struct suspended *suspended;
4994     char message[MAXLEN];
4995
4996     if(!IsSuspended(channel->channel_info))
4997     {
4998         reply("CSMSG_NOT_SUSPENDED", channel->name);
4999         return 0;
5000     }
5001
5002     suspended = channel->channel_info->suspended;
5003
5004     /* Expire the suspension and join ChanServ to the channel. */
5005     timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5006     chanserv_expire_suspension(suspended);
5007     reply("CSMSG_UNSUSPENDED", channel->name);
5008     sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5009     global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5010     return 1;
5011 }
5012
5013 typedef struct chanservSearch
5014 {
5015     char *name;
5016     char *registrar;
5017
5018     time_t unvisited;
5019     time_t registered;
5020
5021     unsigned long flags;
5022     unsigned int limit;
5023 } *search_t;
5024
5025 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5026
5027 static search_t
5028 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5029 {
5030     search_t search;
5031     unsigned int i;
5032
5033     search = malloc(sizeof(struct chanservSearch));
5034     memset(search, 0, sizeof(*search));
5035     search->limit = 25;
5036
5037     for(i = 0; i < argc; i++)
5038     {
5039         /* Assume all criteria require arguments. */
5040         if(i == (argc - 1))
5041         {
5042             send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5043             goto fail;
5044         }
5045
5046         if(!irccasecmp(argv[i], "name"))
5047             search->name = argv[++i];
5048         else if(!irccasecmp(argv[i], "registrar"))
5049             search->registrar = argv[++i];
5050         else if(!irccasecmp(argv[i], "unvisited"))
5051             search->unvisited = ParseInterval(argv[++i]);
5052         else if(!irccasecmp(argv[i], "registered"))
5053             search->registered = ParseInterval(argv[++i]);
5054         else if(!irccasecmp(argv[i], "flags"))
5055         {
5056             i++;
5057             if(!irccasecmp(argv[i], "nodelete"))
5058                 search->flags |= CHANNEL_NODELETE;
5059             else if(!irccasecmp(argv[i], "suspended"))
5060                 search->flags |= CHANNEL_SUSPENDED;
5061             else if(!irccasecmp(argv[i], "unreviewed"))
5062                 search->flags |= CHANNEL_UNREVIEWED;
5063             else
5064             {
5065                 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5066                 goto fail;
5067             }
5068         }
5069         else if(!irccasecmp(argv[i], "limit"))
5070             search->limit = strtoul(argv[++i], NULL, 10);
5071         else
5072         {
5073             send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5074             goto fail;
5075         }
5076     }
5077
5078     if(search->name && !strcmp(search->name, "*"))
5079         search->name = 0;
5080     if(search->registrar && !strcmp(search->registrar, "*"))
5081         search->registrar = 0;
5082
5083     return search;
5084   fail:
5085     free(search);
5086     return NULL;
5087 }
5088
5089 static int
5090 chanserv_channel_match(struct chanData *channel, search_t search)
5091 {
5092     const char *name = channel->channel->name;
5093     if((search->name && !match_ircglob(name, search->name)) ||
5094        (search->registrar && !channel->registrar) ||
5095        (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5096        (search->unvisited && (now - channel->visited) < search->unvisited) ||
5097        (search->registered && (now - channel->registered) > search->registered) ||
5098        (search->flags && ((search->flags & channel->flags) != search->flags)))
5099         return 0;
5100
5101     return 1;
5102 }
5103
5104 static unsigned int
5105 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5106 {
5107     struct chanData *channel;
5108     unsigned int matches = 0;
5109
5110     for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5111     {
5112         if(!chanserv_channel_match(channel, search))
5113             continue;
5114         matches++;
5115         smf(channel, data);
5116     }
5117
5118     return matches;
5119 }
5120
5121 static void
5122 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5123 {
5124 }
5125
5126 static void
5127 search_print(struct chanData *channel, void *data)
5128 {
5129     send_message_type(4, data, chanserv, "%s", channel->channel->name);
5130 }
5131
5132 static CHANSERV_FUNC(cmd_search)
5133 {
5134     search_t search;
5135     unsigned int matches;
5136     channel_search_func action;
5137
5138     REQUIRE_PARAMS(3);
5139
5140     if(!irccasecmp(argv[1], "count"))
5141         action = search_count;
5142     else if(!irccasecmp(argv[1], "print"))
5143         action = search_print;
5144     else
5145     {
5146         reply("CSMSG_ACTION_INVALID", argv[1]);
5147         return 0;
5148     }
5149
5150     search = chanserv_search_create(user, argc - 2, argv + 2);
5151     if(!search)
5152         return 0;
5153
5154     if(action == search_count)
5155         search->limit = INT_MAX;
5156
5157     if(action == search_print)
5158         reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5159
5160     matches = chanserv_channel_search(search, action, user);
5161
5162     if(matches)
5163         reply("MSG_MATCH_COUNT", matches);
5164     else
5165         reply("MSG_NO_MATCHES");
5166
5167     free(search);
5168     return 1;
5169 }
5170
5171 static CHANSERV_FUNC(cmd_unvisited)
5172 {
5173     struct chanData *cData;
5174     time_t interval = chanserv_conf.channel_expire_delay;
5175     char buffer[INTERVALLEN];
5176     unsigned int limit = 25, matches = 0;
5177
5178     if(argc > 1)
5179     {
5180         interval = ParseInterval(argv[1]);
5181         if(argc > 2)
5182             limit = atoi(argv[2]);
5183     }
5184
5185     intervalString(buffer, interval, user->handle_info);
5186     reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5187
5188     for(cData = channelList; cData && matches < limit; cData = cData->next)
5189     {
5190         if((now - cData->visited) < interval)
5191             continue;
5192
5193         intervalString(buffer, now - cData->visited, user->handle_info);
5194         reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5195         matches++;
5196     }
5197
5198     return 1;
5199 }
5200
5201 static MODCMD_FUNC(chan_opt_defaulttopic)
5202 {
5203     if(argc > 1)
5204     {
5205         char *topic;
5206
5207         if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5208         {
5209             reply("CSMSG_TOPIC_LOCKED", channel->name);
5210             return 0;
5211         }
5212
5213         topic = unsplit_string(argv+1, argc-1, NULL);
5214
5215         free(channel->channel_info->topic);
5216         if(topic[0] == '*' && topic[1] == 0)
5217         {
5218             topic = channel->channel_info->topic = NULL;
5219         }
5220         else
5221         {
5222             topic = channel->channel_info->topic = strdup(topic);
5223             if(channel->channel_info->topic_mask
5224                && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5225                 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5226         }
5227         SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5228     }
5229
5230     if(channel->channel_info->topic)
5231         reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5232     else
5233         reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5234     return 1;
5235 }
5236
5237 static MODCMD_FUNC(chan_opt_topicmask)
5238 {
5239     if(argc > 1)
5240     {
5241         struct chanData *cData = channel->channel_info;
5242         char *mask;
5243
5244         if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5245         {
5246             reply("CSMSG_TOPIC_LOCKED", channel->name);
5247             return 0;
5248         }
5249
5250         mask = unsplit_string(argv+1, argc-1, NULL);
5251
5252         if(cData->topic_mask)
5253             free(cData->topic_mask);
5254         if(mask[0] == '*' && mask[1] == 0)
5255         {
5256             cData->topic_mask = 0;
5257         }
5258         else
5259         {
5260             cData->topic_mask = strdup(mask);
5261             if(!cData->topic)
5262                 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5263             else if(!match_ircglob(cData->topic, cData->topic_mask))
5264                 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5265         }
5266     }
5267
5268     if(channel->channel_info->topic_mask)
5269         reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5270     else
5271         reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5272     return 1;
5273 }
5274
5275 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5276 {
5277     if(argc > 1)
5278     {
5279         char *greeting = unsplit_string(argv+1, argc-1, NULL);
5280         char *previous;
5281
5282         previous = *data;
5283         if(greeting[0] == '*' && greeting[1] == 0)
5284             *data = NULL;
5285         else
5286         {
5287             unsigned int length = strlen(greeting);
5288             if(length > chanserv_conf.greeting_length)
5289             {
5290                 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5291                 return 0;
5292             }
5293             *data = strdup(greeting);
5294         }
5295         if(previous)
5296             free(previous);
5297     }
5298
5299     if(*data)
5300         reply(name, *data);
5301     else
5302         reply(name, user_find_message(user, "MSG_NONE"));
5303     return 1;
5304 }
5305
5306 static MODCMD_FUNC(chan_opt_greeting)
5307 {
5308     return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5309 }
5310
5311 static MODCMD_FUNC(chan_opt_usergreeting)
5312 {
5313     return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5314 }
5315
5316 static MODCMD_FUNC(chan_opt_modes)
5317 {
5318     struct mod_chanmode *new_modes;
5319     char modes[MODELEN];
5320
5321     if(argc > 1)
5322     {
5323         if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5324         {
5325             reply("CSMSG_NO_ACCESS");
5326             return 0;
5327         }
5328         if(argv[1][0] == '*' && argv[1][1] == 0)
5329         {
5330             memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5331         }
5332         else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
5333         {
5334             reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5335             return 0;
5336         }
5337         else if(new_modes->argc > 1)
5338         {
5339             reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5340             mod_chanmode_free(new_modes);
5341             return 0;
5342         }
5343         else
5344         {
5345             channel->channel_info->modes = *new_modes;
5346             modcmd_chanmode_announce(new_modes);
5347             mod_chanmode_free(new_modes);
5348         }
5349     }
5350
5351     mod_chanmode_format(&channel->channel_info->modes, modes);
5352     if(modes[0])
5353         reply("CSMSG_SET_MODES", modes);
5354     else
5355         reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5356     return 1;
5357 }
5358
5359 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5360 static int
5361 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5362 {
5363     struct chanData *cData = channel->channel_info;
5364     int value;
5365
5366     if(argc > 1)
5367     {
5368         /* Set flag according to value. */
5369         if(enabled_string(argv[1]))
5370         {
5371             cData->flags |= mask;
5372             value = 1;
5373         }
5374         else if(disabled_string(argv[1]))
5375         {
5376             cData->flags &= ~mask;
5377             value = 0;
5378         }
5379         else
5380         {
5381             reply("MSG_INVALID_BINARY", argv[1]);
5382             return 0;
5383         }
5384     }
5385     else
5386     {
5387         /* Find current option value. */
5388         value = (cData->flags & mask) ? 1 : 0;
5389     }
5390
5391     if(value)
5392         reply(name, user_find_message(user, "MSG_ON"));
5393     else
5394         reply(name, user_find_message(user, "MSG_OFF"));
5395     return 1;
5396 }
5397
5398 static MODCMD_FUNC(chan_opt_nodelete)
5399 {
5400     if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5401     {
5402         reply("MSG_SETTING_PRIVILEGED", argv[0]);
5403         return 0;
5404     }
5405
5406     CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5407 }
5408
5409 static MODCMD_FUNC(chan_opt_dynlimit)
5410 {
5411     CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5412 }
5413
5414 static MODCMD_FUNC(chan_opt_offchannel)
5415 {
5416     struct chanData *cData = channel->channel_info;
5417     int value;
5418
5419     if(argc > 1)
5420     {
5421         /* Set flag according to value. */
5422         if(enabled_string(argv[1]))
5423         {
5424             if(!IsOffChannel(cData))
5425                 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5426             cData->flags |= CHANNEL_OFFCHANNEL;
5427             value = 1;
5428         }
5429         else if(disabled_string(argv[1]))
5430         {
5431             if(IsOffChannel(cData))
5432             {
5433                 struct mod_chanmode change;
5434                 mod_chanmode_init(&change);
5435                 change.argc = 1;
5436                 change.args[0].mode = MODE_CHANOP;
5437                 change.args[0].u.member = AddChannelUser(chanserv, channel);
5438                 mod_chanmode_announce(chanserv, channel, &change);
5439             }
5440             cData->flags &= ~CHANNEL_OFFCHANNEL;
5441             value = 0;
5442         }
5443         else
5444         {
5445             reply("MSG_INVALID_BINARY", argv[1]);
5446             return 0;
5447         }
5448     }
5449     else
5450     {
5451         /* Find current option value. */
5452         value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5453     }
5454
5455     if(value)
5456         reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5457     else
5458         reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5459     return 1;
5460 }
5461
5462 static MODCMD_FUNC(chan_opt_unreviewed)
5463 {
5464     struct chanData *cData = channel->channel_info;
5465     int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5466
5467     if(argc > 1)
5468     {
5469         int new_value;
5470
5471         /* The two directions can have different ACLs. */
5472         if(enabled_string(argv[1]))
5473             new_value = 1;
5474         else if(disabled_string(argv[1]))
5475             new_value = 0;
5476         else
5477         {
5478             reply("MSG_INVALID_BINARY", argv[1]);
5479             return 0;
5480         }
5481
5482         if (new_value != value)
5483         {
5484             struct svccmd *subcmd;
5485             char subcmd_name[32];
5486
5487             snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5488             subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5489             if(!subcmd)
5490             {
5491                 reply("MSG_COMMAND_DISABLED", subcmd_name);
5492                 return 0;
5493             }
5494             else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5495                 return 0;
5496
5497             if (new_value)
5498                 cData->flags |= CHANNEL_UNREVIEWED;
5499             else
5500             {
5501                 free(cData->registrar);
5502                 cData->registrar = strdup(user->handle_info->handle);
5503                 cData->flags &= ~CHANNEL_UNREVIEWED;
5504             }
5505             value = new_value;
5506         }
5507     }
5508
5509     if(value)
5510         reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5511     else
5512         reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5513     return 1;
5514 }
5515
5516 static MODCMD_FUNC(chan_opt_defaults)
5517 {
5518     struct userData *uData;
5519     struct chanData *cData;
5520     const char *confirm;
5521     enum levelOption lvlOpt;
5522     enum charOption chOpt;
5523
5524     cData = channel->channel_info;
5525     uData = GetChannelUser(cData, user->handle_info);
5526     if(!uData || (uData->access < UL_OWNER))
5527     {
5528         reply("CSMSG_OWNER_DEFAULTS", channel->name);
5529         return 0;
5530     }
5531     confirm = make_confirmation_string(uData);
5532     if((argc < 2) || strcmp(argv[1], confirm))
5533     {
5534         reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5535         return 0;
5536     }
5537     cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5538         | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5539     cData->modes = chanserv_conf.default_modes;
5540     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5541         cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5542     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5543         cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5544     reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5545     return 1;
5546 }
5547
5548 static int
5549 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5550 {
5551     struct chanData *cData = channel->channel_info;
5552     struct userData *uData;
5553     unsigned short value;
5554
5555     if(argc > 1)
5556     {
5557         if(!check_user_level(channel, user, option, 1, 1))
5558         {
5559             reply("CSMSG_CANNOT_SET");
5560             return 0;
5561         }
5562         value = user_level_from_name(argv[1], UL_OWNER+1);
5563         if(!value && strcmp(argv[1], "0"))
5564         {
5565             reply("CSMSG_INVALID_ACCESS", argv[1]);
5566             return 0;
5567         }
5568         uData = GetChannelUser(cData, user->handle_info);
5569         if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5570         {
5571             reply("CSMSG_BAD_SETLEVEL");
5572             return 0;
5573         }
5574         switch(option)
5575         {
5576         case lvlGiveVoice:
5577             if(value > cData->lvlOpts[lvlGiveOps])
5578             {
5579                 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5580                 return 0;
5581             }
5582             break;
5583         case lvlGiveOps:
5584             if(value < cData->lvlOpts[lvlGiveVoice])
5585             {
5586                 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5587                 return 0;
5588             }
5589             break;
5590         case lvlSetters:
5591             /* This test only applies to owners, since non-owners
5592              * trying to set an option to above their level get caught
5593              * by the CSMSG_BAD_SETLEVEL test above.
5594              */
5595             if(value > uData->access)
5596             {
5597                 reply("CSMSG_BAD_SETTERS");
5598                 return 0;
5599             }
5600             break;
5601         default:
5602             break;
5603         }
5604         cData->lvlOpts[option] = value;
5605     }
5606     reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5607     return argc > 1;
5608 }
5609
5610 static MODCMD_FUNC(chan_opt_enfops)
5611 {
5612     return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5613 }
5614
5615 static MODCMD_FUNC(chan_opt_giveops)
5616 {
5617     return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5618 }
5619
5620 static MODCMD_FUNC(chan_opt_enfmodes)
5621 {
5622     return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5623 }
5624
5625 static MODCMD_FUNC(chan_opt_enftopic)
5626 {
5627     return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5628 }
5629
5630 static MODCMD_FUNC(chan_opt_pubcmd)
5631 {
5632     return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5633 }
5634
5635 static MODCMD_FUNC(chan_opt_setters)
5636 {
5637     return channel_level_option(lvlSetters, CSFUNC_ARGS);
5638 }
5639
5640 static MODCMD_FUNC(chan_opt_ctcpusers)
5641 {
5642     return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5643 }
5644
5645 static MODCMD_FUNC(chan_opt_userinfo)
5646 {
5647     return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5648 }
5649
5650 static MODCMD_FUNC(chan_opt_givevoice)
5651 {
5652     return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5653 }
5654
5655 static MODCMD_FUNC(chan_opt_topicsnarf)
5656 {
5657     return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5658 }
5659
5660 static MODCMD_FUNC(chan_opt_inviteme)
5661 {
5662     return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5663 }
5664
5665 static int
5666 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5667 {
5668     struct chanData *cData = channel->channel_info;
5669     int count = charOptions[option].count, index;
5670
5671     if(argc > 1)
5672     {
5673         index = atoi(argv[1]);
5674
5675         if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5676         {
5677             reply("CSMSG_INVALID_NUMERIC", index);
5678             /* Show possible values. */
5679             for(index = 0; index < count; index++)
5680                 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5681             return 0;
5682         }
5683
5684         cData->chOpts[option] = charOptions[option].values[index].value;
5685     }
5686     else
5687     {
5688         /* Find current option value. */
5689       find_value:
5690         for(index = 0;
5691             (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5692             index++);
5693         if(index == count)
5694         {
5695             /* Somehow, the option value is corrupt; reset it to the default. */
5696             cData->chOpts[option] = charOptions[option].default_value;
5697             goto find_value;
5698         }
5699     }
5700
5701     reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5702     return 1;
5703 }
5704
5705 static MODCMD_FUNC(chan_opt_protect)
5706 {
5707     return channel_multiple_option(chProtect, CSFUNC_ARGS);
5708 }
5709
5710 static MODCMD_FUNC(chan_opt_toys)
5711 {
5712     return channel_multiple_option(chToys, CSFUNC_ARGS);
5713 }
5714
5715 static MODCMD_FUNC(chan_opt_ctcpreaction)
5716 {
5717     return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5718 }
5719
5720 static MODCMD_FUNC(chan_opt_topicrefresh)
5721 {
5722     return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5723 }
5724
5725 static struct svccmd_list set_shows_list;
5726
5727 static void
5728 handle_svccmd_unbind(struct svccmd *target) {
5729     unsigned int ii;
5730     for(ii=0; ii<set_shows_list.used; ++ii)
5731         if(target == set_shows_list.list[ii])
5732             set_shows_list.used = 0;
5733 }
5734
5735 static CHANSERV_FUNC(cmd_set)
5736 {
5737     struct svccmd *subcmd;
5738     char buf[MAXLEN];
5739     unsigned int ii;
5740
5741     /* Check if we need to (re-)initialize set_shows_list. */
5742     if(!set_shows_list.used)
5743     {
5744         if(!set_shows_list.size)
5745         {
5746             set_shows_list.size = chanserv_conf.set_shows->used;
5747             set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5748         }
5749         for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5750         {
5751             const char *name = chanserv_conf.set_shows->list[ii];
5752             sprintf(buf, "%s %s", argv[0], name);
5753             subcmd = dict_find(cmd->parent->commands, buf, NULL);
5754             if(!subcmd)
5755             {
5756                 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5757                 continue;
5758             }
5759             svccmd_list_append(&set_shows_list, subcmd);
5760         }
5761     }
5762
5763     if(argc < 2)
5764     {
5765         reply("CSMSG_CHANNEL_OPTIONS");
5766         for(ii = 0; ii < set_shows_list.used; ii++)
5767         {
5768             subcmd = set_shows_list.list[ii];
5769             subcmd->command->func(user, channel, 1, argv+1, subcmd);
5770         }
5771         return 1;
5772     }
5773
5774     sprintf(buf, "%s %s", argv[0], argv[1]);
5775     subcmd = dict_find(cmd->parent->commands, buf, NULL);
5776     if(!subcmd)
5777     {
5778         reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5779         return 0;
5780     }
5781     if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5782     {
5783         reply("CSMSG_NO_ACCESS");
5784         return 0;
5785     }
5786
5787     argv[0] = "";
5788     argv[1] = buf;
5789     return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5790 }
5791
5792 static int
5793 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5794 {
5795     struct userData *uData;
5796
5797     uData = GetChannelAccess(channel->channel_info, user->handle_info);
5798     if(!uData)
5799     {
5800         reply("CSMSG_NOT_USER", channel->name);
5801         return 0;
5802     }
5803
5804     if(argc < 2)
5805     {
5806         /* Just show current option value. */
5807     }
5808     else if(enabled_string(argv[1]))
5809     {
5810         uData->flags |= mask;
5811     }
5812     else if(disabled_string(argv[1]))
5813     {
5814         uData->flags &= ~mask;
5815     }
5816     else
5817     {
5818         reply("MSG_INVALID_BINARY", argv[1]);
5819         return 0;
5820     }
5821
5822     reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5823     return 1;
5824 }
5825
5826 static MODCMD_FUNC(user_opt_noautoop)
5827 {
5828     struct userData *uData;
5829
5830     uData = GetChannelAccess(channel->channel_info, user->handle_info);
5831     if(!uData)
5832     {
5833         reply("CSMSG_NOT_USER", channel->name);
5834         return 0;
5835     }
5836     if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5837         return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5838     else
5839         return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5840 }
5841
5842 static MODCMD_FUNC(user_opt_autoinvite)
5843 {
5844     return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5845 }
5846
5847 static MODCMD_FUNC(user_opt_info)
5848 {
5849     struct userData *uData;
5850     char *infoline;
5851
5852     uData = GetChannelAccess(channel->channel_info, user->handle_info);
5853
5854     if(!uData)
5855     {
5856         /* If they got past the command restrictions (which require access)
5857          * but fail this test, we have some fool with security override on.
5858          */
5859         reply("CSMSG_NOT_USER", channel->name);
5860         return 0;
5861     }
5862
5863     if(argc > 1)
5864     {
5865         size_t bp;
5866         infoline = unsplit_string(argv + 1, argc - 1, NULL);
5867         if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5868         {
5869             reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5870             return 0;
5871         }
5872         bp = strcspn(infoline, "\001");
5873         if(infoline[bp])
5874         {
5875             reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5876             return 0;
5877         }
5878         if(uData->info)
5879             free(uData->info);
5880         if(infoline[0] == '*' && infoline[1] == 0)
5881             uData->info = NULL;
5882         else
5883             uData->info = strdup(infoline);
5884     }
5885     if(uData->info)
5886         reply("CSMSG_USET_INFO", uData->info);
5887     else
5888         reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5889     return 1;
5890 }
5891
5892 struct svccmd_list uset_shows_list;
5893
5894 static CHANSERV_FUNC(cmd_uset)
5895 {
5896     struct svccmd *subcmd;
5897     char buf[MAXLEN];
5898     unsigned int ii;
5899
5900     /* Check if we need to (re-)initialize uset_shows_list. */
5901     if(!uset_shows_list.used)
5902     {
5903         char *options[] =
5904         {
5905             "NoAutoOp", "AutoInvite", "Info"
5906         };
5907
5908         if(!uset_shows_list.size)
5909         {
5910             uset_shows_list.size = ArrayLength(options);
5911             uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5912         }
5913         for(ii = 0; ii < ArrayLength(options); ii++)
5914         {
5915             const char *name = options[ii];
5916             sprintf(buf, "%s %s", argv[0], name);
5917             subcmd = dict_find(cmd->parent->commands, buf, NULL);
5918             if(!subcmd)
5919             {
5920                 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5921                 continue;
5922             }
5923             svccmd_list_append(&uset_shows_list, subcmd);
5924         }
5925     }
5926
5927     if(argc < 2)
5928     {
5929         /* Do this so options are presented in a consistent order. */
5930         reply("CSMSG_USER_OPTIONS");
5931         for(ii = 0; ii < uset_shows_list.used; ii++)
5932             uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5933         return 1;
5934     }
5935
5936     sprintf(buf, "%s %s", argv[0], argv[1]);
5937     subcmd = dict_find(cmd->parent->commands, buf, NULL);
5938     if(!subcmd)
5939     {
5940         reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5941         return 0;
5942     }
5943
5944     return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5945 }
5946
5947 static CHANSERV_FUNC(cmd_giveownership)
5948 {
5949     struct handle_info *new_owner_hi;
5950     struct userData *new_owner;
5951     struct userData *curr_user;
5952     struct userData *invoker;
5953     struct chanData *cData = channel->channel_info;
5954     struct do_not_register *dnr;
5955     const char *confirm;
5956     unsigned int force;
5957     unsigned short co_access;
5958     char reason[MAXLEN];
5959
5960     REQUIRE_PARAMS(2);
5961     curr_user = GetChannelAccess(cData, user->handle_info);
5962     force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5963     if(!curr_user || (curr_user->access != UL_OWNER))
5964     {
5965         struct userData *owner = NULL;
5966         for(curr_user = channel->channel_info->users;
5967             curr_user;
5968             curr_user = curr_user->next)
5969         {
5970             if(curr_user->access != UL_OWNER)
5971                 continue;
5972             if(owner)
5973             {
5974                 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5975                 return 0;
5976             }
5977             owner = curr_user;
5978         }
5979         curr_user = owner;
5980     }
5981     else if(!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5982     {
5983         char delay[INTERVALLEN];
5984         intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5985         reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5986         return 0;
5987     }
5988     if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5989         return 0;
5990     if(new_owner_hi == user->handle_info)
5991     {
5992         reply("CSMSG_NO_TRANSFER_SELF");
5993         return 0;
5994     }
5995     new_owner = GetChannelAccess(cData, new_owner_hi);
5996     if(!new_owner)
5997     {
5998         if(force)
5999         {
6000             new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6001         }
6002         else
6003         {
6004             reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6005             return 0;
6006         }
6007     }
6008     if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6009     {
6010         reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6011         return 0;
6012     }
6013     if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6014         if(!IsHelping(user))
6015             reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6016         else
6017             chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6018         return 0;
6019     }
6020     invoker = GetChannelUser(cData, user->handle_info);
6021     if(invoker->access <= UL_OWNER)
6022     {
6023         confirm = make_confirmation_string(curr_user);
6024         if((argc < 3) || strcmp(argv[2], confirm))
6025         {
6026             reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6027             return 0;
6028         }
6029     }
6030     if(new_owner->access >= UL_COOWNER)
6031         co_access = new_owner->access;
6032     else
6033         co_access = UL_COOWNER;
6034     new_owner->access = UL_OWNER;
6035     if(curr_user)
6036         curr_user->access = co_access;
6037     cData->ownerTransfer = now;
6038     reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6039     sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6040     global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6041     return 1;
6042 }
6043
6044 static CHANSERV_FUNC(cmd_suspend)
6045 {
6046     struct handle_info *hi;
6047     struct userData *self, *real_self, *target;
6048     unsigned int override = 0;
6049
6050     REQUIRE_PARAMS(2);
6051     if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6052     self = GetChannelUser(channel->channel_info, user->handle_info);
6053     real_self = GetChannelAccess(channel->channel_info, user->handle_info);
6054     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6055     {
6056         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6057         return 0;
6058     }
6059     if(target->access >= self->access)
6060     {
6061         reply("MSG_USER_OUTRANKED", hi->handle);
6062         return 0;
6063     }
6064     if(target->flags & USER_SUSPENDED)
6065     {
6066         reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6067         return 0;
6068     }
6069     if(target->present)
6070     {
6071         target->present = 0;
6072         target->seen = now;
6073     }
6074     if(!real_self || target->access >= real_self->access)
6075         override = CMD_LOG_OVERRIDE;
6076     target->flags |= USER_SUSPENDED;
6077     reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6078     return 1 | override;
6079 }
6080
6081 static CHANSERV_FUNC(cmd_unsuspend)
6082 {
6083     struct handle_info *hi;
6084     struct userData *self, *real_self, *target;
6085     unsigned int override = 0;
6086
6087     REQUIRE_PARAMS(2);
6088     if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6089     self = GetChannelUser(channel->channel_info, user->handle_info);
6090     real_self = GetChannelAccess(channel->channel_info, user->handle_info);
6091     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6092     {
6093         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6094         return 0;
6095     }
6096     if(target->access >= self->access)
6097     {
6098         reply("MSG_USER_OUTRANKED", hi->handle);
6099         return 0;
6100     }
6101     if(!(target->flags & USER_SUSPENDED))
6102     {
6103         reply("CSMSG_NOT_SUSPENDED", hi->handle);
6104         return 0;
6105     }
6106     if(!real_self || target->access >= real_self->access)
6107         override = CMD_LOG_OVERRIDE;
6108     target->flags &= ~USER_SUSPENDED;
6109     scan_user_presence(target, NULL);
6110     reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6111     return 1 | override;
6112 }
6113
6114 static MODCMD_FUNC(cmd_deleteme)
6115 {
6116     struct handle_info *hi;
6117     struct userData *target;
6118     const char *confirm_string;
6119     unsigned short access;
6120     char *channel_name;
6121
6122     hi = user->handle_info;
6123     if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6124     {
6125         reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6126         return 0;
6127     }
6128     if(target->access == UL_OWNER)
6129     {
6130         reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6131         return 0;
6132     }
6133     confirm_string = make_confirmation_string(target);
6134     if((argc < 2) || strcmp(argv[1], confirm_string))
6135     {
6136         reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6137         return 0;
6138     }
6139     access = target->access;
6140     channel_name = strdup(channel->name);
6141     del_channel_user(target, 1);
6142     reply("CSMSG_DELETED_YOU", access, channel_name);
6143     free(channel_name);
6144     return 1;
6145 }
6146
6147 static void
6148 chanserv_refresh_topics(UNUSED_ARG(void *data))
6149 {
6150     unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
6151     struct chanData *cData;
6152     char opt;
6153
6154     for(cData = channelList; cData; cData = cData->next)
6155     {
6156         if(IsSuspended(cData))
6157             continue;
6158         opt = cData->chOpts[chTopicRefresh];
6159         if(opt == 'n')
6160             continue;
6161         if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6162             continue;
6163         if(cData->topic)
6164             SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6165         cData->last_refresh = refresh_num;
6166     }
6167     timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6168 }
6169
6170 static CHANSERV_FUNC(cmd_unf)
6171 {
6172     if(channel)
6173     {
6174         char response[MAXLEN];
6175         const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6176         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6177         irc_privmsg(cmd->parent->bot, channel->name, response);
6178     }
6179     else
6180         reply("CSMSG_UNF_RESPONSE");
6181     return 1;
6182 }
6183
6184 static CHANSERV_FUNC(cmd_ping)
6185 {
6186     if(channel)
6187     {
6188         char response[MAXLEN];
6189         const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6190         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6191         irc_privmsg(cmd->parent->bot, channel->name, response);
6192     }
6193     else
6194         reply("CSMSG_PING_RESPONSE");
6195     return 1;
6196 }
6197
6198 static CHANSERV_FUNC(cmd_wut)
6199 {
6200     if(channel)
6201     {
6202         char response[MAXLEN];
6203         const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6204         sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6205         irc_privmsg(cmd->parent->bot, channel->name, response);
6206     }
6207     else
6208         reply("CSMSG_WUT_RESPONSE");
6209     return 1;
6210 }
6211
6212 static CHANSERV_FUNC(cmd_8ball)
6213 {
6214     unsigned int i, j, accum;
6215     const char *resp;
6216
6217     REQUIRE_PARAMS(2);
6218     accum = 0;
6219     for(i=1; i<argc; i++)
6220         for(j=0; argv[i][j]; j++)
6221             accum = (accum << 5) - accum + toupper(argv[i][j]);
6222     resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6223     if(channel)
6224     {
6225         char response[MAXLEN];
6226         sprintf(response, "\ 2%s\ 2: %s", user->nick, resp);
6227         irc_privmsg(cmd->parent->bot, channel->name, response);
6228     }
6229     else
6230         send_message_type(4, user, cmd->parent->bot, "%s", resp);
6231     return 1;
6232 }
6233
6234 static CHANSERV_FUNC(cmd_d)
6235 {
6236     unsigned long sides, count, modifier, ii, total;
6237     char response[MAXLEN], *sep;
6238     const char *fmt;
6239
6240     REQUIRE_PARAMS(2);
6241     if((count = strtoul(argv[1], &sep, 10)) < 1)
6242         goto no_dice;
6243     if(sep[0] == 0)
6244     {
6245         if(count == 1)
6246             goto no_dice;
6247         sides = count;
6248         count = 1;
6249         modifier = 0;
6250     }
6251     else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6252             && (sides = strtoul(sep+1, &sep, 10)) > 1)
6253     {
6254         if(sep[0] == 0)
6255             modifier = 0;
6256         else if((sep[0] == '-') && isdigit(sep[1]))
6257             modifier = strtoul(sep, NULL, 10);
6258         else if((sep[0] == '+') && isdigit(sep[1]))
6259             modifier = strtoul(sep+1, NULL, 10);
6260         else
6261             goto no_dice;
6262     }
6263     else
6264     {
6265       no_dice:
6266         reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6267         return 0;
6268     }
6269     if(count > 10)
6270     {
6271         reply("CSMSG_BAD_DICE_COUNT", count, 10);
6272         return 0;
6273     }
6274     for(total = ii = 0; ii < count; ++ii)
6275         total += (rand() % sides) + 1;
6276     total += modifier;
6277
6278     if((count > 1) || modifier)
6279     {
6280         fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6281         sprintf(response, fmt, total, count, sides, modifier);
6282     }
6283     else
6284     {
6285         fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6286         sprintf(response, fmt, total, sides);
6287     }
6288     if(channel)
6289         send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6290     else
6291         send_message_type(4, user, cmd->parent->bot, "%s", response);
6292     return 1;
6293 }
6294
6295 static CHANSERV_FUNC(cmd_huggle)
6296 {
6297     /* CTCP must be via PRIVMSG, never notice */
6298     if(channel)
6299         send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6300     else
6301         send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6302     return 1;
6303 }
6304
6305 static void
6306 chanserv_adjust_limit(void *data)
6307 {
6308     struct mod_chanmode change;
6309     struct chanData *cData = data;
6310     struct chanNode *channel = cData->channel;
6311     unsigned int limit;
6312
6313     if(IsSuspended(cData))
6314         return;
6315
6316     cData->limitAdjusted = now;
6317     limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6318     if(cData->modes.modes_set & MODE_LIMIT)
6319     {
6320         if(limit > cData->modes.new_limit)
6321             limit = cData->modes.new_limit;
6322         else if(limit == cData->modes.new_limit)
6323             return;
6324     }
6325
6326     mod_chanmode_init(&change);
6327     change.modes_set = MODE_LIMIT;
6328     change.new_limit = limit;
6329     mod_chanmode_announce(chanserv, channel, &change);
6330 }
6331
6332 static void
6333 handle_new_channel(struct chanNode *channel)
6334 {
6335     struct chanData *cData;
6336
6337     if(!(cData = channel->channel_info))
6338         return;
6339
6340     if(cData->modes.modes_set || cData->modes.modes_clear)
6341         mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6342
6343     if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6344         SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6345 }
6346
6347 /* Welcome to my worst nightmare. Warning: Read (or modify)
6348    the code below at your own risk. */
6349 static int
6350 handle_join(struct modeNode *mNode)
6351 {
6352     struct mod_chanmode change;
6353     struct userNode *user = mNode->user;
6354     struct chanNode *channel = mNode->channel;
6355     struct chanData *cData;
6356     struct userData *uData = NULL;
6357     struct banData *bData;
6358     struct handle_info *handle;
6359     unsigned int modes = 0, info = 0;
6360     char *greeting;
6361
6362     if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6363         return 0;
6364
6365     cData = channel->channel_info;
6366     if(channel->members.used > cData->max)
6367         cData->max = channel->members.used;
6368
6369     /* Check for bans.  If they're joining through a ban, one of two
6370      * cases applies:
6371      *   1: Join during a netburst, by riding the break.  Kick them
6372      *      unless they have ops or voice in the channel.
6373      *   2: They're allowed to join through the ban (an invite in
6374      *   ircu2.10, or a +e on Hybrid, or something).
6375      * If they're not joining through a ban, and the banlist is not
6376      * full, see if they're on the banlist for the channel.  If so,
6377      * kickban them.
6378      */
6379     if(user->uplink->burst && !mNode->modes)
6380     {
6381         unsigned int ii;
6382         for(ii = 0; ii < channel->banlist.used; ii++)
6383         {
6384             if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6385             {
6386                 /* Riding a netburst.  Naughty. */
6387                 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6388                 return 1;
6389             }
6390         }
6391     }
6392
6393     mod_chanmode_init(&change);
6394     change.argc = 1;
6395     if(channel->banlist.used < MAXBANS)
6396     {
6397         /* Not joining through a ban. */
6398         for(bData = cData->bans;
6399             bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6400             bData = bData->next);
6401
6402         if(bData)
6403         {
6404             char kick_reason[MAXLEN];
6405             sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6406
6407             bData->triggered = now;
6408             if(bData != cData->bans)
6409             {
6410                 /* Shuffle the ban to the head of the list. */
6411                 if(bData->next)
6412                     bData->next->prev = bData->prev;
6413                 if(bData->prev)
6414                     bData->prev->next = bData->next;
6415
6416                 bData->prev = NULL;
6417                 bData->next = cData->bans;
6418
6419                 if(cData->bans)
6420                     cData->bans->prev = bData;
6421                 cData->bans = bData;
6422             }
6423
6424             change.args[0].mode = MODE_BAN;
6425             change.args[0].u.hostmask = bData->mask;
6426             mod_chanmode_announce(chanserv, channel, &change);
6427             KickChannelUser(user, channel, chanserv, kick_reason);
6428             return 1;
6429         }
6430     }
6431
6432     /* ChanServ will not modify the limits in join-flooded channels.
6433        It will also skip DynLimit processing when the user (or srvx)
6434        is bursting in, because there are likely more incoming. */
6435     if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6436        && !user->uplink->burst
6437        && !channel->join_flooded
6438        && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6439     {
6440         /* The user count has begun "bumping" into the channel limit,
6441            so set a timer to raise the limit a bit. Any previous
6442            timers are removed so three incoming users within the delay
6443            results in one limit change, not three. */
6444
6445         timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6446         timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6447     }
6448
6449     if(channel->join_flooded)
6450     {
6451         /* don't automatically give ops or voice during a join flood */
6452     }
6453     else if(cData->lvlOpts[lvlGiveOps] == 0)
6454         modes |= MODE_CHANOP;
6455     else if(cData->lvlOpts[lvlGiveVoice] == 0)
6456         modes |= MODE_VOICE;
6457
6458     greeting = cData->greeting;
6459     if(user->handle_info)
6460     {
6461         handle = user->handle_info;
6462
6463         if(IsHelper(user) && !IsHelping(user))
6464         {
6465             unsigned int ii;
6466             for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6467             {
6468                 if(channel == chanserv_conf.support_channels.list[ii])
6469                 {
6470                     HANDLE_SET_FLAG(user->handle_info, HELPING);
6471                     break;
6472                 }
6473             }
6474         }
6475
6476         uData = GetTrueChannelAccess(cData, handle);
6477         if(uData && !IsUserSuspended(uData))
6478         {
6479             /* Ops and above were handled by the above case. */
6480             if(IsUserAutoOp(uData))
6481             {
6482                 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6483                     modes |= MODE_CHANOP;
6484                 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6485                     modes |= MODE_VOICE;
6486             }
6487             if(uData->access >= UL_PRESENT)
6488                 cData->visited = now;
6489             if(cData->user_greeting)
6490                 greeting = cData->user_greeting;
6491             if(uData->info
6492                && (uData->access >= cData->lvlOpts[lvlUserInfo])
6493                && ((now - uData->seen) >= chanserv_conf.info_delay)
6494                && !uData->present)
6495                 info = 1;
6496             uData->seen = now;
6497             uData->present = 1;
6498         }
6499     }
6500
6501     /* If user joining normally (not during burst), apply op or voice,
6502      * and send greeting/userinfo as appropriate.
6503      */
6504     if(!user->uplink->burst)
6505     {
6506         if(modes)
6507         {
6508             if(modes & MODE_CHANOP)
6509                 modes &= ~MODE_VOICE;
6510             change.args[0].mode = modes;
6511             change.args[0].u.member = mNode;
6512             mod_chanmode_announce(chanserv, channel, &change);
6513         }
6514         if(greeting)
6515             send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6516         if(uData && info)
6517             send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6518     }
6519     return 0;
6520 }
6521
6522 static void
6523 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6524 {
6525     struct mod_chanmode change;
6526     struct userData *channel;
6527     unsigned int ii, jj;
6528
6529     if(!user->handle_info)
6530         return;
6531
6532     mod_chanmode_init(&change);
6533     change.argc = 1;
6534     for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6535     {
6536         struct chanNode *cn;
6537         struct modeNode *mn;
6538         if(IsUserSuspended(channel)
6539            || IsSuspended(channel->channel)
6540            || !(cn = channel->channel->channel))
6541             continue;
6542
6543         mn = GetUserMode(cn, user);
6544         if(!mn)
6545         {
6546             if(!IsUserSuspended(channel)
6547                && IsUserAutoInvite(channel)
6548                && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6549                && !self->burst
6550                && !user->uplink->burst)
6551                 irc_invite(chanserv, user, cn);
6552             continue;
6553         }
6554
6555         if(channel->access >= UL_PRESENT)
6556             channel->channel->visited = now;
6557
6558         if(IsUserAutoOp(channel))
6559         {
6560             if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6561                 change.args[0].mode = MODE_CHANOP;
6562             else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6563                 change.args[0].mode = MODE_VOICE;
6564             else
6565                 change.args[0].mode = 0;
6566             change.args[0].u.member = mn;
6567             if(change.args[0].mode)
6568                 mod_chanmode_announce(chanserv, cn, &change);
6569         }
6570
6571         channel->seen = now;
6572         channel->present = 1;
6573     }
6574
6575     for(ii = 0; ii < user->channels.used; ++ii)
6576     {
6577         struct chanNode *channel = user->channels.list[ii]->channel;
6578         struct banData *ban;
6579
6580         if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6581            || !channel->channel_info
6582            || IsSuspended(channel->channel_info))
6583             continue;
6584         for(jj = 0; jj < channel->banlist.used; ++jj)
6585             if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6586                 break;
6587         if(jj < channel->banlist.used)
6588             continue;
6589         for(ban = channel->channel_info->bans; ban; ban = ban->next)
6590         {
6591             char kick_reason[MAXLEN];
6592             if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6593                 continue;
6594             change.args[0].mode = MODE_BAN;
6595             change.args[0].u.hostmask = ban->mask;
6596             mod_chanmode_announce(chanserv, channel, &change);
6597             sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6598             KickChannelUser(user, channel, chanserv, kick_reason);
6599             ban->triggered = now;
6600             break;
6601         }
6602     }
6603
6604     if(IsSupportHelper(user))
6605     {
6606         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6607         {
6608             if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6609             {
6610                 HANDLE_SET_FLAG(user->handle_info, HELPING);
6611                 break;
6612             }
6613         }
6614     }
6615 }
6616
6617 static void
6618 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6619 {
6620     struct chanData *cData;
6621     struct userData *uData;
6622
6623     cData = mn->channel->channel_info;
6624     if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6625         return;
6626
6627     if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6628     {
6629         /* Allow for a bit of padding so that the limit doesn't
6630            track the user count exactly, which could get annoying. */
6631         if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6632         {
6633             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6634             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6635         }
6636     }
6637
6638     if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6639     {
6640         scan_user_presence(uData, mn->user);
6641         uData->seen = now;
6642         if (uData->access >= UL_PRESENT)
6643             cData->visited = now;
6644     }
6645
6646     if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6647     {
6648         unsigned int ii, jj;
6649         for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6650         {
6651             for(jj = 0; jj < mn->user->channels.used; ++jj)
6652                 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6653                     break;
6654             if(jj < mn->user->channels.used)
6655                 break;
6656         }
6657         if(ii == chanserv_conf.support_channels.used)
6658             HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6659     }
6660 }
6661
6662 static void
6663 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6664 {
6665     struct userData *uData;
6666
6667     if(!channel->channel_info || !kicker || IsService(kicker)
6668        || (kicker == victim) || IsSuspended(channel->channel_info)
6669        || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6670         return;
6671
6672     if(protect_user(victim, kicker, channel->channel_info))
6673     {
6674         const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6675         KickChannelUser(kicker, channel, chanserv, reason);
6676     }
6677
6678     if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6679         uData->seen = now;
6680 }
6681
6682 static int
6683 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6684 {
6685     struct chanData *cData;
6686
6687     if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6688         return 0;
6689
6690     cData = channel->channel_info;
6691     if(bad_topic(channel, user, channel->topic))
6692     {
6693         send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6694         if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6695             SetChannelTopic(channel, chanserv, old_topic, 1);
6696         else if(cData->topic)
6697             SetChannelTopic(channel, chanserv, cData->topic, 1);
6698         return 1;
6699     }
6700     /* With topicsnarf, grab the topic and save it as the default topic. */
6701     if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6702     {
6703         free(cData->topic);
6704         cData->topic = strdup(channel->topic);
6705     }
6706     return 0;
6707 }
6708
6709 static void
6710 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6711 {
6712     struct mod_chanmode *bounce = NULL;
6713     unsigned int bnc, ii;
6714     char deopped = 0;
6715
6716     if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6717         return;
6718
6719     if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6720        && mode_lock_violated(&channel->channel_info->modes, change))
6721     {
6722         char correct[MAXLEN];
6723         bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6724         mod_chanmode_format(&channel->channel_info->modes, correct);
6725         send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6726     }
6727     for(ii = bnc = 0; ii < change->argc; ++ii)
6728     {
6729         if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6730         {
6731             const struct userNode *victim = change->args[ii].u.member->user;
6732             if(!protect_user(victim, user, channel->channel_info))
6733                 continue;
6734             if(!bounce)
6735                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6736             if(!deopped)
6737             {
6738                 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6739                 bounce->args[bnc].u.member = GetUserMode(channel, user);
6740                 if(bounce->args[bnc].u.member)
6741                     bnc++;
6742                 deopped = 1;
6743             }
6744             bounce->args[bnc].mode = MODE_CHANOP;
6745             bounce->args[bnc].u.member = change->args[ii].u.member;
6746             bnc++;
6747             send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6748         }
6749         else if(change->args[ii].mode & MODE_CHANOP)
6750         {
6751             const struct userNode *victim = change->args[ii].u.member->user;
6752             if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6753                 continue;
6754             if(!bounce)
6755                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6756             bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6757             bounce->args[bnc].u.member = change->args[ii].u.member;
6758             bnc++;
6759         }
6760         else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6761         {
6762             const char *ban = change->args[ii].u.hostmask;
6763             if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6764                 continue;
6765             if(!bounce)
6766                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6767             bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6768             bounce->args[bnc].u.hostmask = strdup(ban);
6769             bnc++;
6770             send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6771         }
6772     }
6773     if(bounce)
6774     {
6775         if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6776             mod_chanmode_announce(chanserv, channel, bounce);
6777         for(ii = 0; ii < change->argc; ++ii)
6778             if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6779                 free((char*)bounce->args[ii].u.hostmask);
6780         mod_chanmode_free(bounce);
6781     }
6782 }
6783
6784 static void
6785 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6786 {
6787     struct chanNode *channel;
6788     struct banData *bData;
6789     struct mod_chanmode change;
6790     unsigned int ii, jj;
6791     char kick_reason[MAXLEN];
6792
6793     mod_chanmode_init(&change);
6794     change.argc = 1;
6795     change.args[0].mode = MODE_BAN;
6796     for(ii = 0; ii < user->channels.used; ++ii)
6797     {
6798         channel = user->channels.list[ii]->channel;
6799         /* Need not check for bans if they're opped or voiced. */
6800         if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6801             continue;
6802         /* Need not check for bans unless channel registration is active. */
6803         if(!channel->channel_info || IsSuspended(channel->channel_info))
6804             continue;
6805         /* Look for a matching ban already on the channel. */
6806         for(jj = 0; jj < channel->banlist.used; ++jj)
6807             if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6808                 break;
6809         /* Need not act if we found one. */
6810         if(jj < channel->banlist.used)
6811             continue;
6812         /* Look for a matching ban in this channel. */
6813         for(bData = channel->channel_info->bans; bData; bData = bData->next)
6814         {
6815             if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6816                 continue;
6817             change.args[0].u.hostmask = bData->mask;
6818             mod_chanmode_announce(chanserv, channel, &change);
6819             sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6820             KickChannelUser(user, channel, chanserv, kick_reason);
6821             bData->triggered = now;
6822             break; /* we don't need to check any more bans in the channel */
6823         }
6824     }
6825 }
6826
6827 static void handle_rename(struct handle_info *handle, const char *old_handle)
6828 {
6829     struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6830
6831     if(dnr)
6832     {
6833         dict_remove2(handle_dnrs, old_handle, 1);
6834         safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6835         dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6836     }
6837 }
6838
6839 static void
6840 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6841 {
6842     struct userNode *h_user;
6843
6844     if(handle->channels)
6845     {
6846         for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6847             send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6848
6849         while(handle->channels)
6850             del_channel_user(handle->channels, 1);
6851     }
6852 }
6853
6854 static void
6855 handle_server_link(UNUSED_ARG(struct server *server))
6856 {
6857     struct chanData *cData;
6858
6859     for(cData = channelList; cData; cData = cData->next)
6860     {
6861         if(!IsSuspended(cData))
6862             cData->may_opchan = 1;
6863         if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6864            && !cData->channel->join_flooded
6865            && ((cData->channel->limit - cData->channel->members.used)
6866                < chanserv_conf.adjust_threshold))
6867         {
6868             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6869             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6870         }
6871     }
6872 }
6873
6874 static void
6875 chanserv_conf_read(void)
6876 {
6877     dict_t conf_node;
6878     const char *str;
6879     char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6880     struct mod_chanmode *change;
6881     struct string_list *strlist;
6882     struct chanNode *chan;
6883     unsigned int ii;
6884
6885     if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6886     {
6887         log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6888         return;
6889     }
6890     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6891         UnlockChannel(chanserv_conf.support_channels.list[ii]);
6892     chanserv_conf.support_channels.used = 0;
6893     if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6894     {
6895         for(ii = 0; ii < strlist->used; ++ii)
6896         {
6897             const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6898             if(!str2)
6899                 str2 = "+nt";
6900             chan = AddChannel(strlist->list[ii], now, str2, NULL);
6901             LockChannel(chan);
6902             channelList_append(&chanserv_conf.support_channels, chan);
6903         }
6904     }
6905     else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6906     {
6907         const char *str2;
6908         str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6909         if(!str2)
6910             str2 = "+nt";
6911         chan = AddChannel(str, now, str2, NULL);
6912         LockChannel(chan);
6913         channelList_append(&chanserv_conf.support_channels, chan);
6914     }
6915     str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6916     chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6917     str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6918     chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6919     str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6920     chanserv_conf.greeting_length = str ? atoi(str) : 200;
6921     str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6922     chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6923     str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6924     chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6925     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6926     chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6927     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6928     chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6929     str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
6930     chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
6931     str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6932     chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6933     str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6934     chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6935     str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6936     chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6937     str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6938     chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6939     str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6940     if(chanserv && str)
6941         NickChange(chanserv, str, 0);
6942     str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6943     chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6944     str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6945     chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6946     str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6947     chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6948     str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6949     chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6950     str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6951     chanserv_conf.max_owned = str ? atoi(str) : 5;
6952     str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6953     chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6954     str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6955     chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6956     str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6957     chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6958     str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6959     if(!str)
6960         str = "+nt";
6961     safestrncpy(mode_line, str, sizeof(mode_line));
6962     ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6963     if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
6964        && (change->argc < 2))
6965     {
6966         chanserv_conf.default_modes = *change;
6967         mod_chanmode_free(change);
6968     }
6969     free_string_list(chanserv_conf.set_shows);
6970     strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6971     if(strlist)
6972         strlist = string_list_copy(strlist);
6973     else
6974     {
6975         static const char *list[] = {
6976             /* free form text */
6977             "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6978             /* options based on user level */
6979             "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6980             "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6981             /* multiple choice options */
6982             "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6983             /* binary options */
6984             "DynLimit", "NoDelete",
6985             /* delimiter */
6986             NULL
6987         };
6988         unsigned int ii;
6989         strlist = alloc_string_list(ArrayLength(list)-1);
6990         for(ii=0; list[ii]; ii++)
6991             string_list_append(strlist, strdup(list[ii]));
6992     }
6993     chanserv_conf.set_shows = strlist;
6994     /* We don't look things up now, in case the list refers to options
6995      * defined by modules initialized after this point.  Just mark the
6996      * function list as invalid, so it will be initialized.
6997      */
6998     set_shows_list.used = 0;
6999     free_string_list(chanserv_conf.eightball);
7000     strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7001     if(strlist)
7002     {
7003         strlist = string_list_copy(strlist);
7004     }
7005     else
7006     {
7007         strlist = alloc_string_list(4);
7008         string_list_append(strlist, strdup("Yes."));
7009         string_list_append(strlist, strdup("No."));
7010         string_list_append(strlist, strdup("Maybe so."));
7011     }
7012     chanserv_conf.eightball = strlist;
7013     free_string_list(chanserv_conf.old_ban_names);
7014     strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7015     if(strlist)
7016         strlist = string_list_copy(strlist);
7017     else
7018         strlist = alloc_string_list(2);
7019     chanserv_conf.old_ban_names = strlist;
7020     str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7021     off_channel = str ? atoi(str) : 0;
7022 }
7023
7024 static void
7025 chanserv_note_type_read(const char *key, struct record_data *rd)
7026 {
7027     dict_t obj;
7028     struct note_type *ntype;
7029     const char *str;
7030
7031     if(!(obj = GET_RECORD_OBJECT(rd)))
7032     {
7033         log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7034         return;
7035     }
7036     if(!(ntype = chanserv_create_note_type(key)))
7037     {
7038         log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7039         return;
7040     }
7041
7042     /* Figure out set access */
7043     if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7044     {
7045         ntype->set_access_type = NOTE_SET_PRIVILEGED;
7046         ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7047     }
7048     else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7049     {
7050         ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7051         ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7052     }
7053     else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7054     {
7055         ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7056     }
7057     else
7058     {
7059         log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7060         ntype->set_access_type = NOTE_SET_PRIVILEGED;
7061         ntype->set_access.min_opserv = 0;
7062     }
7063
7064     /* Figure out visibility */
7065     if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7066         ntype->visible_type = NOTE_VIS_PRIVILEGED;
7067     else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7068         ntype->visible_type = NOTE_VIS_PRIVILEGED;
7069     else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7070         ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7071     else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7072         ntype->visible_type = NOTE_VIS_ALL;
7073     else
7074         ntype->visible_type = NOTE_VIS_PRIVILEGED;
7075
7076     str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7077     ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7078 }
7079
7080 static void
7081 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7082 {
7083     struct handle_info *handle;
7084     struct userData *uData;
7085     char *seen, *inf, *flags;
7086     time_t last_seen;
7087     unsigned short access;
7088
7089     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7090     {
7091         log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7092         return;
7093     }
7094
7095     access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7096     if(access > UL_OWNER)
7097     {
7098         log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7099         return;
7100     }
7101
7102     inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7103     seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7104     last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
7105     flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7106     handle = get_handle_info(key);
7107     if(!handle)
7108     {
7109         log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7110         return;
7111     }
7112
7113     uData = add_channel_user(chan, handle, access, last_seen, inf);
7114     uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7115 }
7116
7117 static void
7118 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7119 {
7120     struct banData *bData;
7121     char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7122     time_t set_time, triggered_time, expires_time;
7123
7124     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7125     {
7126         log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7127         return;
7128     }
7129
7130     set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7131     triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7132     s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7133     s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7134     owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7135     reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7136     if (!reason || !owner)
7137         return;
7138
7139     set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
7140     triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
7141     if(s_expires)
7142         expires_time = (time_t)strtoul(s_expires, NULL, 0);
7143     else if(s_duration)
7144         expires_time = set_time + atoi(s_duration);
7145     else
7146         expires_time = 0;
7147
7148     if(!reason || (expires_time && (expires_time < now)))
7149         return;
7150
7151     bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7152 }
7153
7154 static struct suspended *
7155 chanserv_read_suspended(dict_t obj)
7156 {
7157     struct suspended *suspended = calloc(1, sizeof(*suspended));
7158     char *str;
7159     dict_t previous;
7160
7161     str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7162     suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
7163     str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7164     suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
7165     str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7166     suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
7167     suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7168     suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7169     previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7170     suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7171     return suspended;
7172 }
7173
7174 static int
7175 chanserv_channel_read(const char *key, struct record_data *hir)
7176 {
7177     struct suspended *suspended;
7178     struct mod_chanmode *modes;
7179     struct chanNode *cNode;
7180     struct chanData *cData;
7181     struct dict *channel, *obj;
7182     char *str, *argv[10];
7183     dict_iterator_t it;
7184     unsigned int argc;
7185
7186     channel = hir->d.object;
7187
7188     str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7189     if(!str)
7190         str = "<unknown>";
7191     cNode = AddChannel(key, now, NULL, NULL);
7192     if(!cNode)
7193     {
7194         log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7195         return 0;
7196     }
7197     cData = register_channel(cNode, str);
7198     if(!cData)
7199     {
7200         log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7201         return 0;
7202     }
7203
7204     if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7205     {
7206         enum levelOption lvlOpt;
7207         enum charOption chOpt;
7208
7209         if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7210             cData->flags = atoi(str);
7211
7212         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7213         {
7214             str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7215             if(str)
7216                 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7217             else if(levelOptions[lvlOpt].old_flag)
7218             {
7219                 if(cData->flags & levelOptions[lvlOpt].old_flag)
7220                     cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7221                 else
7222                     cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7223             }
7224         }
7225
7226         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7227         {
7228             if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7229                 continue;
7230             cData->chOpts[chOpt] = str[0];
7231         }
7232     }
7233     else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7234     {
7235         enum levelOption lvlOpt;
7236         enum charOption chOpt;
7237         unsigned int count;
7238
7239         cData->flags = base64toint(str, 5);
7240         count = strlen(str += 5);
7241         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7242         {
7243             unsigned short lvl;
7244             if(levelOptions[lvlOpt].old_flag)
7245             {
7246                 if(cData->flags & levelOptions[lvlOpt].old_flag)
7247                     lvl = levelOptions[lvlOpt].flag_value;
7248                 else
7249                     lvl = levelOptions[lvlOpt].default_value;
7250             }
7251             else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7252             {
7253             case 'c': lvl = UL_COOWNER; break;
7254             case 'm': lvl = UL_MASTER; break;
7255             case 'n': lvl = UL_OWNER+1; break;
7256             case 'o': lvl = UL_OP; break;
7257             case 'p': lvl = UL_PEON; break;
7258             case 'w': lvl = UL_OWNER; break;
7259             default: lvl = 0; break;
7260             }
7261             cData->lvlOpts[lvlOpt] = lvl;
7262         }
7263         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7264             cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7265     }
7266
7267     if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7268     {
7269         suspended = chanserv_read_suspended(obj);
7270         cData->suspended = suspended;
7271         suspended->cData = cData;
7272         /* We could use suspended->expires and suspended->revoked to
7273          * set the CHANNEL_SUSPENDED flag, but we don't. */
7274     }
7275     else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7276     {
7277         suspended = calloc(1, sizeof(*suspended));
7278         suspended->issued = 0;
7279         suspended->revoked = 0;
7280         suspended->suspender = strdup(str);
7281         str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7282         suspended->expires = str ? atoi(str) : 0;
7283         str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7284         suspended->reason = strdup(str ? str : "No reason");
7285         suspended->previous = NULL;
7286         cData->suspended = suspended;
7287         suspended->cData = cData;
7288     }
7289     else
7290     {
7291         cData->flags &= ~CHANNEL_SUSPENDED;
7292         suspended = NULL; /* to squelch a warning */
7293     }
7294
7295     if(IsSuspended(cData)) {
7296         if(suspended->expires > now)
7297             timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7298         else if(suspended->expires)
7299             cData->flags &= ~CHANNEL_SUSPENDED;
7300     }
7301
7302     if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7303         struct mod_chanmode change;
7304         mod_chanmode_init(&change);
7305         change.argc = 1;
7306         change.args[0].mode = MODE_CHANOP;
7307         change.args[0].u.member = AddChannelUser(chanserv, cNode);
7308         mod_chanmode_announce(chanserv, cNode, &change);
7309     }
7310
7311     str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7312     cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
7313     str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7314     cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
7315     str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7316     cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
7317     str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7318     cData->max = str ? atoi(str) : 0;
7319     str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7320     cData->greeting = str ? strdup(str) : NULL;
7321     str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7322     cData->user_greeting = str ? strdup(str) : NULL;
7323     str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7324     cData->topic_mask = str ? strdup(str) : NULL;
7325     str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7326     cData->topic = str ? strdup(str) : NULL;
7327
7328     if(!IsSuspended(cData)
7329        && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7330        && (argc = split_line(str, 0, ArrayLength(argv), argv))
7331        && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
7332         cData->modes = *modes;
7333         if(off_channel > 0)
7334           cData->modes.modes_set |= MODE_REGISTERED;
7335         if(cData->modes.argc > 1)
7336             cData->modes.argc = 1;
7337         mod_chanmode_announce(chanserv, cNode, &cData->modes);
7338         mod_chanmode_free(modes);
7339     }
7340
7341     obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7342     for(it = dict_first(obj); it; it = iter_next(it))
7343         user_read_helper(iter_key(it), iter_data(it), cData);
7344
7345     if(!cData->users && !IsProtected(cData))
7346     {
7347         log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7348         unregister_channel(cData, "has empty user list.");
7349         return 0;
7350     }
7351
7352     obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7353     for(it = dict_first(obj); it; it = iter_next(it))
7354         ban_read_helper(iter_key(it), iter_data(it), cData);
7355
7356     obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7357     for(it = dict_first(obj); it; it = iter_next(it))
7358     {
7359         struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7360         struct record_data *rd = iter_data(it);
7361         const char *note, *setter;
7362
7363         if(rd->type != RECDB_OBJECT)
7364         {
7365             log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7366         }
7367         else if(!ntype)
7368         {
7369             log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7370         }
7371         else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7372         {
7373             log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7374         }
7375         else
7376         {
7377             setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7378             if(!setter) setter = "<unknown>";
7379             chanserv_add_channel_note(cData, ntype, setter, note);
7380         }
7381     }
7382
7383     return 0;
7384 }
7385
7386 static void
7387 chanserv_dnr_read(const char *key, struct record_data *hir)
7388 {
7389     const char *setter, *reason, *str;
7390     struct do_not_register *dnr;
7391     time_t expiry;
7392
7393     setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7394     if(!setter)
7395     {
7396         log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7397         return;
7398     }
7399     reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7400     if(!reason)
7401     {
7402         log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7403         return;
7404     }
7405     str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7406     expiry = str ? (time_t)strtoul(str, NULL, 0) : 0;
7407     if(expiry && expiry <= now)
7408         return;
7409     dnr = chanserv_add_dnr(key, setter, expiry, reason);
7410     if(!dnr)
7411         return;
7412     str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7413     if(str)
7414         dnr->set = atoi(str);
7415     else
7416         dnr->set = 0;
7417 }
7418
7419 static int
7420 chanserv_saxdb_read(struct dict *database)
7421 {
7422     struct dict *section;
7423     dict_iterator_t it;
7424
7425     if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7426         for(it = dict_first(section); it; it = iter_next(it))
7427             chanserv_note_type_read(iter_key(it), iter_data(it));
7428
7429     if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7430         for(it = dict_first(section); it; it = iter_next(it))
7431             chanserv_channel_read(iter_key(it), iter_data(it));
7432
7433     if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7434         for(it = dict_first(section); it; it = iter_next(it))
7435             chanserv_dnr_read(iter_key(it), iter_data(it));
7436
7437     return 0;
7438 }
7439
7440 static int
7441 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7442 {
7443     int high_present = 0;
7444     saxdb_start_record(ctx, KEY_USERS, 1);
7445     for(; uData; uData = uData->next)
7446     {
7447         if((uData->access >= UL_PRESENT) && uData->present)
7448             high_present = 1;
7449         saxdb_start_record(ctx, uData->handle->handle, 0);
7450         saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7451         saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7452         if(uData->flags)
7453             saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7454         if(uData->info)
7455             saxdb_write_string(ctx, KEY_INFO, uData->info);
7456         saxdb_end_record(ctx);
7457     }
7458     saxdb_end_record(ctx);
7459     return high_present;
7460 }
7461
7462 static void
7463 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7464 {
7465     if(!bData)
7466         return;
7467     saxdb_start_record(ctx, KEY_BANS, 1);
7468     for(; bData; bData = bData->next)
7469     {
7470         saxdb_start_record(ctx, bData->mask, 0);
7471         saxdb_write_int(ctx, KEY_SET, bData->set);
7472         if(bData->triggered)
7473             saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7474         if(bData->expires)
7475             saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7476         if(bData->owner[0])
7477             saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7478         if(bData->reason)
7479             saxdb_write_string(ctx, KEY_REASON, bData->reason);
7480         saxdb_end_record(ctx);
7481     }
7482     saxdb_end_record(ctx);
7483 }
7484
7485 static void
7486 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7487 {
7488     saxdb_start_record(ctx, name, 0);
7489     saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7490     saxdb_write_string(ctx, KEY_REASON, susp->reason);
7491     if(susp->issued)
7492         saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7493     if(susp->expires)
7494         saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7495     if(susp->revoked)
7496         saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7497     if(susp->previous)
7498         chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7499     saxdb_end_record(ctx);
7500 }
7501
7502 static void
7503 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7504 {
7505     char buf[MAXLEN];
7506     int high_present;
7507     enum levelOption lvlOpt;
7508     enum charOption chOpt;
7509
7510     saxdb_start_record(ctx, channel->channel->name, 1);
7511
7512     saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7513     saxdb_write_int(ctx, KEY_MAX, channel->max);
7514     if(channel->topic)
7515         saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7516     if(channel->registrar)
7517         saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7518     if(channel->greeting)
7519         saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7520     if(channel->user_greeting)
7521         saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7522     if(channel->topic_mask)
7523         saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7524     if(channel->suspended)
7525         chanserv_write_suspended(ctx, "suspended", channel->suspended);
7526
7527     saxdb_start_record(ctx, KEY_OPTIONS, 0);
7528     saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7529     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7530         saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7531     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7532     {
7533         buf[0] = channel->chOpts[chOpt];
7534         buf[1] = '\0';
7535         saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7536     }
7537     saxdb_end_record(ctx);
7538
7539     if(channel->modes.modes_set || channel->modes.modes_clear)
7540     {
7541         mod_chanmode_format(&channel->modes, buf);
7542         saxdb_write_string(ctx, KEY_MODES, buf);
7543     }
7544
7545     high_present = chanserv_write_users(ctx, channel->users);
7546     chanserv_write_bans(ctx, channel->bans);
7547
7548     if(dict_size(channel->notes))
7549     {
7550         dict_iterator_t it;
7551
7552         saxdb_start_record(ctx, KEY_NOTES, 1);
7553         for(it = dict_first(channel->notes); it; it = iter_next(it))
7554         {
7555             struct note *note = iter_data(it);
7556             saxdb_start_record(ctx, iter_key(it), 0);
7557             saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7558             saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7559             saxdb_end_record(ctx);
7560         }
7561         saxdb_end_record(ctx);
7562     }
7563
7564     if(channel->ownerTransfer)
7565         saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7566     saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7567     saxdb_end_record(ctx);
7568 }
7569
7570 static void
7571 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7572 {
7573     const char *str;
7574
7575     saxdb_start_record(ctx, ntype->name, 0);
7576     switch(ntype->set_access_type)
7577     {
7578     case NOTE_SET_CHANNEL_ACCESS:
7579         saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7580         break;
7581     case NOTE_SET_CHANNEL_SETTER:
7582         saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7583         break;
7584     case NOTE_SET_PRIVILEGED: default:
7585         saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7586         break;
7587     }
7588     switch(ntype->visible_type)
7589     {
7590     case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7591     case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7592     case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7593     }
7594     saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7595     saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7596     saxdb_end_record(ctx);
7597 }
7598
7599 static void
7600 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7601 {
7602     struct do_not_register *dnr;
7603     dict_iterator_t it, next;
7604
7605     for(it = dict_first(dnrs); it; it = next)
7606     {
7607         next = iter_next(it);
7608         dnr = iter_data(it);
7609         if(dnr->expires && dnr->expires <= now)
7610         {
7611             dict_remove(dnrs, iter_key(it));
7612             continue;
7613         }
7614         saxdb_start_record(ctx, dnr->chan_name, 0);
7615         if(dnr->set)
7616             saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7617         if(dnr->expires)
7618             saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7619         saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7620         saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7621         saxdb_end_record(ctx);
7622     }
7623 }
7624
7625 static int
7626 chanserv_saxdb_write(struct saxdb_context *ctx)
7627 {
7628     dict_iterator_t it;
7629     struct chanData *channel;
7630
7631     /* Notes */
7632     saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7633     for(it = dict_first(note_types); it; it = iter_next(it))
7634         chanserv_write_note_type(ctx, iter_data(it));
7635     saxdb_end_record(ctx);
7636
7637     /* DNRs */
7638     saxdb_start_record(ctx, KEY_DNR, 1);
7639     write_dnrs_helper(ctx, handle_dnrs);
7640     write_dnrs_helper(ctx, plain_dnrs);
7641     write_dnrs_helper(ctx, mask_dnrs);
7642     saxdb_end_record(ctx);
7643
7644     /* Channels */
7645     saxdb_start_record(ctx, KEY_CHANNELS, 1);
7646     for(channel = channelList; channel; channel = channel->next)
7647         chanserv_write_channel(ctx, channel);
7648     saxdb_end_record(ctx);
7649
7650     return 0;
7651 }
7652
7653 static void
7654 chanserv_db_cleanup(void) {
7655     unsigned int ii;
7656     unreg_part_func(handle_part);
7657     while(channelList)
7658         unregister_channel(channelList, "terminating.");
7659     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7660         UnlockChannel(chanserv_conf.support_channels.list[ii]);
7661     free(chanserv_conf.support_channels.list);
7662     dict_delete(handle_dnrs);
7663     dict_delete(plain_dnrs);
7664     dict_delete(mask_dnrs);
7665     dict_delete(note_types);
7666     free_string_list(chanserv_conf.eightball);
7667     free_string_list(chanserv_conf.old_ban_names);
7668     free_string_list(chanserv_conf.set_shows);
7669     free(set_shows_list.list);
7670     free(uset_shows_list.list);
7671     while(helperList)
7672     {
7673         struct userData *helper = helperList;
7674         helperList = helperList->next;
7675         free(helper);
7676     }
7677 }
7678
7679 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7680 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7681 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7682
7683 void
7684 init_chanserv(const char *nick)
7685 {
7686     CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7687     conf_register_reload(chanserv_conf_read);
7688
7689     if(nick)
7690     {
7691         reg_server_link_func(handle_server_link);
7692         reg_new_channel_func(handle_new_channel);
7693         reg_join_func(handle_join);
7694         reg_part_func(handle_part);
7695         reg_kick_func(handle_kick);
7696         reg_topic_func(handle_topic);
7697         reg_mode_change_func(handle_mode);
7698         reg_nick_change_func(handle_nick_change);
7699         reg_auth_func(handle_auth);
7700     }
7701
7702     reg_handle_rename_func(handle_rename);
7703     reg_unreg_func(handle_unreg);
7704
7705     handle_dnrs = dict_new();
7706     dict_set_free_data(handle_dnrs, free);
7707     plain_dnrs = dict_new();
7708     dict_set_free_data(plain_dnrs, free);
7709     mask_dnrs = dict_new();
7710     dict_set_free_data(mask_dnrs, free);
7711
7712     reg_svccmd_unbind_func(handle_svccmd_unbind);
7713     chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7714     DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7715     DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7716     DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7717     DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
7718     modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
7719     modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
7720     modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
7721     DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7722     DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7723     DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7724     DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7725     DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7726
7727     DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7728     DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7729
7730     DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7731     DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7732     DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7733     DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7734     DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7735
7736     DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7737     DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7738     DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7739     DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7740     DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7741
7742     DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7743     DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7744     DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7745     DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7746
7747     DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7748     DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7749     DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7750     DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7751     DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7752     DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7753     DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7754     DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7755
7756     DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7757     DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7758     DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7759     DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7760     DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7761     DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7762     DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7763     DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7764     DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7765     DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7766     DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7767     DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7768     DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7769     DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7770
7771     DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7772     DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7773     DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7774     DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7775     DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7776
7777     DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7778     DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7779
7780     DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7781     DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7782     DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7783     DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7784     DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7785     DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7786     DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7787     DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7788     DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7789     DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7790     DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7791
7792     DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7793     DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7794
7795     DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7796     DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7797     DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7798     DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7799
7800     DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7801     DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7802     DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7803     DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7804     DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7805
7806     DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7807     DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7808     DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7809     DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7810     DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7811     DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7812
7813     /* Channel options */
7814     DEFINE_CHANNEL_OPTION(defaulttopic);
7815     DEFINE_CHANNEL_OPTION(topicmask);
7816     DEFINE_CHANNEL_OPTION(greeting);
7817     DEFINE_CHANNEL_OPTION(usergreeting);
7818     DEFINE_CHANNEL_OPTION(modes);
7819     DEFINE_CHANNEL_OPTION(enfops);
7820     DEFINE_CHANNEL_OPTION(giveops);
7821     DEFINE_CHANNEL_OPTION(protect);
7822     DEFINE_CHANNEL_OPTION(enfmodes);
7823     DEFINE_CHANNEL_OPTION(enftopic);
7824     DEFINE_CHANNEL_OPTION(pubcmd);
7825     DEFINE_CHANNEL_OPTION(givevoice);
7826     DEFINE_CHANNEL_OPTION(userinfo);
7827     DEFINE_CHANNEL_OPTION(dynlimit);
7828     DEFINE_CHANNEL_OPTION(topicsnarf);
7829     DEFINE_CHANNEL_OPTION(nodelete);
7830     DEFINE_CHANNEL_OPTION(toys);
7831     DEFINE_CHANNEL_OPTION(setters);
7832     DEFINE_CHANNEL_OPTION(topicrefresh);
7833     DEFINE_CHANNEL_OPTION(ctcpusers);
7834     DEFINE_CHANNEL_OPTION(ctcpreaction);
7835     DEFINE_CHANNEL_OPTION(inviteme);
7836     DEFINE_CHANNEL_OPTION(unreviewed);
7837     modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
7838     modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
7839     if(off_channel > 1)
7840         DEFINE_CHANNEL_OPTION(offchannel);
7841     modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7842
7843     /* Alias set topic to set defaulttopic for compatibility. */
7844     modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7845
7846     /* User options */
7847     DEFINE_USER_OPTION(noautoop);
7848     DEFINE_USER_OPTION(autoinvite);
7849     DEFINE_USER_OPTION(info);
7850
7851     /* Alias uset autovoice to uset autoop. */
7852     modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7853
7854     note_types = dict_new();
7855     dict_set_free_data(note_types, chanserv_deref_note_type);
7856     if(nick)
7857     {
7858         const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7859         chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7860         service_register(chanserv)->trigger = '!';
7861         reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7862     }
7863     saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7864
7865     if(chanserv_conf.channel_expire_frequency)
7866         timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7867
7868     if(chanserv_conf.dnr_expire_frequency)
7869         timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
7870
7871     if(chanserv_conf.refresh_period)
7872     {
7873         time_t next_refresh;
7874         next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7875         timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7876     }
7877
7878     reg_exit_func(chanserv_db_cleanup);
7879     message_register_table(msgtab);
7880 }