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