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