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