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