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