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