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