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