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