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