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