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