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