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