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