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