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