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