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