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