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