Fix clearing "helping" flag from users when parting other channels.
[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             struct chanNode *channel;
6720             struct userNode *exclude;
6721             /* When looking at the channel that is being /part'ed, we
6722              * have to skip over the client that is leaving.  For
6723              * other channels, we must not do that.
6724              */
6725             channel = chanserv_conf.support_channels.list[ii];
6726             exclude = (channel == mn->channel) ? mn->user : NULL;
6727             if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
6728                 break;
6729         }
6730         if(ii == chanserv_conf.support_channels.used)
6731             HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6732     }
6733 }
6734
6735 static void
6736 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6737 {
6738     struct userData *uData;
6739
6740     if(!channel->channel_info || !kicker || IsService(kicker)
6741        || (kicker == victim) || IsSuspended(channel->channel_info)
6742        || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6743         return;
6744
6745     if(protect_user(victim, kicker, channel->channel_info))
6746     {
6747         const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6748         KickChannelUser(kicker, channel, chanserv, reason);
6749     }
6750
6751     if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6752         uData->seen = now;
6753 }
6754
6755 static int
6756 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6757 {
6758     struct chanData *cData;
6759
6760     if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6761         return 0;
6762
6763     cData = channel->channel_info;
6764     if(bad_topic(channel, user, channel->topic))
6765     {
6766         send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6767         if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6768             SetChannelTopic(channel, chanserv, old_topic, 1);
6769         else if(cData->topic)
6770             SetChannelTopic(channel, chanserv, cData->topic, 1);
6771         return 1;
6772     }
6773     /* With topicsnarf, grab the topic and save it as the default topic. */
6774     if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6775     {
6776         free(cData->topic);
6777         cData->topic = strdup(channel->topic);
6778     }
6779     return 0;
6780 }
6781
6782 static void
6783 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6784 {
6785     struct mod_chanmode *bounce = NULL;
6786     unsigned int bnc, ii;
6787     char deopped = 0;
6788
6789     if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6790         return;
6791
6792     if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6793        && mode_lock_violated(&channel->channel_info->modes, change))
6794     {
6795         char correct[MAXLEN];
6796         bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6797         mod_chanmode_format(&channel->channel_info->modes, correct);
6798         send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6799     }
6800     for(ii = bnc = 0; ii < change->argc; ++ii)
6801     {
6802         if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6803         {
6804             const struct userNode *victim = change->args[ii].u.member->user;
6805             if(!protect_user(victim, user, channel->channel_info))
6806                 continue;
6807             if(!bounce)
6808                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6809             if(!deopped)
6810             {
6811                 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6812                 bounce->args[bnc].u.member = GetUserMode(channel, user);
6813                 if(bounce->args[bnc].u.member)
6814                     bnc++;
6815                 deopped = 1;
6816             }
6817             bounce->args[bnc].mode = MODE_CHANOP;
6818             bounce->args[bnc].u.member = change->args[ii].u.member;
6819             bnc++;
6820             send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6821         }
6822         else if(change->args[ii].mode & MODE_CHANOP)
6823         {
6824             const struct userNode *victim = change->args[ii].u.member->user;
6825             if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6826                 continue;
6827             if(!bounce)
6828                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6829             bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6830             bounce->args[bnc].u.member = change->args[ii].u.member;
6831             bnc++;
6832         }
6833         else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6834         {
6835             const char *ban = change->args[ii].u.hostmask;
6836             if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6837                 continue;
6838             if(!bounce)
6839                 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6840             bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6841             bounce->args[bnc].u.hostmask = strdup(ban);
6842             bnc++;
6843             send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6844         }
6845     }
6846     if(bounce)
6847     {
6848         if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6849             mod_chanmode_announce(chanserv, channel, bounce);
6850         for(ii = 0; ii < change->argc; ++ii)
6851             if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6852                 free((char*)bounce->args[ii].u.hostmask);
6853         mod_chanmode_free(bounce);
6854     }
6855 }
6856
6857 static void
6858 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6859 {
6860     struct chanNode *channel;
6861     struct banData *bData;
6862     struct mod_chanmode change;
6863     unsigned int ii, jj;
6864     char kick_reason[MAXLEN];
6865
6866     mod_chanmode_init(&change);
6867     change.argc = 1;
6868     change.args[0].mode = MODE_BAN;
6869     for(ii = 0; ii < user->channels.used; ++ii)
6870     {
6871         channel = user->channels.list[ii]->channel;
6872         /* Need not check for bans if they're opped or voiced. */
6873         if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6874             continue;
6875         /* Need not check for bans unless channel registration is active. */
6876         if(!channel->channel_info || IsSuspended(channel->channel_info))
6877             continue;
6878         /* Look for a matching ban already on the channel. */
6879         for(jj = 0; jj < channel->banlist.used; ++jj)
6880             if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6881                 break;
6882         /* Need not act if we found one. */
6883         if(jj < channel->banlist.used)
6884             continue;
6885         /* Look for a matching ban in this channel. */
6886         for(bData = channel->channel_info->bans; bData; bData = bData->next)
6887         {
6888             if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6889                 continue;
6890             change.args[0].u.hostmask = bData->mask;
6891             mod_chanmode_announce(chanserv, channel, &change);
6892             sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6893             KickChannelUser(user, channel, chanserv, kick_reason);
6894             bData->triggered = now;
6895             break; /* we don't need to check any more bans in the channel */
6896         }
6897     }
6898 }
6899
6900 static void handle_rename(struct handle_info *handle, const char *old_handle)
6901 {
6902     struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6903
6904     if(dnr)
6905     {
6906         dict_remove2(handle_dnrs, old_handle, 1);
6907         safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6908         dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6909     }
6910 }
6911
6912 static void
6913 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6914 {
6915     struct userNode *h_user;
6916
6917     if(handle->channels)
6918     {
6919         for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6920             send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6921
6922         while(handle->channels)
6923             del_channel_user(handle->channels, 1);
6924     }
6925 }
6926
6927 static void
6928 handle_server_link(UNUSED_ARG(struct server *server))
6929 {
6930     struct chanData *cData;
6931
6932     for(cData = channelList; cData; cData = cData->next)
6933     {
6934         if(!IsSuspended(cData))
6935             cData->may_opchan = 1;
6936         if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6937            && !cData->channel->join_flooded
6938            && ((cData->channel->limit - cData->channel->members.used)
6939                < chanserv_conf.adjust_threshold))
6940         {
6941             timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6942             timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6943         }
6944     }
6945 }
6946
6947 static void
6948 chanserv_conf_read(void)
6949 {
6950     dict_t conf_node;
6951     const char *str;
6952     char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6953     struct mod_chanmode *change;
6954     struct string_list *strlist;
6955     struct chanNode *chan;
6956     unsigned int ii;
6957
6958     if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6959     {
6960         log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6961         return;
6962     }
6963     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6964         UnlockChannel(chanserv_conf.support_channels.list[ii]);
6965     chanserv_conf.support_channels.used = 0;
6966     if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6967     {
6968         for(ii = 0; ii < strlist->used; ++ii)
6969         {
6970             const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6971             if(!str2)
6972                 str2 = "+nt";
6973             chan = AddChannel(strlist->list[ii], now, str2, NULL);
6974             LockChannel(chan);
6975             channelList_append(&chanserv_conf.support_channels, chan);
6976         }
6977     }
6978     else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6979     {
6980         const char *str2;
6981         str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6982         if(!str2)
6983             str2 = "+nt";
6984         chan = AddChannel(str, now, str2, NULL);
6985         LockChannel(chan);
6986         channelList_append(&chanserv_conf.support_channels, chan);
6987     }
6988     str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6989     chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6990     str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6991     chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6992     str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6993     chanserv_conf.greeting_length = str ? atoi(str) : 200;
6994     str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6995     chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6996     str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6997     chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6998     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6999     chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7000     str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7001     chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7002     str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7003     chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7004     str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7005     chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7006     str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7007     chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7008     str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7009     chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7010     str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7011     chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7012     str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7013     if(chanserv && str)
7014         NickChange(chanserv, str, 0);
7015     str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7016     chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7017     str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7018     chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7019     str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7020     chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7021     str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7022     chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7023     str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7024     chanserv_conf.max_owned = str ? atoi(str) : 5;
7025     str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7026     chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7027     str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7028     chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7029     str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7030     chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7031     str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7032     if(!str)
7033         str = "+nt";
7034     safestrncpy(mode_line, str, sizeof(mode_line));
7035     ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7036     if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
7037        && (change->argc < 2))
7038     {
7039         chanserv_conf.default_modes = *change;
7040         mod_chanmode_free(change);
7041     }
7042     free_string_list(chanserv_conf.set_shows);
7043     strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7044     if(strlist)
7045         strlist = string_list_copy(strlist);
7046     else
7047     {
7048         static const char *list[] = {
7049             /* free form text */
7050             "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7051             /* options based on user level */
7052             "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7053             "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7054             /* multiple choice options */
7055             "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7056             /* binary options */
7057             "DynLimit", "NoDelete",
7058             /* delimiter */
7059             NULL
7060         };
7061         strlist = alloc_string_list(ArrayLength(list)-1);
7062         for(ii=0; list[ii]; ii++)
7063             string_list_append(strlist, strdup(list[ii]));
7064     }
7065     chanserv_conf.set_shows = strlist;
7066     /* We don't look things up now, in case the list refers to options
7067      * defined by modules initialized after this point.  Just mark the
7068      * function list as invalid, so it will be initialized.
7069      */
7070     set_shows_list.used = 0;
7071     free_string_list(chanserv_conf.eightball);
7072     strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7073     if(strlist)
7074     {
7075         strlist = string_list_copy(strlist);
7076     }
7077     else
7078     {
7079         strlist = alloc_string_list(4);
7080         string_list_append(strlist, strdup("Yes."));
7081         string_list_append(strlist, strdup("No."));
7082         string_list_append(strlist, strdup("Maybe so."));
7083     }
7084     chanserv_conf.eightball = strlist;
7085     free_string_list(chanserv_conf.old_ban_names);
7086     strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7087     if(strlist)
7088         strlist = string_list_copy(strlist);
7089     else
7090         strlist = alloc_string_list(2);
7091     chanserv_conf.old_ban_names = strlist;
7092     str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7093     off_channel = str ? atoi(str) : 0;
7094 }
7095
7096 static void
7097 chanserv_note_type_read(const char *key, struct record_data *rd)
7098 {
7099     dict_t obj;
7100     struct note_type *ntype;
7101     const char *str;
7102
7103     if(!(obj = GET_RECORD_OBJECT(rd)))
7104     {
7105         log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7106         return;
7107     }
7108     if(!(ntype = chanserv_create_note_type(key)))
7109     {
7110         log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7111         return;
7112     }
7113
7114     /* Figure out set access */
7115     if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7116     {
7117         ntype->set_access_type = NOTE_SET_PRIVILEGED;
7118         ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7119     }
7120     else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7121     {
7122         ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7123         ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7124     }
7125     else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7126     {
7127         ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7128     }
7129     else
7130     {
7131         log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7132         ntype->set_access_type = NOTE_SET_PRIVILEGED;
7133         ntype->set_access.min_opserv = 0;
7134     }
7135
7136     /* Figure out visibility */
7137     if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7138         ntype->visible_type = NOTE_VIS_PRIVILEGED;
7139     else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7140         ntype->visible_type = NOTE_VIS_PRIVILEGED;
7141     else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7142         ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7143     else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7144         ntype->visible_type = NOTE_VIS_ALL;
7145     else
7146         ntype->visible_type = NOTE_VIS_PRIVILEGED;
7147
7148     str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7149     ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7150 }
7151
7152 static void
7153 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7154 {
7155     struct handle_info *handle;
7156     struct userData *uData;
7157     char *seen, *inf, *flags;
7158     unsigned long last_seen;
7159     unsigned short access_level;
7160
7161     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7162     {
7163         log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7164         return;
7165     }
7166
7167     access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7168     if(access_level > UL_OWNER)
7169     {
7170         log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7171         return;
7172     }
7173
7174     inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7175     seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7176     last_seen = seen ? strtoul(seen, NULL, 0) : now;
7177     flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7178     handle = get_handle_info(key);
7179     if(!handle)
7180     {
7181         log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7182         return;
7183     }
7184
7185     uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7186     uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7187 }
7188
7189 static void
7190 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7191 {
7192     struct banData *bData;
7193     char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7194     unsigned long set_time, triggered_time, expires_time;
7195
7196     if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7197     {
7198         log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7199         return;
7200     }
7201
7202     set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7203     triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7204     s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7205     s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7206     owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7207     reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7208     if (!reason || !owner)
7209         return;
7210
7211     set_time = set ? strtoul(set, NULL, 0) : now;
7212     triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7213     if(s_expires)
7214         expires_time = strtoul(s_expires, NULL, 0);
7215     else if(s_duration)
7216         expires_time = set_time + atoi(s_duration);
7217     else
7218         expires_time = 0;
7219
7220     if(!reason || (expires_time && (expires_time < now)))
7221         return;
7222
7223     bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7224 }
7225
7226 static struct suspended *
7227 chanserv_read_suspended(dict_t obj)
7228 {
7229     struct suspended *suspended = calloc(1, sizeof(*suspended));
7230     char *str;
7231     dict_t previous;
7232
7233     str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7234     suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7235     str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7236     suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7237     str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7238     suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7239     suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7240     suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7241     previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7242     suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7243     return suspended;
7244 }
7245
7246 static int
7247 chanserv_channel_read(const char *key, struct record_data *hir)
7248 {
7249     struct suspended *suspended;
7250     struct mod_chanmode *modes;
7251     struct chanNode *cNode;
7252     struct chanData *cData;
7253     struct dict *channel, *obj;
7254     char *str, *argv[10];
7255     dict_iterator_t it;
7256     unsigned int argc;
7257
7258     channel = hir->d.object;
7259
7260     str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7261     if(!str)
7262         str = "<unknown>";
7263     cNode = AddChannel(key, now, NULL, NULL);
7264     if(!cNode)
7265     {
7266         log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7267         return 0;
7268     }
7269     cData = register_channel(cNode, str);
7270     if(!cData)
7271     {
7272         log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7273         return 0;
7274     }
7275
7276     if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7277     {
7278         enum levelOption lvlOpt;
7279         enum charOption chOpt;
7280
7281         if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7282             cData->flags = atoi(str);
7283
7284         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7285         {
7286             str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7287             if(str)
7288                 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7289             else if(levelOptions[lvlOpt].old_flag)
7290             {
7291                 if(cData->flags & levelOptions[lvlOpt].old_flag)
7292                     cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7293                 else
7294                     cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7295             }
7296         }
7297
7298         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7299         {
7300             if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7301                 continue;
7302             cData->chOpts[chOpt] = str[0];
7303         }
7304     }
7305     else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7306     {
7307         enum levelOption lvlOpt;
7308         enum charOption chOpt;
7309         unsigned int count;
7310
7311         cData->flags = base64toint(str, 5);
7312         count = strlen(str += 5);
7313         for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7314         {
7315             unsigned short lvl;
7316             if(levelOptions[lvlOpt].old_flag)
7317             {
7318                 if(cData->flags & levelOptions[lvlOpt].old_flag)
7319                     lvl = levelOptions[lvlOpt].flag_value;
7320                 else
7321                     lvl = levelOptions[lvlOpt].default_value;
7322             }
7323             else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7324             {
7325             case 'c': lvl = UL_COOWNER; break;
7326             case 'm': lvl = UL_MASTER; break;
7327             case 'n': lvl = UL_OWNER+1; break;
7328             case 'o': lvl = UL_OP; break;
7329             case 'p': lvl = UL_PEON; break;
7330             case 'w': lvl = UL_OWNER; break;
7331             default: lvl = 0; break;
7332             }
7333             cData->lvlOpts[lvlOpt] = lvl;
7334         }
7335         for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7336             cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7337     }
7338
7339     if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7340     {
7341         suspended = chanserv_read_suspended(obj);
7342         cData->suspended = suspended;
7343         suspended->cData = cData;
7344         /* We could use suspended->expires and suspended->revoked to
7345          * set the CHANNEL_SUSPENDED flag, but we don't. */
7346     }
7347     else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7348     {
7349         suspended = calloc(1, sizeof(*suspended));
7350         suspended->issued = 0;
7351         suspended->revoked = 0;
7352         suspended->suspender = strdup(str);
7353         str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7354         suspended->expires = str ? atoi(str) : 0;
7355         str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7356         suspended->reason = strdup(str ? str : "No reason");
7357         suspended->previous = NULL;
7358         cData->suspended = suspended;
7359         suspended->cData = cData;
7360     }
7361     else
7362     {
7363         cData->flags &= ~CHANNEL_SUSPENDED;
7364         suspended = NULL; /* to squelch a warning */
7365     }
7366
7367     if(IsSuspended(cData)) {
7368         if(suspended->expires > now)
7369             timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7370         else if(suspended->expires)
7371             cData->flags &= ~CHANNEL_SUSPENDED;
7372     }
7373
7374     if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7375         struct mod_chanmode change;
7376         mod_chanmode_init(&change);
7377         change.argc = 1;
7378         change.args[0].mode = MODE_CHANOP;
7379         change.args[0].u.member = AddChannelUser(chanserv, cNode);
7380         mod_chanmode_announce(chanserv, cNode, &change);
7381     }
7382
7383     str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7384     cData->registered = str ? strtoul(str, NULL, 0) : now;
7385     str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7386     cData->visited = str ? strtoul(str, NULL, 0) : now;
7387     str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7388     cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
7389     str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7390     cData->max = str ? atoi(str) : 0;
7391     str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7392     cData->greeting = str ? strdup(str) : NULL;
7393     str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7394     cData->user_greeting = str ? strdup(str) : NULL;
7395     str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7396     cData->topic_mask = str ? strdup(str) : NULL;
7397     str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7398     cData->topic = str ? strdup(str) : NULL;
7399
7400     if(!IsSuspended(cData)
7401        && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7402        && (argc = split_line(str, 0, ArrayLength(argv), argv))
7403        && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
7404         cData->modes = *modes;
7405         if(off_channel > 0)
7406           cData->modes.modes_set |= MODE_REGISTERED;
7407         if(cData->modes.argc > 1)
7408             cData->modes.argc = 1;
7409         mod_chanmode_announce(chanserv, cNode, &cData->modes);
7410         mod_chanmode_free(modes);
7411     }
7412
7413     obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7414     for(it = dict_first(obj); it; it = iter_next(it))
7415         user_read_helper(iter_key(it), iter_data(it), cData);
7416
7417     if(!cData->users && !IsProtected(cData))
7418     {
7419         log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7420         unregister_channel(cData, "has empty user list.");
7421         return 0;
7422     }
7423
7424     obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7425     for(it = dict_first(obj); it; it = iter_next(it))
7426         ban_read_helper(iter_key(it), iter_data(it), cData);
7427
7428     obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7429     for(it = dict_first(obj); it; it = iter_next(it))
7430     {
7431         struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7432         struct record_data *rd = iter_data(it);
7433         const char *note, *setter;
7434
7435         if(rd->type != RECDB_OBJECT)
7436         {
7437             log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7438         }
7439         else if(!ntype)
7440         {
7441             log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7442         }
7443         else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7444         {
7445             log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7446         }
7447         else
7448         {
7449             setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7450             if(!setter) setter = "<unknown>";
7451             chanserv_add_channel_note(cData, ntype, setter, note);
7452         }
7453     }
7454
7455     return 0;
7456 }
7457
7458 static void
7459 chanserv_dnr_read(const char *key, struct record_data *hir)
7460 {
7461     const char *setter, *reason, *str;
7462     struct do_not_register *dnr;
7463     unsigned long expiry;
7464
7465     setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7466     if(!setter)
7467     {
7468         log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7469         return;
7470     }
7471     reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7472     if(!reason)
7473     {
7474         log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7475         return;
7476     }
7477     str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7478     expiry = str ? strtoul(str, NULL, 0) : 0;
7479     if(expiry && expiry <= now)
7480         return;
7481     dnr = chanserv_add_dnr(key, setter, expiry, reason);
7482     if(!dnr)
7483         return;
7484     str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7485     if(str)
7486         dnr->set = atoi(str);
7487     else
7488         dnr->set = 0;
7489 }
7490
7491 static int
7492 chanserv_saxdb_read(struct dict *database)
7493 {
7494     struct dict *section;
7495     dict_iterator_t it;
7496
7497     if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7498         for(it = dict_first(section); it; it = iter_next(it))
7499             chanserv_note_type_read(iter_key(it), iter_data(it));
7500
7501     if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7502         for(it = dict_first(section); it; it = iter_next(it))
7503             chanserv_channel_read(iter_key(it), iter_data(it));
7504
7505     if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7506         for(it = dict_first(section); it; it = iter_next(it))
7507             chanserv_dnr_read(iter_key(it), iter_data(it));
7508
7509     return 0;
7510 }
7511
7512 static int
7513 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7514 {
7515     int high_present = 0;
7516     saxdb_start_record(ctx, KEY_USERS, 1);
7517     for(; uData; uData = uData->next)
7518     {
7519         if((uData->access >= UL_PRESENT) && uData->present)
7520             high_present = 1;
7521         saxdb_start_record(ctx, uData->handle->handle, 0);
7522         saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7523         saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7524         if(uData->flags)
7525             saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7526         if(uData->info)
7527             saxdb_write_string(ctx, KEY_INFO, uData->info);
7528         saxdb_end_record(ctx);
7529     }
7530     saxdb_end_record(ctx);
7531     return high_present;
7532 }
7533
7534 static void
7535 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7536 {
7537     if(!bData)
7538         return;
7539     saxdb_start_record(ctx, KEY_BANS, 1);
7540     for(; bData; bData = bData->next)
7541     {
7542         saxdb_start_record(ctx, bData->mask, 0);
7543         saxdb_write_int(ctx, KEY_SET, bData->set);
7544         if(bData->triggered)
7545             saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7546         if(bData->expires)
7547             saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7548         if(bData->owner[0])
7549             saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7550         if(bData->reason)
7551             saxdb_write_string(ctx, KEY_REASON, bData->reason);
7552         saxdb_end_record(ctx);
7553     }
7554     saxdb_end_record(ctx);
7555 }
7556
7557 static void
7558 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7559 {
7560     saxdb_start_record(ctx, name, 0);
7561     saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7562     saxdb_write_string(ctx, KEY_REASON, susp->reason);
7563     if(susp->issued)
7564         saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7565     if(susp->expires)
7566         saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7567     if(susp->revoked)
7568         saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7569     if(susp->previous)
7570         chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7571     saxdb_end_record(ctx);
7572 }
7573
7574 static void
7575 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7576 {
7577     char buf[MAXLEN];
7578     int high_present;
7579     enum levelOption lvlOpt;
7580     enum charOption chOpt;
7581
7582     saxdb_start_record(ctx, channel->channel->name, 1);
7583
7584     saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7585     saxdb_write_int(ctx, KEY_MAX, channel->max);
7586     if(channel->topic)
7587         saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7588     if(channel->registrar)
7589         saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7590     if(channel->greeting)
7591         saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7592     if(channel->user_greeting)
7593         saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7594     if(channel->topic_mask)
7595         saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7596     if(channel->suspended)
7597         chanserv_write_suspended(ctx, "suspended", channel->suspended);
7598
7599     saxdb_start_record(ctx, KEY_OPTIONS, 0);
7600     saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7601     for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7602         saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7603     for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7604     {
7605         buf[0] = channel->chOpts[chOpt];
7606         buf[1] = '\0';
7607         saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7608     }
7609     saxdb_end_record(ctx);
7610
7611     if(channel->modes.modes_set || channel->modes.modes_clear)
7612     {
7613         mod_chanmode_format(&channel->modes, buf);
7614         saxdb_write_string(ctx, KEY_MODES, buf);
7615     }
7616
7617     high_present = chanserv_write_users(ctx, channel->users);
7618     chanserv_write_bans(ctx, channel->bans);
7619
7620     if(dict_size(channel->notes))
7621     {
7622         dict_iterator_t it;
7623
7624         saxdb_start_record(ctx, KEY_NOTES, 1);
7625         for(it = dict_first(channel->notes); it; it = iter_next(it))
7626         {
7627             struct note *note = iter_data(it);
7628             saxdb_start_record(ctx, iter_key(it), 0);
7629             saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7630             saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7631             saxdb_end_record(ctx);
7632         }
7633         saxdb_end_record(ctx);
7634     }
7635
7636     if(channel->ownerTransfer)
7637         saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7638     saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7639     saxdb_end_record(ctx);
7640 }
7641
7642 static void
7643 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7644 {
7645     const char *str;
7646
7647     saxdb_start_record(ctx, ntype->name, 0);
7648     switch(ntype->set_access_type)
7649     {
7650     case NOTE_SET_CHANNEL_ACCESS:
7651         saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7652         break;
7653     case NOTE_SET_CHANNEL_SETTER:
7654         saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7655         break;
7656     case NOTE_SET_PRIVILEGED: default:
7657         saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7658         break;
7659     }
7660     switch(ntype->visible_type)
7661     {
7662     case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7663     case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7664     case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7665     }
7666     saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7667     saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7668     saxdb_end_record(ctx);
7669 }
7670
7671 static void
7672 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7673 {
7674     struct do_not_register *dnr;
7675     dict_iterator_t it, next;
7676
7677     for(it = dict_first(dnrs); it; it = next)
7678     {
7679         next = iter_next(it);
7680         dnr = iter_data(it);
7681         if(dnr->expires && dnr->expires <= now)
7682         {
7683             dict_remove(dnrs, iter_key(it));
7684             continue;
7685         }
7686         saxdb_start_record(ctx, dnr->chan_name, 0);
7687         if(dnr->set)
7688             saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7689         if(dnr->expires)
7690             saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7691         saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7692         saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7693         saxdb_end_record(ctx);
7694     }
7695 }
7696
7697 static int
7698 chanserv_saxdb_write(struct saxdb_context *ctx)
7699 {
7700     dict_iterator_t it;
7701     struct chanData *channel;
7702
7703     /* Notes */
7704     saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7705     for(it = dict_first(note_types); it; it = iter_next(it))
7706         chanserv_write_note_type(ctx, iter_data(it));
7707     saxdb_end_record(ctx);
7708
7709     /* DNRs */
7710     saxdb_start_record(ctx, KEY_DNR, 1);
7711     write_dnrs_helper(ctx, handle_dnrs);
7712     write_dnrs_helper(ctx, plain_dnrs);
7713     write_dnrs_helper(ctx, mask_dnrs);
7714     saxdb_end_record(ctx);
7715
7716     /* Channels */
7717     saxdb_start_record(ctx, KEY_CHANNELS, 1);
7718     for(channel = channelList; channel; channel = channel->next)
7719         chanserv_write_channel(ctx, channel);
7720     saxdb_end_record(ctx);
7721
7722     return 0;
7723 }
7724
7725 static void
7726 chanserv_db_cleanup(void) {
7727     unsigned int ii;
7728     unreg_part_func(handle_part);
7729     while(channelList)
7730         unregister_channel(channelList, "terminating.");
7731     for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7732         UnlockChannel(chanserv_conf.support_channels.list[ii]);
7733     free(chanserv_conf.support_channels.list);
7734     dict_delete(handle_dnrs);
7735     dict_delete(plain_dnrs);
7736     dict_delete(mask_dnrs);
7737     dict_delete(note_types);
7738     free_string_list(chanserv_conf.eightball);
7739     free_string_list(chanserv_conf.old_ban_names);
7740     free_string_list(chanserv_conf.set_shows);
7741     free(set_shows_list.list);
7742     free(uset_shows_list.list);
7743     while(helperList)
7744     {
7745         struct userData *helper = helperList;
7746         helperList = helperList->next;
7747         free(helper);
7748     }
7749 }
7750
7751 #if defined(GCC_VARMACROS)
7752 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
7753 #elif defined(C99_VARMACROS)
7754 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
7755 #endif
7756 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7757 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7758
7759 void
7760 init_chanserv(const char *nick)
7761 {
7762     CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7763     conf_register_reload(chanserv_conf_read);
7764
7765     if(nick)
7766     {
7767         reg_server_link_func(handle_server_link);
7768         reg_new_channel_func(handle_new_channel);
7769         reg_join_func(handle_join);
7770         reg_part_func(handle_part);
7771         reg_kick_func(handle_kick);
7772         reg_topic_func(handle_topic);
7773         reg_mode_change_func(handle_mode);
7774         reg_nick_change_func(handle_nick_change);
7775         reg_auth_func(handle_auth);
7776     }
7777
7778     reg_handle_rename_func(handle_rename);
7779     reg_unreg_func(handle_unreg);
7780
7781     handle_dnrs = dict_new();
7782     dict_set_free_data(handle_dnrs, free);
7783     plain_dnrs = dict_new();
7784     dict_set_free_data(plain_dnrs, free);
7785     mask_dnrs = dict_new();
7786     dict_set_free_data(mask_dnrs, free);
7787
7788     reg_svccmd_unbind_func(handle_svccmd_unbind);
7789     chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7790     DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7791     DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7792     DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7793     DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
7794     modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
7795     modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
7796     modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
7797     DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7798     DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7799     DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7800     DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7801     DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7802
7803     DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7804     DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7805
7806     DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7807     DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7808     DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7809     DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7810     DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7811
7812     DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7813     DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7814     DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7815     DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7816     DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7817
7818     DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7819     DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7820     DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7821     DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7822
7823     DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7824     DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7825     DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7826     DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7827     DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7828     DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7829     DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7830     DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7831
7832     DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7833     DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7834     DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7835     DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7836     DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7837     DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7838     DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7839     DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7840     DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7841     DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7842     DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7843     DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7844     DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7845     DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7846
7847     DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7848     DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7849     DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7850     DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7851     DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7852
7853     DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7854     DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7855
7856     DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7857     DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7858     DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7859     DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7860     DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7861     DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7862     DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7863     DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7864     DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7865     DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7866     DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7867
7868     DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7869     DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7870
7871     DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7872     DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7873     DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7874     DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7875
7876     DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7877     DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7878     DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7879     DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7880     DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7881
7882     DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7883     DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7884     DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7885     DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7886     DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7887     DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7888
7889     /* Channel options */
7890     DEFINE_CHANNEL_OPTION(defaulttopic);
7891     DEFINE_CHANNEL_OPTION(topicmask);
7892     DEFINE_CHANNEL_OPTION(greeting);
7893     DEFINE_CHANNEL_OPTION(usergreeting);
7894     DEFINE_CHANNEL_OPTION(modes);
7895     DEFINE_CHANNEL_OPTION(enfops);
7896     DEFINE_CHANNEL_OPTION(giveops);
7897     DEFINE_CHANNEL_OPTION(protect);
7898     DEFINE_CHANNEL_OPTION(enfmodes);
7899     DEFINE_CHANNEL_OPTION(enftopic);
7900     DEFINE_CHANNEL_OPTION(pubcmd);
7901     DEFINE_CHANNEL_OPTION(givevoice);
7902     DEFINE_CHANNEL_OPTION(userinfo);
7903     DEFINE_CHANNEL_OPTION(dynlimit);
7904     DEFINE_CHANNEL_OPTION(topicsnarf);
7905     DEFINE_CHANNEL_OPTION(nodelete);
7906     DEFINE_CHANNEL_OPTION(toys);
7907     DEFINE_CHANNEL_OPTION(setters);
7908     DEFINE_CHANNEL_OPTION(topicrefresh);
7909     DEFINE_CHANNEL_OPTION(ctcpusers);
7910     DEFINE_CHANNEL_OPTION(ctcpreaction);
7911     DEFINE_CHANNEL_OPTION(inviteme);
7912     DEFINE_CHANNEL_OPTION(unreviewed);
7913     modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
7914     modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
7915     if(off_channel > 1)
7916         DEFINE_CHANNEL_OPTION(offchannel);
7917     modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7918
7919     /* Alias set topic to set defaulttopic for compatibility. */
7920     modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7921
7922     /* User options */
7923     DEFINE_USER_OPTION(noautoop);
7924     DEFINE_USER_OPTION(autoinvite);
7925     DEFINE_USER_OPTION(info);
7926
7927     /* Alias uset autovoice to uset autoop. */
7928     modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7929
7930     note_types = dict_new();
7931     dict_set_free_data(note_types, chanserv_deref_note_type);
7932     if(nick)
7933     {
7934         const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7935         chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7936         service_register(chanserv)->trigger = '!';
7937         reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7938     }
7939     saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7940
7941     if(chanserv_conf.channel_expire_frequency)
7942         timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7943
7944     if(chanserv_conf.dnr_expire_frequency)
7945         timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
7946
7947     if(chanserv_conf.refresh_period)
7948     {
7949         unsigned long next_refresh;
7950         next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7951         timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7952     }
7953
7954     reg_exit_func(chanserv_db_cleanup);
7955     message_register_table(msgtab);
7956 }