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