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