]> jfr.im git - irc/evilnet/x3.git/blame - src/chanserv.c
Clean up tab/whitespace/indent and brace layout
[irc/evilnet/x3.git] / src / chanserv.c
CommitLineData
d76ed9a9 1/* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
3 *
83ff05c3 4 * This file is part of x3.
d76ed9a9 5 *
d0f04f71 6 * x3 is free software; you can redistribute it and/or modify
d76ed9a9 7 * it under the terms of the GNU General Public License as published by
348683aa 8 * the Free Software Foundation; either version 3 of the License, or
d76ed9a9 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"
b404335b 24#include "gline.h"
25#include "ioset.h"
d76ed9a9 26#include "modcmd.h"
27#include "opserv.h" /* for opserv_bad_channel() */
1136f709 28#include "nickserv.h" /* for oper_outranks() */
d76ed9a9 29#include "saxdb.h"
b404335b 30#include "shun.h"
63c95a47 31#include "spamserv.h"
d76ed9a9 32#include "timeq.h"
33
c52639b4 34#define CHANSERV_CONF_NAME "services/chanserv"
d76ed9a9 35
36/* ChanServ options */
c52639b4
AS
37#define KEY_SUPPORT_CHANNEL "support_channel"
38#define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
39#define KEY_DB_BACKUP_FREQ "db_backup_freq"
40#define KEY_INFO_DELAY "info_delay"
41#define KEY_MAX_GREETLEN "max_greetlen"
42#define KEY_ADJUST_THRESHOLD "adjust_threshold"
43#define KEY_ADJUST_DELAY "adjust_delay"
44#define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
45#define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
46#define KEY_DNR_EXPIRE_FREQ "dnr_expire_freq"
47#define KEY_BAN_TIMEOUT_FREQ "ban_timeout_freq"
48#define KEY_MAX_CHAN_USERS "max_chan_users"
49#define KEY_MAX_CHAN_BANS "max_chan_bans"
50#define KEY_NICK "nick"
51#define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
52#define KEY_8BALL_RESPONSES "8ball"
53#define KEY_OLD_BAN_NAMES "old_ban_names"
54#define KEY_REFRESH_PERIOD "refresh_period"
d76ed9a9 55#define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
56#define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
57#define KEY_MAX_OWNED "max_owned"
58#define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
59#define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
60#define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
c52639b4
AS
61#define KEY_NODELETE_LEVEL "nodelete_level"
62#define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
63#define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
64#define KEY_VALID_CHANNEL_REGEX "valid_channel_regex"
d76ed9a9 65
66/* ChanServ database */
c52639b4
AS
67#define KEY_VERSION_CONTROL "version_control"
68#define KEY_CHANNELS "channels"
69#define KEY_NOTE_TYPES "note_types"
d76ed9a9 70
c8ca69a0 71/* version control paramiter */
c52639b4 72#define KEY_VERSION_NUMBER "version_number"
c8ca69a0 73
d76ed9a9 74/* Note type parameters */
c52639b4
AS
75#define KEY_NOTE_OPSERV_ACCESS "opserv_access"
76#define KEY_NOTE_CHANNEL_ACCESS "channel_access"
77#define KEY_NOTE_SETTER_ACCESS "setter_access"
78#define KEY_NOTE_VISIBILITY "visibility"
79#define KEY_NOTE_VIS_PRIVILEGED "privileged"
80#define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
81#define KEY_NOTE_VIS_ALL "all"
82#define KEY_NOTE_MAX_LENGTH "max_length"
83#define KEY_NOTE_SETTER "setter"
84#define KEY_NOTE_NOTE "note"
d76ed9a9 85
86/* Do-not-register channels */
c52639b4
AS
87#define KEY_DNR "dnr"
88#define KEY_DNR_SET "set"
89#define KEY_DNR_SETTER "setter"
90#define KEY_DNR_REASON "reason"
d76ed9a9 91
92/* Channel data */
c52639b4
AS
93#define KEY_REGISTERED "registered"
94#define KEY_REGISTRAR "registrar"
95#define KEY_SUSPENDED "suspended"
96#define KEY_PREVIOUS "previous"
97#define KEY_SUSPENDER "suspender"
98#define KEY_ISSUED "issued"
99#define KEY_REVOKED "revoked"
100#define KEY_SUSPEND_EXPIRES "suspend_expires"
101#define KEY_SUSPEND_REASON "suspend_reason"
102#define KEY_GIVEOWNERSHIP "giveownership"
103#define KEY_STAFF_ISSUER "staff_issuer"
104#define KEY_OLD_OWNER "old_owner"
105#define KEY_TARGET "target"
106#define KEY_TARGET_ACCESS "target_access"
107#define KEY_VISITED "visited"
108#define KEY_TOPIC "topic"
109#define KEY_GREETING "greeting"
110#define KEY_USER_GREETING "user_greeting"
111#define KEY_MODES "modes"
112#define KEY_FLAGS "flags"
113#define KEY_OPTIONS "options"
114#define KEY_USERS "users"
115#define KEY_BANS "bans" /* for lamers */
116#define KEY_MAX "max"
117#define KEY_NOTES "notes"
118#define KEY_TOPIC_MASK "topic_mask"
119#define KEY_OWNER_TRANSFER "owner_transfer"
120#define KEY_MAXSETINFO "maxsetinfo"
d76ed9a9 121
122/* User data */
c52639b4
AS
123#define KEY_LEVEL "level"
124#define KEY_INFO "info"
125#define KEY_SEEN "seen"
126#define KEY_ACCESSEXPIRY "accessexpiry"
127#define KEY_CLVLEXPIRY "clvlexpiry"
128#define KEY_LASTLEVEL "lastlevel"
d76ed9a9 129
130/* Ban data */
c52639b4
AS
131#define KEY_OWNER "owner"
132#define KEY_REASON "reason"
133#define KEY_SET "set"
134#define KEY_DURATION "duration"
135#define KEY_EXPIRES "expires"
136#define KEY_TRIGGERED "triggered"
d76ed9a9 137
c52639b4 138#define KEY_GOD_TIMEOUT "god_timeout"
7637f48f 139
1136f709 140#define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
141#define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
d76ed9a9 142#define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
143
144/* Administrative messages */
145static const struct message_entry msgtab[] = {
146 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
147
148/* Channel registration */
149 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
150 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
151 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
f8f30e75 152 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
d76ed9a9 153 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
f8f30e75 154 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
155 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
156 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
d76ed9a9 157
158/* Do-not-register channels */
159 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
de9510bc 160 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
1136f709 161 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
162 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
163 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
d76ed9a9 164 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
165 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
166 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
167 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
168 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
169 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
170 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
1136f709 171 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
172 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
d76ed9a9 173
174/* Channel unregistration */
175 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
176 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
177 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
fa0fac3e 178 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
d76ed9a9 179
180/* Channel moving */
181 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
182 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
183
184/* Channel merging */
185 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
186 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
187 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
188 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
189 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
190
191/* Handle unregistration */
192 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
193
194/* Error messages */
195 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
196 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
197 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
198 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
c8273589 199 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
d76ed9a9 200 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
201 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
202 { "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." },
203 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
55342ce8 204 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
d76ed9a9 205 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
55342ce8 206 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
d76ed9a9 207 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
1136f709 208 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
d76ed9a9 209 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
6ff42e24 210 { "CSMSG_NOT_IN_CHANNEL", "I am not in %s." },
d76ed9a9 211
212/* Removing yourself from a channel. */
213 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
214 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
215 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
216
217/* User management */
fd20b142 218 { "CSMSG_AUTO_DELETED", "Your %s access has expired in %s." },
dfaa28a4 219 { "CSMSG_CLVL_EXPIRED", "Your CLVL access has expired in %s." },
de9510bc 220 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
d76ed9a9 221 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
222 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
223 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
224 { "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." },
a915f7d4 225 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
226 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
b208a99e 227 { "CSMSG_ADDUSER_PENDING", "I have sent %s a message letting them know, and if they auth or register soon, I will finish adding them automatically." },
ac3bdc8d 228 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
1d957482 229 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
230 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
1bcd5f19 231 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
ac3bdc8d 232 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
1bcd5f19 233 { "CSMSG_ADDUSER_PENDING_TARGET", "Channel Services bot here: %s would like to add you to my userlist in channel %s, but you are not authenticated to $b$N$b. Please authenticate now and you will be added. If you do not have an account, type /msg $N help register" },
d76ed9a9 234 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
235
236 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
237 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
dfaa28a4 238 { "CSMSG_NO_BUMP_EXPIRY", "You cannot give users timed $bCLVL$b's when they already have timed access." },
d76ed9a9 239 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
5678501c 240 { "CSMSG_NO_OWNER", "There is no owner for %s; please use $bCLVL$b and/or $bADDOWNER$b instead." },
a32da4c7 241 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
d76ed9a9 242 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
1136f709 243 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
d76ed9a9 244 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
245
246/* Ban management */
c8273589 247 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
248 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
d76ed9a9 249 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
250 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
c8273589 251 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
252 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
9a8aa132 253 { "CSMSG_BAN_REMOVED", "Ban(s) and LAMER(s) matching $b%s$b were removed." },
c8273589 254 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
d76ed9a9 255 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
c8273589 256 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
d76ed9a9 257 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
258 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
259 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
be1ad341 260 { "CSMSG_NO_EXTBANS", "$b%s$b is an extended ban, which are not allowed." },
d76ed9a9 261 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
262 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
c8273589 263 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
d76ed9a9 264 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
3b7fa78b 265 { "CSMSG_BAD_BAN", "The given ban $b%s$b is invalid." },
d76ed9a9 266
267 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
268
269/* Channel management */
270 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
271 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
272 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
273
274 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
275 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
276 { "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" },
a915f7d4 277 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
d76ed9a9 278 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
279 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
280 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
281
282 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
283 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
284 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
285 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
286 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
287 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
288 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
289 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
290 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
d76ed9a9 291 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
292 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
7553c653 293 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
d76ed9a9 294 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
295 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
296 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
297 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
298 { "CSMSG_SET_MODES", "$bModes $b %s" },
299 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
ee25b1b2 300 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
35caf917 301 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
d76ed9a9 302 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
35caf917 303 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
d76ed9a9 304 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
35caf917 305 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
306 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
307 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
35caf917 308 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
309 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
310 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
311 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
4b6129c0 312 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
d76ed9a9 313 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
314 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
315 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
316 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
7637f48f 317 { "CSMSG_SET_RESYNC", "$bResync $b %d - %s" },
31f23f13 318 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
8b9e7d45 319 { "CSMSG_SET_MAXSETINFO", "$bMaxSetInfo $b %d - maximum characters in a setinfo line." },
4b6129c0 320
c8ca69a0 321 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
322 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
d76ed9a9 323 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
cd25f2e9 324 { "CSMSG_USET_AUTOJOIN", "$bAutoJoin $b %s" },
d76ed9a9 325 { "CSMSG_USET_INFO", "$bInfo $b %s" },
326
327 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
31f23f13 328 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
d76ed9a9 329 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
55342ce8 330 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
d76ed9a9 331 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
332 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
55342ce8 333 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
d76ed9a9 334 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
55342ce8 335 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
d76ed9a9 336 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
337 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
4b6129c0 338
339 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
2a4ca4f5 340 { "CSMSG_AUTOMODE_NORMAL", "Give voice to pals, half-op to halfops, and op to ops." },
639bdb1d 341 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
342 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
343 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
344 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
4d69a3b1 345 { "CSMSG_AUTOMODE_ONLYVOICE", "Just voice everyone with access." },
4b6129c0 346
d76ed9a9 347 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
348 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
349 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
350 { "CSMSG_PROTECT_NONE", "No users will be protected." },
351 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
352 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
353 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
31f23f13 354
d76ed9a9 355 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
356 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
357 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
358 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
359 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
31f23f13 360
7637f48f 361 { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
362 { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
363 { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
364 { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
365 { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
366
35caf917 367 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
d76ed9a9 368 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
369 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
370 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
371 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
31f23f13 372
373 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
374 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
375 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
376 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
377 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
378 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
d76ed9a9 379
380 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
381 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
382 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
5f1600ab 383 { "CSMSG_CANNOT_INVITE", "You cannot invite %s to %s." },
d76ed9a9 384 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
385 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
1136f709 386 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
d76ed9a9 387 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
388 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
389
390 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
c8273589 391 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
d76ed9a9 392 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
393
394/* Channel userlist */
338a82b5 395 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
396 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
397 /* uncomment if needed to adujust styles (and change code below)
398 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
399 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
400 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
401 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
d9896a83 402 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
403 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
338a82b5 404 */
d76ed9a9 405 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
de9510bc 406 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
c8273589 407 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
d76ed9a9 408
409/* Channel note list */
410 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
411 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
412 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
413 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
414 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
415 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
416 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
417 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
418 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
419 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
420 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
421 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
422 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
423 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
424 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
425
426/* Channel [un]suspension */
427 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
428 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
429 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
430 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
431 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
432 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
433 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
434
435/* Access information */
436 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
437 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
438 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
439 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
440 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
de9510bc 441 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
d76ed9a9 442 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
de9510bc 443 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
d76ed9a9 444 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
6b912b17 445 { "CSMSG_SMURF_TARGET", "%s %s ($b%s$b)." },
1136f709 446 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
447 { "CSMSG_UC_H_TITLE", "network helper" },
448 { "CSMSG_LC_H_TITLE", "support helper" },
d76ed9a9 449 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
450
451/* Seen information */
452 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
453 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
454 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
455 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
456
457/* Names information */
458 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
459 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
460
461/* Channel information */
de9510bc 462 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
463 { "CSMSG_BAR", "----------------------------------------"},
d76ed9a9 464 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
465 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
466 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
467 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
c8273589 468 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
469 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
d76ed9a9 470 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
471 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
472 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
473 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
474 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
475 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
476 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
477 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
478 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
479 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
480 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
481 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
482 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
483 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
82f37c08 484 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
485 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
486 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
487 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
de9510bc 488 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
cde20a3f 489 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
d76ed9a9 490
de9510bc 491 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
d76ed9a9 492 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
493 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
494 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
495 { "CSMSG_PEEK_OPS", "$bOps:$b" },
496 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
de9510bc 497 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
d76ed9a9 498
499/* Network information */
500 { "CSMSG_NETWORK_INFO", "Network Information:" },
501 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
502 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
c8273589 503 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
d76ed9a9 504 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
505 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
506 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
507 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
508 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
509
510/* Staff list */
511 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
512 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
513 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
514
515/* Channel searches */
516 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
517 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
518 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
de9510bc 519 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
d76ed9a9 520
521/* Channel configuration */
522 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
1136f709 523 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
de9510bc 524 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
525 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
d76ed9a9 526 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
527
528/* User settings */
529 { "CSMSG_USER_OPTIONS", "User Options:" },
35caf917 530// { "CSMSG_USER_PROTECTED", "That user is protected." },
d76ed9a9 531
532/* Toys */
533 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
534 { "CSMSG_PING_RESPONSE", "Pong!" },
535 { "CSMSG_WUT_RESPONSE", "wut" },
536 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
537 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
538 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
539 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
540 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
541 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
542 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
87708af4 543 { "CSMSG_ROULETTE_LOADS", "\001ACTION loads the gun and sets it on the table\001" },
a6fa0035 544 { "CSMSG_ROULETTE_NEW", "Please type %croulette to start a new round" } ,
87708af4 545 { "CSMSG_ROULETTE_BETTER_LUCK", "Better luck next time, %s" },
546 { "CSMSG_ROULETTE_BANG", "Bang!!!" } ,
547 { "CSMSG_ROULETTE_CLICK", "Click" } ,
d76ed9a9 548
b404335b 549 { "CSMSG_SPIN_WHEEL1", "\001ACTION spins the wheel of misfortune for: %s\001" } ,
550 { "CSMSG_SPIN_WHEEL2", "Round and round she goes, where she stops, nobody knows...!" } ,
551 { "CSMSG_SPIN_WHEEL3", "The wheel of misfortune has stopped on..." } ,
552
5e6460e4 553 { "CSMSG_SPIN_PEER", "Peer: Peer's gonna eat you!!!!" } ,
554 { "CSMSG_SPIN_PARTALL", "Part all: Part all channels" } ,
555 { "CSMSG_SPIN_Gline", "Gline: /gline for random amount of time" } ,
556 { "CSMSG_SPIN_SHUN", "Shun: /shun for random amount of time" } ,
557 { "CSMSG_SPIN_NOTHING", "Nothing: Absolutely nothing" } ,
558 { "CSMSG_SPIN_RANDJOIN", "Random join: Join a bunch of random channels, then /part all of 'em several times" } ,
559 { "CSMSG_SPIN_ABUSEWHOIS", "Abuse whois: Abuse line added to /whois info" } ,
560 { "CSMSG_SPIN_KICKALL", "Kick all: /kick from each channel you're in" } ,
561 { "CSMSG_SPIN_NICKCHANGE", "Nick change: Random Nick Change" } ,
562 { "CSMSG_SPIN_KILL", "Kill: /kill" } ,
563 { "CSMSG_SPIN_SVSIGNORE", "Ignore: Services ignore for random amount of time" } ,
564 { "CSMSG_SPIN_SVSIGNORE_OPER", "Ignore: I'm trying REALLY hard to ignore you, but your IRCOp smell is overwhelming!" } ,
565 { "CSMSG_SPIN_KICKBANALL", "Kickban all: /kick and ban from each channel your're in" } ,
566 { "CSMSG_SPIN_UNKNOWN", "Error: I don't know how to '%s' you, so you live for now..." },
b404335b 567
d76ed9a9 568/* Other things */
de9510bc 569 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
23475fc6 570 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
08895577 571 { "CSMSG_DEFCON_NO_NEW_CHANNELS", "You cannot register new channels at this time, please try again soon." },
572 { "CSMSG_DEFCON_NO_MODE_CHANGE", "You cannot change the MODE at this time, please try again soon." },
d76ed9a9 573 { NULL, NULL }
574};
575
576/* eject_user and unban_user flags */
c52639b4
AS
577#define ACTION_KICK 0x0001
578#define ACTION_BAN 0x0002
579#define ACTION_ADD_LAMER 0x0004
580#define ACTION_ADD_TIMED_LAMER 0x0008
581#define ACTION_UNBAN 0x0010
582#define ACTION_DEL_LAMER 0x0020
d76ed9a9 583
584/* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
c52639b4
AS
585#define MODELEN 40 + KEYLEN
586#define PADLEN 21
587#define ACCESSLEN 10
d76ed9a9 588
c52639b4 589#define CSFUNC_ARGS user, channel, argc, argv, cmd
d76ed9a9 590
591#define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
c52639b4
AS
592#define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
593#define REQUIRE_PARAMS(N) if(argc < (N)) { \
594 reply("MSG_MISSING_PARAMS", argv[0]); \
595 CHANSERV_SYNTAX(); \
596 return 0; }
d76ed9a9 597
598DECLARE_LIST(dnrList, struct do_not_register *);
1136f709 599DEFINE_LIST(dnrList, struct do_not_register *)
d76ed9a9 600
601static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
602
603struct userNode *chanserv;
604dict_t note_types;
605int off_channel;
7637f48f 606extern struct string_list *autojoin_channels;
d76ed9a9 607static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
608static struct log_type *CS_LOG;
ac3bdc8d 609struct adduserPending* adduser_pendings = NULL;
610unsigned int adduser_pendings_count = 0;
7637f48f 611unsigned long god_timeout;
d76ed9a9 612
613static struct
614{
615 struct channelList support_channels;
616 struct mod_chanmode default_modes;
617
c52639b4
AS
618 unsigned long db_backup_frequency;
619 unsigned long channel_expire_frequency;
31f23f13 620 unsigned long ban_timeout_frequency;
c52639b4 621 unsigned long dnr_expire_frequency;
d76ed9a9 622
c52639b4
AS
623 long info_delay;
624 unsigned int adjust_delay;
625 long channel_expire_delay;
d76ed9a9 626 unsigned int nodelete_level;
627
c52639b4
AS
628 unsigned int adjust_threshold;
629 int join_flood_threshold;
d76ed9a9 630
c52639b4 631 unsigned int greeting_length;
d76ed9a9 632 unsigned int refresh_period;
a32da4c7 633 unsigned int giveownership_period;
d76ed9a9 634
635 unsigned int max_owned;
c52639b4
AS
636 unsigned int max_chan_users;
637 unsigned int max_chan_bans; /* lamers */
d76ed9a9 638 unsigned int max_userinfo_length;
d3abe0df 639 unsigned int valid_channel_regex_set : 1;
640
641 regex_t valid_channel_regex;
d76ed9a9 642
643 struct string_list *set_shows;
644 struct string_list *eightball;
645 struct string_list *old_ban_names;
5e6460e4 646 struct string_list *wheel;
d76ed9a9 647
648 const char *ctcp_short_ban_duration;
649 const char *ctcp_long_ban_duration;
650
651 const char *irc_operator_epithet;
652 const char *network_helper_epithet;
653 const char *support_helper_epithet;
654} chanserv_conf;
655
656struct listData
657{
658 struct userNode *user;
659 struct userNode *bot;
660 struct chanNode *channel;
661 const char *search;
662 unsigned short lowest;
663 unsigned short highest;
664 struct userData **users;
665 struct helpfile_table table;
666};
667
668enum note_access_type
669{
670 NOTE_SET_CHANNEL_ACCESS,
671 NOTE_SET_CHANNEL_SETTER,
672 NOTE_SET_PRIVILEGED
673};
674
675enum note_visible_type
676{
677 NOTE_VIS_ALL,
678 NOTE_VIS_CHANNEL_USERS,
679 NOTE_VIS_PRIVILEGED
680};
681
b404335b 682struct io_fd *socket_io_fd;
683extern struct cManagerNode cManager;
684
d76ed9a9 685struct note_type
686{
687 enum note_access_type set_access_type;
688 union {
689 unsigned int min_opserv;
690 unsigned short min_ulevel;
691 } set_access;
692 enum note_visible_type visible_type;
693 unsigned int max_length;
694 unsigned int refs;
695 char name[1];
696};
697
698struct note
699{
700 struct note_type *type;
701 char setter[NICKSERV_HANDLE_LEN+1];
702 char note[1];
703};
704
705static unsigned int registered_channels;
706static unsigned int banCount;
707
708static const struct {
709 char *name;
710 char *title;
711 unsigned short level;
712 char ch;
a915f7d4 713} accessLevels[] = { /* MUST be orderd less to most! */
2a4ca4f5 714 { "pal", "Pal", UL_PEON, '+' },
d76ed9a9 715 { "peon", "Peon", UL_PEON, '+' },
55342ce8 716 { "halfop", "HalfOp", UL_HALFOP, '%' },
d76ed9a9 717 { "op", "Op", UL_OP, '@' },
4048352e 718 { "manager", "Manager", UL_MANAGER, '%' },
d76ed9a9 719 { "coowner", "Coowner", UL_COOWNER, '*' },
720 { "owner", "Owner", UL_OWNER, '!' },
721 { "helper", "BUG:", UL_HELPER, 'X' }
722};
723
35caf917 724/* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
d76ed9a9 725static const struct {
726 char *format_name;
727 char *db_name;
728 unsigned short default_value;
729 unsigned int old_idx;
730 unsigned int old_flag;
731 unsigned short flag_value;
732} levelOptions[] = {
e3b2f789 733 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
734 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
735 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
736 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
737 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
738 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
739 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
740 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
d76ed9a9 741 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
742};
743
744struct charOptionValues {
745 char value;
746 char *format_name;
4b6129c0 747} automodeValues[] = {
748 { 'n', "CSMSG_AUTOMODE_NONE" },
749 { 'y', "CSMSG_AUTOMODE_NORMAL" },
750 { 'v', "CSMSG_AUTOMODE_VOICE" },
751 { 'h', "CSMSG_AUTOMODE_HOP" },
752 { 'o', "CSMSG_AUTOMODE_OP" },
4d69a3b1 753 { 'm', "CSMSG_AUTOMODE_MUTE" },
754 { 'l', "CSMSG_AUTOMODE_ONLYVOICE" }
c8ca69a0 755}, protectValues[] = {
d76ed9a9 756 { 'a', "CSMSG_PROTECT_ALL" },
757 { 'e', "CSMSG_PROTECT_EQUAL" },
758 { 'l', "CSMSG_PROTECT_LOWER" },
759 { 'n', "CSMSG_PROTECT_NONE" }
760}, toysValues[] = {
761 { 'd', "CSMSG_TOYS_DISABLED" },
762 { 'n', "CSMSG_TOYS_PRIVATE" },
763 { 'p', "CSMSG_TOYS_PUBLIC" }
764}, topicRefreshValues[] = {
765 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
766 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
767 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
768 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
769 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
770}, ctcpReactionValues[] = {
35caf917 771 { 'n', "CSMSG_CTCPREACTION_NONE" },
d76ed9a9 772 { 'k', "CSMSG_CTCPREACTION_KICK" },
773 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
774 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
775 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
31f23f13 776}, banTimeoutValues[] = {
777 { '0', "CSMSG_BANTIMEOUT_NONE" },
778 { '1', "CSMSG_BANTIMEOUT_10M" },
779 { '2', "CSMSG_BANTIMEOUT_2H" },
780 { '3', "CSMSG_BANTIMEOUT_4H" },
781 { '4', "CSMSG_BANTIMEOUT_1D" },
782 { '5', "CSMSG_BANTIMEOUT_1W" }
d3347099 783},
784resyncValues[] = {
7637f48f 785 { 'n', "CSMSG_RESYNC_NEVER" },
786 { '1', "CSMSG_RESYNC_3_HOURS" },
787 { '2', "CSMSG_RESYNC_6_HOURS" },
788 { '3', "CSMSG_RESYNC_12_HOURS" },
789 { '4', "CSMSG_RESYNC_24_HOURS" }
d76ed9a9 790};
791
792static const struct {
793 char *format_name;
794 char *db_name;
795 char default_value;
796 unsigned int old_idx;
797 unsigned char count;
798 struct charOptionValues *values;
799} charOptions[] = {
4b6129c0 800 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues), automodeValues },
c8ca69a0 801 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
802 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
d76ed9a9 803 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
31f23f13 804 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues },
7637f48f 805 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues), banTimeoutValues },
806 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues), resyncValues },
d76ed9a9 807};
808
809struct userData *helperList;
810struct chanData *channelList;
811static struct module *chanserv_module;
812static unsigned int userCount;
c8ca69a0 813unsigned int chanserv_read_version = 0; /* db version control */
814
815#define CHANSERV_DB_VERSION 2
d76ed9a9 816
d76ed9a9 817#define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
818#define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
819
b404335b 820void sputsock(const char *text, ...) PRINTF_LIKE(1, 2);
821
822void
823sputsock(const char *text, ...)
824{
825 va_list arg_list;
826 char buffer[MAXLEN];
827 int pos;
828
829 if (!cManager.uplink || cManager.uplink->state == DISCONNECTED) return;
830 buffer[0] = '\0';
831 va_start(arg_list, text);
832 pos = vsnprintf(buffer, MAXLEN - 2, text, arg_list);
833 va_end(arg_list);
834 if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2;
835 buffer[pos] = 0;
836 log_replay(MAIN_LOG, true, buffer);
837 buffer[pos++] = '\n';
838 buffer[pos] = 0;
839 ioset_write(socket_io_fd, buffer, pos);
840}
841
d76ed9a9 842unsigned short
843user_level_from_name(const char *name, unsigned short clamp_level)
844{
845 unsigned int level = 0, ii;
846 if(isdigit(name[0]))
847 level = strtoul(name, NULL, 10);
848 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
849 if(!irccasecmp(name, accessLevels[ii].name))
850 level = accessLevels[ii].level;
851 if(level > clamp_level)
852 return 0;
853 return level;
854}
855
e6892204 856char *
857user_level_name_from_level(int level)
858{
859 unsigned int ii;
a915f7d4 860 char* highest;
861
862 highest = "None";
863 if(level >= 1)
2a4ca4f5 864 highest = "Pal";
e6892204 865 for(ii = 0; (ii < ArrayLength(accessLevels)); ii++)
a915f7d4 866 if(level >= accessLevels[ii].level)
867 highest = accessLevels[ii].title;
868 return(highest);
e6892204 869}
870
871
d76ed9a9 872int
873parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
874{
875 char *sep;
876 *minl = strtoul(arg, &sep, 10);
877 if(*sep == '\0')
878 {
879 *maxl = *minl;
880 return 1;
881 }
882 else if(*sep == '-')
883 {
884 *maxl = strtoul(sep+1, &sep, 10);
885 return *sep == '\0';
886 }
887 else
888 return 0;
889}
890
891struct userData*
892_GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
893{
894 struct userData *uData, **head;
895
896 if(!channel || !handle)
897 return NULL;
898
899 if(override && HANDLE_FLAGGED(handle, HELPING)
900 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
901 {
c52639b4
AS
902 for(uData = helperList;
903 uData && uData->handle != handle;
904 uData = uData->next);
d76ed9a9 905
c52639b4
AS
906 if(!uData)
907 {
908 uData = calloc(1, sizeof(struct userData));
909 uData->handle = handle;
d76ed9a9 910
c52639b4
AS
911 uData->access = UL_HELPER;
912 uData->seen = 0;
d76ed9a9 913
c52639b4 914 uData->info = NULL;
d76ed9a9 915
c52639b4
AS
916 uData->prev = NULL;
917 uData->next = helperList;
918 if(helperList)
919 helperList->prev = uData;
920 helperList = uData;
921 }
d76ed9a9 922
c52639b4 923 head = &helperList;
d76ed9a9 924 }
925 else
926 {
c52639b4 927 for(uData = channel->users; uData; uData = uData->next)
d76ed9a9 928 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
929 break;
930
c52639b4 931 head = &(channel->users);
d76ed9a9 932 }
933
934 if(uData && (uData != *head))
935 {
c52639b4
AS
936 /* Shuffle the user to the head of whatever list he was in. */
937 if(uData->next)
d76ed9a9 938 uData->next->prev = uData->prev;
c52639b4 939 if(uData->prev)
d76ed9a9 940 uData->prev->next = uData->next;
941
c52639b4
AS
942 uData->prev = NULL;
943 uData->next = *head;
d76ed9a9 944
c52639b4
AS
945 if(*head)
946 (**head).prev = uData;
947 *head = uData;
d76ed9a9 948 }
949
950 return uData;
951}
952
953/* Returns non-zero if user has at least the minimum access.
954 * exempt_owner is set when handling !set, so the owner can set things
955 * to/from >500.
956 */
957int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
958{
959 struct userData *uData;
960 struct chanData *cData = channel->channel_info;
961 unsigned short minimum = cData->lvlOpts[opt];
962 if(!minimum)
963 return 1;
964 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
965 if(!uData)
966 return 0;
967 if(minimum <= uData->access)
968 return 1;
969 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
970 return 1;
971 return 0;
972}
973
974/* Scan for other users authenticated to the same handle
975 still in the channel. If so, keep them listed as present.
976
977 user is optional, if not null, it skips checking that userNode
978 (for the handle_part function) */
979static void
980scan_user_presence(struct userData *uData, struct userNode *user)
981{
982 struct modeNode *mn;
983
984 if(IsSuspended(uData->channel)
985 || IsUserSuspended(uData)
986 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
987 {
988 uData->present = 0;
989 }
990 else
991 {
992 uData->present = 1;
993 uData->seen = now;
994 }
995}
996
997static void
1c14af0b 998chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice), UNUSED_ARG(void *extra))
d76ed9a9 999{
1000 unsigned int eflags, argc;
1001 char *argv[4];
1002 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
1003
1004 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
1005 if(!channel->channel_info
1006 || IsSuspended(channel->channel_info)
1007 || IsService(user)
1008 || !ircncasecmp(text, "ACTION ", 7))
1009 return;
35caf917 1010 /* We dont punish people we know -Rubin
1011 * * Figure out the minimum level needed to CTCP the channel *
1012 *
c52639b4
AS
1013 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
1014 * return;
35caf917 1015 */
1016 /* If they are a user of the channel, they are exempt */
1017 if(_GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
c52639b4 1018 return;
d76ed9a9 1019 /* We need to enforce against them; do so. */
1020 eflags = 0;
1136f709 1021 argv[0] = (char*)text;
d76ed9a9 1022 argv[1] = user->nick;
1023 argc = 2;
1024 if(GetUserMode(channel, user))
1025 eflags |= ACTION_KICK;
1026 switch(channel->channel_info->chOpts[chCTCPReaction]) {
35caf917 1027 default: case 'n': return;
1028 case 'k':
1029 eflags |= ACTION_KICK;
1030 break;
d76ed9a9 1031 case 'b':
1032 eflags |= ACTION_BAN;
1033 break;
1034 case 't':
c8273589 1035 eflags |= ACTION_BAN | ACTION_ADD_LAMER | ACTION_ADD_TIMED_LAMER;
d76ed9a9 1036 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
1037 break;
1038 case 'T':
c8273589 1039 eflags |= ACTION_BAN | ACTION_ADD_LAMER | ACTION_ADD_TIMED_LAMER;
d76ed9a9 1040 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
1041 break;
1042 }
1043 argv[argc++] = bad_ctcp_reason;
1044 eject_user(chanserv, channel, argc, argv, NULL, eflags);
1045}
1046
1047struct note_type *
1048chanserv_create_note_type(const char *name)
1049{
1050 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
1051 strcpy(ntype->name, name);
1052 ntype->refs = 1;
1053 dict_insert(note_types, ntype->name, ntype);
1054 return ntype;
1055}
1056
1057static void
1058chanserv_deref_note_type(void *data)
1059{
1060 struct note_type *ntype = data;
1061
1062 if(--ntype->refs > 0)
c52639b4 1063 return;
d76ed9a9 1064 free(ntype);
1065}
1066
1067static void
1068chanserv_flush_note_type(struct note_type *ntype)
1069{
1070 struct chanData *cData;
1071 for(cData = channelList; cData; cData = cData->next)
1072 dict_remove(cData->notes, ntype->name);
1073}
1074
1075static void
1076chanserv_truncate_notes(struct note_type *ntype)
1077{
1078 struct chanData *cData;
1079 struct note *note;
1080 unsigned int size = sizeof(*note) + ntype->max_length;
1081
1082 for(cData = channelList; cData; cData = cData->next) {
1083 note = dict_find(cData->notes, ntype->name, NULL);
1084 if(!note)
1085 continue;
c52639b4 1086 if(strlen(note->note) <= ntype->max_length)
d76ed9a9 1087 continue;
1088 dict_remove2(cData->notes, ntype->name, 1);
1089 note = realloc(note, size);
1090 note->note[ntype->max_length] = 0;
1091 dict_insert(cData->notes, ntype->name, note);
1092 }
1093}
1094
1095static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
1096
1097static struct note *
1098chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
1099{
1100 struct note *note;
1101 unsigned int len = strlen(text);
1102
1103 if(len > type->max_length) len = type->max_length;
1104 note = calloc(1, sizeof(*note) + len);
1105 note->type = type;
1106 strncpy(note->setter, setter, sizeof(note->setter)-1);
1107 memcpy(note->note, text, len);
1108 note->note[len] = 0;
1109 dict_insert(channel->notes, type->name, note);
1110 type->refs++;
1111 return note;
1112}
1113
1114static void
1115chanserv_free_note(void *data)
1116{
1117 struct note *note = data;
1118
1119 chanserv_deref_note_type(note->type);
1120 assert(note->type->refs > 0); /* must use delnote to remove the type */
1121 free(note);
1122}
1123
1124static MODCMD_FUNC(cmd_createnote) {
1125 struct note_type *ntype;
1126 unsigned int arg = 1, existed = 0, max_length;
1127
1128 if((ntype = dict_find(note_types, argv[1], NULL)))
1129 existed = 1;
1130 else
c52639b4 1131 ntype = chanserv_create_note_type(argv[arg]);
d76ed9a9 1132 if(!irccasecmp(argv[++arg], "privileged"))
1133 {
1134 arg++;
1135 ntype->set_access_type = NOTE_SET_PRIVILEGED;
1136 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1137 }
1138 else if(!irccasecmp(argv[arg], "channel"))
1139 {
1140 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1141 if(!ulvl)
1142 {
1143 reply("CSMSG_INVALID_ACCESS", argv[arg]);
c52639b4 1144 goto fail;
d76ed9a9 1145 }
1146 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1147 ntype->set_access.min_ulevel = ulvl;
1148 }
1149 else if(!irccasecmp(argv[arg], "setter"))
1150 {
1151 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1152 }
1153 else
1154 {
1155 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
c52639b4 1156 goto fail;
d76ed9a9 1157 }
1158
1159 if(!irccasecmp(argv[++arg], "privileged"))
1160 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1161 else if(!irccasecmp(argv[arg], "channel_users"))
1162 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1163 else if(!irccasecmp(argv[arg], "all"))
1164 ntype->visible_type = NOTE_VIS_ALL;
1165 else {
1166 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
c52639b4 1167 goto fail;
d76ed9a9 1168 }
1169
1170 if((arg+1) >= argc) {
1171 reply("MSG_MISSING_PARAMS", argv[0]);
c52639b4 1172 goto fail;
d76ed9a9 1173 }
1174 max_length = strtoul(argv[++arg], NULL, 0);
1175 if(max_length < 20 || max_length > 450)
1176 {
1177 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
c52639b4 1178 goto fail;
d76ed9a9 1179 }
1180 if(existed && (max_length < ntype->max_length))
1181 {
c52639b4
AS
1182 ntype->max_length = max_length;
1183 chanserv_truncate_notes(ntype);
d76ed9a9 1184 }
1185 ntype->max_length = max_length;
1186
1187 if(existed)
1188 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1189 else
1190 reply("CSMSG_NOTE_CREATED", ntype->name);
1191 return 1;
1192
1193fail:
1194 if(!existed)
c52639b4 1195 dict_remove(note_types, ntype->name);
d76ed9a9 1196 return 0;
1197}
1198
1199static MODCMD_FUNC(cmd_removenote) {
1200 struct note_type *ntype;
1201 int force;
1202
1203 ntype = dict_find(note_types, argv[1], NULL);
1204 force = (argc > 2) && !irccasecmp(argv[2], "force");
1205 if(!ntype)
1206 {
1207 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1208 return 0;
1209 }
1210 if(ntype->refs > 1)
1211 {
1212 if(!force)
1213 {
1214 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1215 return 0;
1216 }
1217 chanserv_flush_note_type(ntype);
1218 }
1219 dict_remove(note_types, argv[1]);
1220 reply("CSMSG_NOTE_DELETED", argv[1]);
1221 return 1;
1222}
1223
1224static int
1225mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1226{
1227 if(!orig)
1228 return 0;
1229 if(orig->modes_set & change->modes_clear)
1230 return 1;
1231 if(orig->modes_clear & change->modes_set)
1232 return 1;
1233 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1234 && strcmp(orig->new_key, change->new_key))
1235 return 1;
1236 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1237 && (orig->new_limit != change->new_limit))
1238 return 1;
1239 return 0;
1240}
1241
1242static char max_length_text[MAXLEN+1][16];
1243
1244static struct helpfile_expansion
1245chanserv_expand_variable(const char *variable)
1246{
1247 struct helpfile_expansion exp;
1248
1249 if(!irccasecmp(variable, "notes"))
1250 {
1251 dict_iterator_t it;
1252 exp.type = HF_TABLE;
1253 exp.value.table.length = 1;
1254 exp.value.table.width = 3;
1255 exp.value.table.flags = 0;
1256 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1257 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1258 exp.value.table.contents[0][0] = "Note Type";
1259 exp.value.table.contents[0][1] = "Visibility";
1260 exp.value.table.contents[0][2] = "Max Length";
1261 for(it=dict_first(note_types); it; it=iter_next(it))
1262 {
1263 struct note_type *ntype = iter_data(it);
1264 int row;
1265
1266 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1267 row = exp.value.table.length++;
1268 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1269 exp.value.table.contents[row][0] = ntype->name;
1270 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1271 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1272 "unknown";
1273 if(!max_length_text[ntype->max_length][0])
1274 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1275 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1276 }
1277 return exp;
1278 }
1279
1280 exp.type = HF_STRING;
1281 exp.value.str = NULL;
1282 return exp;
1283}
1284
1285static struct chanData*
1286register_channel(struct chanNode *cNode, char *registrar)
1287{
1288 struct chanData *channel;
1289 enum levelOption lvlOpt;
1290 enum charOption chOpt;
1291
1292 channel = calloc(1, sizeof(struct chanData));
1293
1294 channel->notes = dict_new();
1295 dict_set_free_data(channel->notes, chanserv_free_note);
1296
1297 channel->registrar = strdup(registrar);
1298 channel->registered = now;
1299 channel->visited = now;
1300 channel->limitAdjusted = now;
a32da4c7 1301 channel->ownerTransfer = now;
d76ed9a9 1302 channel->flags = CHANNEL_DEFAULT_FLAGS;
1303 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1304 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1305 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1306 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1307
1308 channel->prev = NULL;
1309 channel->next = channelList;
1310
1311 if(channelList)
c52639b4 1312 channelList->prev = channel;
d76ed9a9 1313 channelList = channel;
1314 registered_channels++;
1315
1316 channel->channel = cNode;
1317 LockChannel(cNode);
1318 cNode->channel_info = channel;
1319
1320 return channel;
1321}
1322
1323static struct userData*
1136f709 1324add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, time_t seen, const char *info, time_t accessexpiry)
d76ed9a9 1325{
1326 struct userData *ud;
1327
1136f709 1328 if(access_level > UL_OWNER)
c52639b4 1329 return NULL;
d76ed9a9 1330
1331 ud = calloc(1, sizeof(*ud));
1332 ud->channel = channel;
1333 ud->handle = handle;
1334 ud->seen = seen;
1136f709 1335 ud->access = access_level;
d76ed9a9 1336 ud->info = info ? strdup(info) : NULL;
dfaa28a4 1337 ud->accessexpiry = accessexpiry ? accessexpiry : 0;
1338 ud->clvlexpiry = 0;
1339 ud->lastaccess = 0;
d76ed9a9 1340
1341 ud->prev = NULL;
1342 ud->next = channel->users;
1343 if(channel->users)
c52639b4 1344 channel->users->prev = ud;
d76ed9a9 1345 channel->users = ud;
1346
1347 channel->userCount++;
1348 userCount++;
1349
1350 ud->u_prev = NULL;
1351 ud->u_next = ud->handle->channels;
1352 if(ud->u_next)
1353 ud->u_next->u_prev = ud;
1354 ud->handle->channels = ud;
1355
c8ca69a0 1356 ud->flags = USER_FLAGS_DEFAULT;
d76ed9a9 1357 return ud;
1358}
1359
1360static void unregister_channel(struct chanData *channel, const char *reason);
1361
fd20b142 1362static void
1363chanserv_expire_tempuser(void *data)
1364{
1365 struct userData *uData = data;
1366 char *handle;
1367
1368 if (data) {
1369 handle = strdup(uData->handle->handle);
dfaa28a4 1370 if (uData->accessexpiry > 0) {
fd20b142 1371 if (uData->present) {
1372 struct userNode *user, *next_un = NULL;
1373 struct handle_info *hi;
1374
1375 hi = get_handle_info(handle);
1376 for (user = hi->users; user; user = next_un) {
1377 struct mod_chanmode *change;
1378 struct modeNode *mn;
1379 unsigned int count = 0;
1380
1381 send_message(user, chanserv, "CSMSG_AUTO_DELETED", chanserv->nick, uData->channel->channel->name);
1382 if (!(mn = GetUserMode(uData->channel->channel, user)) || !(mn->modes & MODE_CHANOP)) {
1383 next_un = user->next_authed;
1384 continue;
1385 }
1386
1387 change = mod_chanmode_alloc(2);
1388 change->args[count].mode = MODE_REMOVE | MODE_CHANOP;
1389 change->args[count++].u.member = mn;
1390
1391 if (count) {
1392 change->argc = count;
1393 mod_chanmode_announce(chanserv, uData->channel->channel, change);
1394 }
1395 mod_chanmode_free(change);
1396 next_un = user->next_authed;
1397 }
1398 }
1399 del_channel_user(uData, 1);
1400 }
1401 }
1402}
1403
dfaa28a4 1404static void
1405chanserv_expire_tempclvl(void *data)
1406{
1407 struct userData *uData = data;
1408 char *handle;
1409
1410 if (data) {
1411 handle = strdup(uData->handle->handle);
1412 if (uData->clvlexpiry > 0) {
1413 int changemodes = 0;
1414 unsigned int mode = 0;
1415
1416 if (((uData->lastaccess == UL_PEON) || (uData->lastaccess == UL_HALFOP)) && (uData->access >= UL_OP)) {
1417 changemodes = 1;
1418 mode = MODE_REMOVE | MODE_CHANOP;
1419 } else if ((uData->lastaccess == UL_PEON) && (uData->access == UL_HALFOP)) {
1420 changemodes = 1;
1421 mode = MODE_REMOVE | MODE_HALFOP;
1422 } else
1423 changemodes = 0;
1424
1425 if (uData->present) {
1426 struct userNode *user, *next_un = NULL;
1427 struct handle_info *hi;
1428
1429 hi = get_handle_info(handle);
1430 for (user = hi->users; user; user = next_un) {
1431 struct mod_chanmode *change;
1432 struct modeNode *mn;
1433 unsigned int count = 0;
1434
1435 send_message(user, chanserv, "CSMSG_CLVL_EXPIRED", uData->channel->channel->name);
1436 if (!(mn = GetUserMode(uData->channel->channel, user))) {
1437 next_un = user->next_authed;
1438 continue;
1439 }
1440
1441 if (changemodes == 0) {
1442 next_un = user->next_authed;
1443 continue;
1444 }
1445
1446 change = mod_chanmode_alloc(2);
1447 change->args[count].mode = mode;
1448 change->args[count++].u.member = mn;
1449
1450 if (count) {
1451 change->argc = count;
1452 mod_chanmode_announce(chanserv, uData->channel->channel, change);
1453 }
1454 mod_chanmode_free(change);
1455 next_un = user->next_authed;
1456 }
1457 }
1458
1459 uData->access = uData->lastaccess;
1460 uData->lastaccess = 0;
1461 uData->clvlexpiry = 0;
1462 }
1463 }
1464}
1465
d76ed9a9 1466void
1467del_channel_user(struct userData *user, int do_gc)
1468{
1469 struct chanData *channel = user->channel;
1470
1471 channel->userCount--;
1472 userCount--;
1473
fd20b142 1474 timeq_del(0, chanserv_expire_tempuser, user, TIMEQ_IGNORE_WHEN);
dfaa28a4 1475 timeq_del(0, chanserv_expire_tempclvl, user, TIMEQ_IGNORE_WHEN);
fd20b142 1476
d76ed9a9 1477 if(user->prev)
1478 user->prev->next = user->next;
1479 else
1480 channel->users = user->next;
1481 if(user->next)
1482 user->next->prev = user->prev;
1483
1484 if(user->u_prev)
1485 user->u_prev->u_next = user->u_next;
1486 else
1487 user->handle->channels = user->u_next;
1488 if(user->u_next)
1489 user->u_next->u_prev = user->u_prev;
1490
1491 free(user->info);
1492 free(user);
63c95a47 1493 if(do_gc && !channel->users && !IsProtected(channel)) {
1494 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
d76ed9a9 1495 unregister_channel(channel, "lost all users.");
63c95a47 1496 }
d76ed9a9 1497}
1498
ac3bdc8d 1499static struct adduserPending*
1500add_adduser_pending(struct chanNode *channel, struct userNode *user, int level)
1501{
1502 struct adduserPending *ap;
1503 ap = calloc(1,sizeof(struct adduserPending));
1504 ap->channel = channel;
1505 ap->user = user;
1506 ap->level = level;
1507 ap->created = time(NULL);
1508
1509 /* ap->prev defaults to NULL already.. */
1510 ap->next = adduser_pendings;
1511 if(adduser_pendings)
1512 adduser_pendings->prev = ap;
1513 adduser_pendings = ap;
1514 adduser_pendings_count++;
1515 return(ap);
1516}
1517
1518static void
1519del_adduser_pending(struct adduserPending *ap)
1520{
1521 if(ap->prev)
1522 ap->prev->next = ap->next;
1523 else
1524 adduser_pendings = ap->next;
1525
1526 if(ap->next)
1527 ap->next->prev = ap->prev;
1528 free(ap);
1529}
1530
1531static void expire_adduser_pending();
1532
1533/* find_adduser_pending(channel, user) will find an arbitrary record
1534 * from user, channel, or user and channel.
1535 * if user or channel are NULL, they will match any records.
1536 */
1537static struct adduserPending*
1538find_adduser_pending(struct chanNode *channel, struct userNode *user)
1539{
1540 struct adduserPending *ap;
1541
1542 expire_adduser_pending(); /* why not here.. */
1543
1544 if(!channel && !user) /* 2 nulls matches all */
1545 return(adduser_pendings);
1546 for(ap = adduser_pendings;ap;ap = ap->next)
1547 {
1548 if((channel == ap->channel && (user == NULL || user == ap->user)) || (user==ap->user && channel==NULL))
1549 return ap;
1550 }
1551 return NULL;
1552}
1553
1554
1555/* Remove all pendings for a user or channel
1556 *
1557 * called in nickserv.c DelUser() and proto-* unregister_channel()
1558 */
1559void
1560wipe_adduser_pending(struct chanNode *channel, struct userNode *user)
1561{
1562 struct adduserPending *ap;
1563
1564 /* So this is a bit wastefull, i hate dealing with linked lists.
1565 * if its a problem we'll rewrite it right */
1566 while((ap = find_adduser_pending(channel, user))) {
1567 del_adduser_pending(ap);
1568 }
1569}
1570
1571/* Called from nickserv.c cmd_auth after someone auths */
1572void
1573process_adduser_pending(struct userNode *user)
1574{
1575 struct adduserPending *ap;
a03d6c77 1576 if(!user->handle_info)
1577 return; /* not associated with an account */
ac3bdc8d 1578 while((ap = find_adduser_pending(NULL, user)))
1579 {
1580 struct userData *actee;
5ca901c3 1581 if(GetTrueChannelAccess(ap->channel->channel_info, ap->user->handle_info))
1582 {
1583 /* Already on the userlist. do nothing*/
1584 }
1585 else
1586 {
fd20b142 1587 actee = add_channel_user(ap->channel->channel_info, ap->user->handle_info, ap->level, 0, NULL, 0);
5ca901c3 1588 scan_user_presence(actee, NULL);
1589 }
ac3bdc8d 1590 del_adduser_pending(ap);
1591 }
1592}
1593
1594static void
1595expire_adduser_pending()
1596{
1597 struct adduserPending *ap, *ap_next;
1598 ap = adduser_pendings;
1599 while(ap)
1600 {
1601 if((ap->created + ADDUSER_PENDING_EXPIRE) < time(NULL))
1602 { /* expire it */
1603 ap_next = ap->next; /* save next */
1604 del_adduser_pending(ap); /* free and relink */
1605 ap = ap_next; /* advance */
1606 }
1607 else
1608 ap = ap->next;
1609 }
1610}
1611
d76ed9a9 1612static void expire_ban(void *data);
1613
63c95a47 1614struct banData*
d76ed9a9 1615add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1616{
1617 struct banData *bd;
1618 unsigned int ii, l1, l2;
1619
1620 if(!mask)
1621 return NULL;
1622
1623 bd = malloc(sizeof(struct banData));
1624
1625 bd->channel = channel;
1626 bd->set = set;
1627 bd->triggered = triggered;
1628 bd->expires = expires;
1629
1630 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1631 {
1632 extern const char *hidden_host_suffix;
1633 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1634 char *new_mask;
1635
1636 l1 = strlen(mask);
1637 l2 = strlen(old_name);
1638 if(l2+2 > l1)
1639 continue;
1640 if(irccasecmp(mask + l1 - l2, old_name))
1641 continue;
1642 new_mask = alloca(MAXLEN);
a32da4c7 1643 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
d76ed9a9 1644 mask = new_mask;
1645 }
1646 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1647 if(owner)
1648 safestrncpy(bd->owner, owner, sizeof(bd->owner));
ec1a68c8 1649 bd->reason = strdup(reason);
d76ed9a9 1650
1651 if(expires)
c52639b4 1652 timeq_add(expires, expire_ban, bd);
d76ed9a9 1653
1654 bd->prev = NULL;
c8273589 1655 bd->next = channel->bans; /* lamers */
d76ed9a9 1656 if(channel->bans)
c52639b4 1657 channel->bans->prev = bd;
d76ed9a9 1658 channel->bans = bd;
1659 channel->banCount++;
1660 banCount++;
1661
1662 return bd;
1663}
1664
1665static void
1666del_channel_ban(struct banData *ban)
1667{
1668 ban->channel->banCount--;
1669 banCount--;
1670
1671 if(ban->prev)
1672 ban->prev->next = ban->next;
1673 else
1674 ban->channel->bans = ban->next;
1675
1676 if(ban->next)
1677 ban->next->prev = ban->prev;
1678
1679 if(ban->expires)
c52639b4 1680 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
d76ed9a9 1681
1682 if(ban->reason)
1683 free(ban->reason);
1684
1685 free(ban);
1686}
1687
1688static void
c8273589 1689expire_ban(void *data) /* lamer.. */
d76ed9a9 1690{
1691 struct banData *bd = data;
1692 if(!IsSuspended(bd->channel))
1693 {
1694 struct banList bans;
1695 struct mod_chanmode change;
1696 unsigned int ii;
1697 bans = bd->channel->channel->banlist;
1698 mod_chanmode_init(&change);
1699 for(ii=0; ii<bans.used; ii++)
1700 {
1701 if(!strcmp(bans.list[ii]->ban, bd->mask))
c52639b4 1702 {
d76ed9a9 1703 change.argc = 1;
1704 change.args[0].mode = MODE_REMOVE|MODE_BAN;
a32da4c7 1705 change.args[0].u.hostmask = bd->mask;
d76ed9a9 1706 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1707 break;
1708 }
1709 }
1710 }
1711 bd->expires = 0;
1712 del_channel_ban(bd);
1713}
1714
1715static void chanserv_expire_suspension(void *data);
1716
1717static void
1718unregister_channel(struct chanData *channel, const char *reason)
1719{
1720 struct mod_chanmode change;
1721 char msgbuf[MAXLEN];
1722
1723 /* After channel unregistration, the following must be cleaned
1724 up:
1725 - Channel information.
1726 - Channel users.
c8273589 1727 - Channel bans. (lamers)
d76ed9a9 1728 - Channel suspension data.
ac3bdc8d 1729 - adduser_pending data.
d76ed9a9 1730 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1731 */
1732
1733 if(!channel)
c52639b4 1734 return;
d76ed9a9 1735
1736 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1737
1117fc5a 1738 if(off_channel > 0)
a32da4c7 1739 {
1740 mod_chanmode_init(&change);
1741 change.modes_clear |= MODE_REGISTERED;
1742 mod_chanmode_announce(chanserv, channel->channel, &change);
1743 }
d76ed9a9 1744
ac3bdc8d 1745 wipe_adduser_pending(channel->channel, NULL);
1746
d76ed9a9 1747 while(channel->users)
c52639b4 1748 del_channel_user(channel->users, 0);
d76ed9a9 1749
1750 while(channel->bans)
c52639b4 1751 del_channel_ban(channel->bans);
d76ed9a9 1752
1753 free(channel->topic);
1754 free(channel->registrar);
1755 free(channel->greeting);
1756 free(channel->user_greeting);
1757 free(channel->topic_mask);
1758
1759 if(channel->prev)
1760 channel->prev->next = channel->next;
1761 else
1762 channelList = channel->next;
1763
1764 if(channel->next)
1765 channel->next->prev = channel->prev;
1766
1767 if(channel->suspended)
1768 {
c52639b4
AS
1769 struct chanNode *cNode = channel->channel;
1770 struct suspended *suspended, *next_suspended;
d76ed9a9 1771
1772 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1773 {
1774 next_suspended = suspended->previous;
1775 free(suspended->suspender);
1776 free(suspended->reason);
1777 if(suspended->expires)
1778 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1779 free(suspended);
1780 }
1781
c52639b4
AS
1782 if(cNode)
1783 cNode->channel_info = NULL;
d76ed9a9 1784 }
1785 channel->channel->channel_info = NULL;
1786
1787 dict_delete(channel->notes);
1788 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1789 if(!IsSuspended(channel))
1790 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1791 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1792 UnlockChannel(channel->channel);
1793 free(channel);
1794 registered_channels--;
1795}
1796
1797static void
1798expire_channels(UNUSED_ARG(void *data))
1799{
1800 struct chanData *channel, *next;
1801 struct userData *user;
1802 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1803
1804 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1805 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1806
1807 for(channel = channelList; channel; channel = next)
1808 {
c52639b4 1809 next = channel->next;
d76ed9a9 1810
1811 /* See if the channel can be expired. */
1812 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1813 || IsProtected(channel))
1814 continue;
1815
1816 /* Make sure there are no high-ranking users still in the channel. */
1817 for(user=channel->users; user; user=user->next)
1818 if(user->present && (user->access >= UL_PRESENT))
1819 break;
1820 if(user)
1821 continue;
1822
1823 /* Unregister the channel */
1824 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
63c95a47 1825 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
d76ed9a9 1826 unregister_channel(channel, "registration expired.");
1827 }
1828
1829 if(chanserv_conf.channel_expire_frequency)
c52639b4 1830 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
d76ed9a9 1831}
1832
1136f709 1833static void
1834expire_dnrs(UNUSED_ARG(void *data))
1835{
1836 dict_iterator_t it, next;
1837 struct do_not_register *dnr;
1838
1839 for(it = dict_first(handle_dnrs); it; it = next)
1840 {
1841 dnr = iter_data(it);
1842 next = iter_next(it);
1843 if(dnr->expires && dnr->expires <= now)
1844 dict_remove(handle_dnrs, dnr->chan_name + 1);
1845 }
1846 for(it = dict_first(plain_dnrs); it; it = next)
1847 {
1848 dnr = iter_data(it);
1849 next = iter_next(it);
1850 if(dnr->expires && dnr->expires <= now)
1851 dict_remove(plain_dnrs, dnr->chan_name + 1);
1852 }
1853 for(it = dict_first(mask_dnrs); it; it = next)
1854 {
1855 dnr = iter_data(it);
1856 next = iter_next(it);
1857 if(dnr->expires && dnr->expires <= now)
1858 dict_remove(mask_dnrs, dnr->chan_name + 1);
1859 }
1860
1861 if(chanserv_conf.dnr_expire_frequency)
1862 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1863}
1864
d76ed9a9 1865static int
ff5f1ab2 1866protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel, int protect_invitables)
d76ed9a9 1867{
1868 char protect = channel->chOpts[chProtect];
1869 struct userData *cs_victim, *cs_aggressor;
1870
ff5f1ab2 1871 /* If victim access level is greater than set invitelevel, don't let
1872 * us kick them, but don't consider it punishment if someone else does
1873 */
d76ed9a9 1874
ff5f1ab2 1875
1876 if(victim == aggressor)
1877 return 0;
d76ed9a9 1878 /* Don't protect if the victim isn't authenticated (because they
1879 can't be a channel user), unless we are to protect non-users
1880 also. */
ff5f1ab2 1881
d76ed9a9 1882 cs_victim = GetChannelAccess(channel, victim->handle_info);
ff5f1ab2 1883
1884 /* If they have enough access to invite themselvs through a ban,
1885 * and its us kicking them, don't. -Rubin */
1886 if(protect_invitables==true && cs_victim && (cs_victim->access >= channel->lvlOpts[lvlInviteMe]))
1887 return 1;
1888
1889 if(protect == 'n')
c52639b4 1890 return 0;
ff5f1ab2 1891
d76ed9a9 1892 if(protect != 'a' && !cs_victim)
1893 return 0;
1894
1895 /* Protect if the aggressor isn't a user because at this point,
1896 the aggressor can only be less than or equal to the victim. */
ff5f1ab2 1897
1898 /* Not protected from chanserv except above */
1899 /* XXX: need to generic-ize chanserv to "one of x3's services" somehow.. */
1900 if(aggressor == chanserv)
1901 return 0;
1902
d76ed9a9 1903 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1904 if(!cs_aggressor)
1905 return 1;
1906
1907 /* If the aggressor was a user, then the victim can't be helped. */
1908 if(!cs_victim)
1909 return 0;
1910
1911 switch(protect)
1912 {
1913 case 'l':
c52639b4 1914 if(cs_victim->access > cs_aggressor->access)
d76ed9a9 1915 return 1;
c52639b4 1916 break;
d76ed9a9 1917 case 'a':
1918 case 'e':
c52639b4 1919 if(cs_victim->access >= cs_aggressor->access)
d76ed9a9 1920 return 1;
c52639b4 1921 break;
d76ed9a9 1922 }
1923
1924 return 0;
1925}
1926
1927static int
c092fcad 1928validate_op(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, struct userNode *victim)
d76ed9a9 1929{
1930 struct chanData *cData = channel->channel_info;
1931 struct userData *cs_victim;
1932
1933 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
b75e24a3 1934 || (cs_victim->access < UL_OP /* cData->lvlOpts[lvlGiveOps]*/))
d76ed9a9 1935 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1936 {
c092fcad 1937 if(cmd)
c52639b4 1938 reply("CSMSG_OPBY_LOCKED");
c092fcad 1939 else
1940 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
c52639b4 1941 return 0;
d76ed9a9 1942 }
1943
1944 return 1;
1945}
1946
55342ce8 1947static int
c092fcad 1948validate_halfop(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, struct userNode *victim)
55342ce8 1949{
1950 struct chanData *cData = channel->channel_info;
1951 struct userData *cs_victim;
1952
1953 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
b75e24a3 1954 || (cs_victim->access < UL_HALFOP /* cData->lvlOpts[lvlGiveHalfOps] */))
55342ce8 1955 && !check_user_level(channel, user, lvlEnfHalfOps, 0, 0))
1956 {
c092fcad 1957 reply("CSMSG_HOPBY_LOCKED");
55342ce8 1958 return 0;
1959 }
1960
1961 return 1;
1962}
1963
1964
d76ed9a9 1965static int
c092fcad 1966validate_deop(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, struct userNode *victim)
d76ed9a9 1967{
1968 if(IsService(victim))
1969 {
c52639b4
AS
1970 reply("MSG_SERVICE_IMMUNE", victim->nick);
1971 return 0;
d76ed9a9 1972 }
1973
ff5f1ab2 1974 if(protect_user(victim, user, channel->channel_info, false))
d76ed9a9 1975 {
c52639b4
AS
1976 reply("CSMSG_USER_PROTECTED", victim->nick);
1977 return 0;
d76ed9a9 1978 }
1979
1980 return 1;
1981}
1982
55342ce8 1983static int
c092fcad 1984validate_dehop(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, struct userNode *victim)
55342ce8 1985{
1986 if(IsService(victim))
1987 {
c092fcad 1988 reply("MSG_SERVICE_IMMUNE", victim->nick);
55342ce8 1989 return 0;
1990 }
1991
ff5f1ab2 1992 if(protect_user(victim, user, channel->channel_info, false))
55342ce8 1993 {
c092fcad 1994 reply("CSMSG_USER_PROTECTED", victim->nick);
55342ce8 1995 return 0;
1996 }
1997
1998 return 1;
1999}
2000
d76ed9a9 2001static struct do_not_register *
1136f709 2002chanserv_add_dnr(const char *chan_name, const char *setter, time_t expires, const char *reason)
d76ed9a9 2003{
2004 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
2005 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
2006 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
2007 strcpy(dnr->reason, reason);
2008 dnr->set = now;
1136f709 2009 dnr->expires = expires;
d76ed9a9 2010 if(dnr->chan_name[0] == '*')
2011 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
2012 else if(strpbrk(dnr->chan_name, "*?"))
2013 dict_insert(mask_dnrs, dnr->chan_name, dnr);
2014 else
2015 dict_insert(plain_dnrs, dnr->chan_name, dnr);
2016 return dnr;
2017}
2018
2019static struct dnrList
1136f709 2020chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
d76ed9a9 2021{
2022 struct dnrList list;
1136f709 2023 dict_iterator_t it, next;
d76ed9a9 2024 struct do_not_register *dnr;
2025
2026 dnrList_init(&list);
1136f709 2027
1117fc5a 2028 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1136f709 2029 {
2030 if(dnr->expires && dnr->expires <= now)
2031 dict_remove(handle_dnrs, handle);
2032 else if (list.used < max)
2033 dnrList_append(&list, dnr);
2034 }
2035
d76ed9a9 2036 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1136f709 2037 {
2038 if(dnr->expires && dnr->expires <= now)
2039 dict_remove(plain_dnrs, chan_name);
2040 else if (list.used < max)
2041 dnrList_append(&list, dnr);
2042 }
d76ed9a9 2043 if(chan_name)
1136f709 2044 {
2045 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
2046 {
2047 next = iter_next(it);
2048 if(!match_ircglob(chan_name, iter_key(it)))
2049 continue;
2050 dnr = iter_data(it);
2051 if(dnr->expires && dnr->expires <= now)
2052 dict_remove(mask_dnrs, iter_key(it));
2053 else
2054 dnrList_append(&list, dnr);
2055 }
2056 }
d76ed9a9 2057 return list;
2058}
2059
1136f709 2060static int dnr_print_func(struct do_not_register *dnr, void *extra)
2061{
2062 struct userNode *user;
2063 char buf1[INTERVALLEN];
2064 char buf2[INTERVALLEN];
2065
2066 user = extra;
2067 if(dnr->set)
2068 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&dnr->set));
2069 if(dnr->expires)
2070 {
2071 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&dnr->expires));
2072 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
2073 }
2074 else if(dnr->set)
2075 {
2076 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
2077 }
2078 else
2079 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
2080 return 0;
2081}
2082
d76ed9a9 2083static unsigned int
1117fc5a 2084chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
d76ed9a9 2085{
2086 struct dnrList list;
d76ed9a9 2087 unsigned int ii;
d76ed9a9 2088
1136f709 2089 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
d76ed9a9 2090 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1136f709 2091 dnr_print_func(list.list[ii], user);
d76ed9a9 2092 if(ii < list.used)
2093 reply("CSMSG_MORE_DNRS", list.used - ii);
2094 free(list.list);
2095 return ii;
2096}
2097
2098struct do_not_register *
2099chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
2100{
1136f709 2101 struct dnrList list;
d76ed9a9 2102 struct do_not_register *dnr;
d76ed9a9 2103
1136f709 2104 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
2105 dnr = list.used ? list.list[0] : NULL;
2106 free(list.list);
2107 return dnr;
2108}
2109
2110static unsigned int send_dnrs(struct userNode *user, dict_t dict)
2111{
2112 struct do_not_register *dnr;
2113 dict_iterator_t it, next;
2114 unsigned int matches = 0;
2115
2116 for(it = dict_first(dict); it; it = next)
d76ed9a9 2117 {
1136f709 2118 dnr = iter_data(it);
2119 next = iter_next(it);
2120 if(dnr->expires && dnr->expires <= now)
2121 {
2122 dict_remove(dict, iter_key(it));
2123 continue;
2124 }
2125 dnr_print_func(dnr, user);
2126 matches++;
d76ed9a9 2127 }
1136f709 2128
2129 return matches;
d76ed9a9 2130}
2131
2132static CHANSERV_FUNC(cmd_noregister)
2133{
2134 const char *target;
1136f709 2135 const char *reason;
2136 time_t expiry, duration;
d76ed9a9 2137 unsigned int matches;
2138
2139 if(argc < 2)
2140 {
d76ed9a9 2141 reply("CSMSG_DNR_SEARCH_RESULTS");
1136f709 2142 matches = send_dnrs(user, handle_dnrs);
2143 matches += send_dnrs(user, plain_dnrs);
2144 matches += send_dnrs(user, mask_dnrs);
d76ed9a9 2145 if(matches)
2146 reply("MSG_MATCH_COUNT", matches);
2147 else
2148 reply("MSG_NO_MATCHES");
2149 return 0;
2150 }
2151
2152 target = argv[1];
2153
2154 if(!IsChannelName(target) && (*target != '*'))
2155 {
2156 reply("CSMSG_NOT_DNR", target);
2157 return 0;
2158 }
2159
2160 if(argc > 2)
2161 {
1136f709 2162 if(argc == 3)
2163 {
2164 reply("MSG_INVALID_DURATION", argv[2]);
2165 return 0;
2166 }
2167
2168 if(!strcmp(argv[2], "0"))
2169 expiry = 0;
2170 else if((duration = ParseInterval(argv[2])))
2171 expiry = now + duration;
2172 else
2173 {
2174 reply("MSG_INVALID_DURATION", argv[2]);
2175 return 0;
2176 }
2177
2178 reason = unsplit_string(argv + 3, argc - 3, NULL);
2179
d76ed9a9 2180 if((*target == '*') && !get_handle_info(target + 1))
2181 {
2182 reply("MSG_HANDLE_UNKNOWN", target + 1);
2183 return 0;
2184 }
1136f709 2185 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
d76ed9a9 2186 reply("CSMSG_NOREGISTER_CHANNEL", target);
2187 return 1;
2188 }
2189
2190 reply("CSMSG_DNR_SEARCH_RESULTS");
0f6fe38c 2191 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
2192 reply("CSMSG_BAR");
d76ed9a9 2193 if(*target == '*')
1117fc5a 2194 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
d76ed9a9 2195 else
2196 matches = chanserv_show_dnrs(user, cmd, target, NULL);
2197 if(!matches)
2198 reply("MSG_NO_MATCHES");
2199 return 0;
2200}
2201
2202static CHANSERV_FUNC(cmd_allowregister)
2203{
2204 const char *chan_name = argv[1];
2205
1136f709 2206 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2207 || dict_remove(plain_dnrs, chan_name)
2208 || dict_remove(mask_dnrs, chan_name))
d76ed9a9 2209 {
d76ed9a9 2210 reply("CSMSG_DNR_REMOVED", chan_name);
1136f709 2211 return 1;
ff3b058a 2212 }
1136f709 2213 reply("CSMSG_NO_SUCH_DNR", chan_name);
2214 return 0;
2215}
2216
2217struct dnr_search {
2218 struct userNode *source;
2219 char *chan_mask;
2220 char *setter_mask;
2221 char *reason_mask;
2222 time_t min_set, max_set;
2223 time_t min_expires, max_expires;
2224 unsigned int limit;
2225};
2226
2227static int
2228dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
2229{
2230 return !((dnr->set < search->min_set)
2231 || (dnr->set > search->max_set)
2232 || (dnr->expires < search->min_expires)
2233 || (search->max_expires
2234 && ((dnr->expires == 0)
2235 || (dnr->expires > search->max_expires)))
2236 || (search->chan_mask
2237 && !match_ircglob(dnr->chan_name, search->chan_mask))
2238 || (search->setter_mask
2239 && !match_ircglob(dnr->setter, search->setter_mask))
2240 || (search->reason_mask
2241 && !match_ircglob(dnr->reason, search->reason_mask)));
2242}
2243
2244static struct dnr_search *
2245dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
2246{
2247 struct dnr_search *discrim;
2248 unsigned int ii;
2249
2250 discrim = calloc(1, sizeof(*discrim));
2251 discrim->source = user;
2252 discrim->chan_mask = NULL;
2253 discrim->setter_mask = NULL;
2254 discrim->reason_mask = NULL;
2255 discrim->max_set = INT_MAX;
2256 discrim->limit = 50;
2257
2258 for(ii=0; ii<argc; ++ii)
d76ed9a9 2259 {
1136f709 2260 if(ii == argc - 1)
2261 {
2262 reply("MSG_MISSING_PARAMS", argv[ii]);
2263 goto fail;
2264 }
2265 else if(0 == irccasecmp(argv[ii], "channel"))
2266 {
2267 discrim->chan_mask = argv[++ii];
2268 }
2269 else if(0 == irccasecmp(argv[ii], "setter"))
2270 {
2271 discrim->setter_mask = argv[++ii];
2272 }
2273 else if(0 == irccasecmp(argv[ii], "reason"))
2274 {
2275 discrim->reason_mask = argv[++ii];
2276 }
2277 else if(0 == irccasecmp(argv[ii], "limit"))
2278 {
2279 discrim->limit = strtoul(argv[++ii], NULL, 0);
2280 }
2281 else if(0 == irccasecmp(argv[ii], "set"))
2282 {
2283 const char *cmp = argv[++ii];
2284 if(cmp[0] == '<') {
2285 if(cmp[1] == '=')
2286 discrim->min_set = now - ParseInterval(cmp + 2);
2287 else
2288 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
2289 } else if(cmp[0] == '=') {
2290 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
2291 } else if(cmp[0] == '>') {
2292 if(cmp[1] == '=')
2293 discrim->max_set = now - ParseInterval(cmp + 2);
2294 else
2295 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
2296 } else {
2297 discrim->max_set = now - (ParseInterval(cmp) - 1);
2298 }
2299 }
2300 else if(0 == irccasecmp(argv[ii], "expires"))
2301 {
2302 const char *cmp = argv[++ii];
2303 if(cmp[0] == '<') {
2304 if(cmp[1] == '=')
2305 discrim->max_expires = now + ParseInterval(cmp + 2);
2306 else
2307 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
2308 } else if(cmp[0] == '=') {
2309 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
2310 } else if(cmp[0] == '>') {
2311 if(cmp[1] == '=')
2312 discrim->min_expires = now + ParseInterval(cmp + 2);
2313 else
2314 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
2315 } else {
2316 discrim->min_expires = now + (ParseInterval(cmp) - 1);
2317 }
2318 }
2319 else
2320 {
2321 reply("MSG_INVALID_CRITERIA", argv[ii]);
2322 goto fail;
2323 }
ff3b058a 2324 }
1136f709 2325 return discrim;
2326
2327 fail:
2328 free(discrim);
2329 return NULL;
2330}
2331
2332typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
2333
2334static unsigned int
2335dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
2336{
2337 struct do_not_register *dnr;
2338 dict_iterator_t next;
2339 dict_iterator_t it;
2340 unsigned int count;
2341 int target_fixed;
2342
2343 /* Initialize local variables. */
2344 count = 0;
2345 target_fixed = 0;
2346 if(discrim->chan_mask)
ff3b058a 2347 {
1136f709 2348 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
2349 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
2350 target_fixed = 1;
2351 }
2352
2353 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
2354 {
2355 /* Check against account-based DNRs. */
2356 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
2357 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2358 dsf(dnr, data);
2359 }
2360 else if(target_fixed)
2361 {
2362 /* Check against channel-based DNRs. */
2363 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2364 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2365 dsf(dnr, data);
2366 }
2367 else
2368 {
2369 /* Exhaustively search account DNRs. */
2370 for(it = dict_first(handle_dnrs); it; it = next)
2371 {
2372 next = iter_next(it);
2373 dnr = iter_data(it);
2374 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2375 break;
2376 }
2377
2378 /* Do the same for channel DNRs. */
2379 for(it = dict_first(plain_dnrs); it; it = next)
2380 {
2381 next = iter_next(it);
2382 dnr = iter_data(it);
2383 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2384 break;
2385 }
2386
2387 /* Do the same for wildcarded channel DNRs. */
2388 for(it = dict_first(mask_dnrs); it; it = next)
2389 {
2390 next = iter_next(it);
2391 dnr = iter_data(it);
2392 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2393 break;
2394 }
2395 }
2396 return count;
2397}
2398
2399static int
2400dnr_remove_func(struct do_not_register *match, void *extra)
2401{
2402 struct userNode *user;
2403 char *chan_name;
2404
2405 chan_name = alloca(strlen(match->chan_name) + 1);
2406 strcpy(chan_name, match->chan_name);
2407 user = extra;
2408 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2409 || dict_remove(plain_dnrs, chan_name)
2410 || dict_remove(mask_dnrs, chan_name))
2411 {
2412 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2413 }
2414 return 0;
2415}
2416
2417static int
2418dnr_count_func(struct do_not_register *match, void *extra)
2419{
2420 return 0; (void)match; (void)extra;
2421}
2422
2423static MODCMD_FUNC(cmd_dnrsearch)
2424{
2425 struct dnr_search *discrim;
2426 dnr_search_func action;
2427 struct svccmd *subcmd;
2428 unsigned int matches;
2429 char buf[MAXLEN];
2430
2431 sprintf(buf, "dnrsearch %s", argv[1]);
2432 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2433 if(!subcmd)
2434 {
2435 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2436 return 0;
d76ed9a9 2437 }
1136f709 2438 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2439 return 0;
2440 if(!irccasecmp(argv[1], "print"))
2441 action = dnr_print_func;
2442 else if(!irccasecmp(argv[1], "remove"))
2443 action = dnr_remove_func;
2444 else if(!irccasecmp(argv[1], "count"))
2445 action = dnr_count_func;
ff3b058a 2446 else
2447 {
1136f709 2448 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
ff3b058a 2449 return 0;
2450 }
1136f709 2451
2452 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2453 if(!discrim)
2454 return 0;
2455
2456 if(action == dnr_print_func)
2457 reply("CSMSG_DNR_SEARCH_RESULTS");
2458 matches = dnr_search(discrim, action, user);
2459 if(matches)
2460 reply("MSG_MATCH_COUNT", matches);
2461 else
2462 reply("MSG_NO_MATCHES");
2463 free(discrim);
d76ed9a9 2464 return 1;
2465}
2466
2467unsigned int
2468chanserv_get_owned_count(struct handle_info *hi)
2469{
2470 struct userData *cList;
2471 unsigned int owned;
2472
2473 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2474 if(cList->access == UL_OWNER)
2475 owned++;
2476 return owned;
2477}
2478
2479static CHANSERV_FUNC(cmd_register)
2480{
d76ed9a9 2481 struct handle_info *handle;
2482 struct chanData *cData;
2483 struct modeNode *mn;
2484 char reason[MAXLEN];
2485 char *chan_name;
2486 unsigned int new_channel, force=0;
2487 struct do_not_register *dnr;
f8f30e75 2488 unsigned int n;
2489
08895577 2490 if (checkDefCon(DEFCON_NO_NEW_CHANNELS) && !IsOper(user)) {
2491 reply("CSMSG_DEFCON_NO_NEW_CHANNELS");
2492 return 0;
2493 }
d76ed9a9 2494
2495 if(channel)
2496 {
2497 if(channel->channel_info)
2498 {
2499 reply("CSMSG_ALREADY_REGGED", channel->name);
2500 return 0;
2501 }
2502
2503 if(channel->bad_channel)
2504 {
2505 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2506 return 0;
2507 }
2508
f8f30e75 2509 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
d76ed9a9 2510 {
2511 reply("CSMSG_MUST_BE_OPPED", channel->name);
2512 return 0;
2513 }
2514
2515 new_channel = 0;
2516 chan_name = channel->name;
2517 }
2518 else
2519 {
fc8798ec 2520 if(argc < 2)
2521 {
2522 reply("MSG_MISSING_PARAMS", cmd->name);
567a5f26 2523 svccmd_send_help_brief(user, chanserv, cmd);
fc8798ec 2524 return 0;
2525 }
2526 if(!IsChannelName(argv[1]))
d76ed9a9 2527 {
2528 reply("MSG_NOT_CHANNEL_NAME");
2529 return 0;
2530 }
2531
2532 if(opserv_bad_channel(argv[1]))
2533 {
2534 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2535 return 0;
2536 }
2537
2538 new_channel = 1;
2539 chan_name = argv[1];
2540 }
2541
2542 if(argc >= (new_channel+2))
2543 {
c52639b4
AS
2544 if(!IsHelping(user))
2545 {
2546 reply("CSMSG_PROXY_FORBIDDEN");
2547 return 0;
2548 }
d76ed9a9 2549
c52639b4 2550 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
d76ed9a9 2551 return 0;
2552 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2553 dnr = chanserv_is_dnr(chan_name, handle);
f8f30e75 2554
2555 /* Check if they are over the limit.. */
2556 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2557 {
2558 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2559 return 0;
2560 }
2561
d76ed9a9 2562 }
2563 else
2564 {
f8f30e75 2565 handle = user->handle_info;
d76ed9a9 2566 dnr = chanserv_is_dnr(chan_name, handle);
f8f30e75 2567 /* Check if they are over the limit.. */
2568 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2569 {
2570 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf.max_owned);
2571 return 0;
2572 }
2573 /* Check if another service is in the channel */
2574 if(channel)
2575 for(n = 0; n < channel->members.used; n++)
2576 {
2577 mn = channel->members.list[n];
2578 if((mn && mn->user && (mn->user->modes & FLAGS_SERVICE)) || IsLocal(mn->user))
2579 {
2580 reply("CSMSG_ANOTHER_SERVICE");
2581 return 0;
2582 }
2583 }
d76ed9a9 2584 }
2585 if(dnr && !force)
2586 {
2587 if(!IsHelping(user))
2588 reply("CSMSG_DNR_CHANNEL", chan_name);
2589 else
1117fc5a 2590 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
d76ed9a9 2591 return 0;
2592 }
2593
f8f30e75 2594 /* now handled above for message specilization *
d76ed9a9 2595 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2596 {
2597 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2598 return 0;
2599 }
f8f30e75 2600 */
d76ed9a9 2601
d3abe0df 2602 if (chanserv_conf.valid_channel_regex_set) {
ed5c805e 2603 int err = regexec(&chanserv_conf.valid_channel_regex, chan_name, 0, 0, 0);
d3abe0df 2604 if (err) {
2605 char buff[256];
2606 buff[regerror(err, &chanserv_conf.valid_channel_regex, buff, sizeof(buff))] = 0;
2607 log_module(CS_LOG, LOG_INFO, "regexec error: %s (%d)", buff, err);
2608 }
2609 if(err == REG_NOMATCH) {
ed5c805e 2610 reply("CSMSG_ILLEGAL_CHANNEL", chan_name);
d3abe0df 2611 return 0;
2612 }
2613 }
2614
d76ed9a9 2615 if(new_channel)
ed5c805e 2616 channel = AddChannel(chan_name, now, NULL, NULL, NULL);
d76ed9a9 2617
2618 cData = register_channel(channel, user->handle_info->handle);
fd20b142 2619 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL, 0), NULL);
d76ed9a9 2620 cData->modes = chanserv_conf.default_modes;
1117fc5a 2621 if(off_channel > 0)
a32da4c7 2622 cData->modes.modes_set |= MODE_REGISTERED;
ec1a68c8 2623 if (IsOffChannel(cData))
2624 {
2625 mod_chanmode_announce(chanserv, channel, &cData->modes);
2626 }
2627 else
2628 {
2629 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2630 change->args[change->argc].mode = MODE_CHANOP;
2631 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2632 change->argc++;
2633 mod_chanmode_announce(chanserv, channel, change);
2634 mod_chanmode_free(change);
2635 }
d76ed9a9 2636
2637 /* Initialize the channel's max user record. */
2638 cData->max = channel->members.used;
8b9e7d45 2639 cData->maxsetinfo = chanserv_conf.max_userinfo_length;
d76ed9a9 2640
2641 if(handle != user->handle_info)
2642 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2643 else
d76ed9a9 2644
2645 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
09a3057c 2646 global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "CSMSG_REGISTERED_TO", channel->name,
2647 handle->handle, user->handle_info->handle);
d76ed9a9 2648 return 1;
2649}
2650
2651static const char *
2652make_confirmation_string(struct userData *uData)
2653{
2654 static char strbuf[16];
2655 char *src;
2656 unsigned int accum;
2657
2658 accum = 0;
2659 for(src = uData->handle->handle; *src; )
2660 accum = accum * 31 + toupper(*src++);
2661 if(uData->channel)
2662 for(src = uData->channel->channel->name; *src; )
2663 accum = accum * 31 + toupper(*src++);
2664 sprintf(strbuf, "%08x", accum);
2665 return strbuf;
2666}
2667
2668static CHANSERV_FUNC(cmd_unregister)
2669{
2670 char *name;
2671 char reason[MAXLEN];
2672 struct chanData *cData;
2673 struct userData *uData;
2674
2675 cData = channel->channel_info;
2676 if(!cData)
2677 {
2678 reply("CSMSG_NOT_REGISTERED", channel->name);
2679 return 0;
2680 }
2681
2682 uData = GetChannelUser(cData, user->handle_info);
2683 if(!uData || (uData->access < UL_OWNER))
2684 {
2685 reply("CSMSG_NO_ACCESS");
2686 return 0;
2687 }
2688
2689 if(IsProtected(cData))
2690 {
2691 reply("CSMSG_UNREG_NODELETE", channel->name);
2692 return 0;
2693 }
2694
2695 if(!IsHelping(user))
2696 {
2697 const char *confirm_string;
2698 if(IsSuspended(cData))
2699 {
2700 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2701 return 0;
2702 }
2703 confirm_string = make_confirmation_string(uData);
c52639b4
AS
2704 if((argc < 2) || strcmp(argv[1], confirm_string))
2705 {
2706 reply("CSMSG_CONFIRM_UNREG", channel->name, confirm_string);
2707 return 0;
2708 }
d76ed9a9 2709 }
2710
2711 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2712 name = strdup(channel->name);
2713 unregister_channel(cData, reason);
63c95a47 2714 spamserv_cs_unregister(user, channel, manually, "unregistered");
d76ed9a9 2715 reply("CSMSG_UNREG_SUCCESS", name);
2716 free(name);
2717 return 1;
2718}
2719
63c95a47 2720static void
2721ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2722{
2723 extern struct userNode *spamserv;
2724 struct mod_chanmode *change;
2725
2726 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2727 {
2728 change = mod_chanmode_alloc(2);
2729 change->argc = 2;
2730 change->args[0].mode = MODE_CHANOP;
2731 change->args[0].u.member = AddChannelUser(chanserv, channel);
2732 change->args[1].mode = MODE_CHANOP;
2733 change->args[1].u.member = AddChannelUser(spamserv, channel);
2734 }
2735 else
2736 {
2737 change = mod_chanmode_alloc(1);
2738 change->argc = 1;
2739 change->args[0].mode = MODE_CHANOP;
2740 change->args[0].u.member = AddChannelUser(chanserv, channel);
2741 }
2742
2743 mod_chanmode_announce(chanserv, channel, change);
2744 mod_chanmode_free(change);
2745}
2746
d76ed9a9 2747static CHANSERV_FUNC(cmd_move)
2748{
1117fc5a 2749 struct mod_chanmode change;
d76ed9a9 2750 struct chanNode *target;
2751 struct modeNode *mn;
2752 struct userData *uData;
d76ed9a9 2753 struct do_not_register *dnr;
63c95a47 2754 int chanserv_join = 0, spamserv_join;
d76ed9a9 2755
2756 REQUIRE_PARAMS(2);
2757
2758 if(IsProtected(channel->channel_info))
2759 {
c52639b4
AS
2760 reply("CSMSG_MOVE_NODELETE", channel->name);
2761 return 0;
d76ed9a9 2762 }
2763
2764 if(!IsChannelName(argv[1]))
2765 {
2766 reply("MSG_NOT_CHANNEL_NAME");
2767 return 0;
2768 }
2769
2770 if(opserv_bad_channel(argv[1]))
2771 {
2772 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2773 return 0;
2774 }
2775
2776 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2777 {
2778 for(uData = channel->channel_info->users; uData; uData = uData->next)
2779 {
2780 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2781 {
2782 if(!IsHelping(user))
2783 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2784 else
1117fc5a 2785 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
d76ed9a9 2786 return 0;
2787 }
2788 }
2789 }
2790
1117fc5a 2791 mod_chanmode_init(&change);
d76ed9a9 2792 if(!(target = GetChannel(argv[1])))
2793 {
2aef5f4b 2794 target = AddChannel(argv[1], now, NULL, NULL, NULL);
d76ed9a9 2795 if(!IsSuspended(channel->channel_info))
63c95a47 2796 chanserv_join = 1;
d76ed9a9 2797 }
2798 else if(target->channel_info)
2799 {
c52639b4
AS
2800 reply("CSMSG_ALREADY_REGGED", target->name);
2801 return 0;
d76ed9a9 2802 }
2803 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2804 && !IsHelping(user))
2805 {
2806 reply("CSMSG_MUST_BE_OPPED", target->name);
2807 return 0;
2808 }
2809 else if(!IsSuspended(channel->channel_info))
63c95a47 2810 chanserv_join = 1;
d76ed9a9 2811
1117fc5a 2812 if(off_channel > 0)
2813 {
2814 /* Clear MODE_REGISTERED from old channel, add it to new. */
2815 change.argc = 0;
2816 change.modes_clear = MODE_REGISTERED;
2817 mod_chanmode_announce(chanserv, channel, &change);
2818 change.modes_clear = 0;
2819 change.modes_set = MODE_REGISTERED;
2820 mod_chanmode_announce(chanserv, target, &change);
2821 }
2822
d76ed9a9 2823 /* Move the channel_info to the target channel; it
2824 shouldn't be necessary to clear timeq callbacks
2825 for the old channel. */
2826 target->channel_info = channel->channel_info;
2827 target->channel_info->channel = target;
2828 channel->channel_info = NULL;
2829
63c95a47 2830 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2831
2832 if (chanserv_join)
2833 ss_cs_join_channel(target, spamserv_join);
d76ed9a9 2834
d76ed9a9 2835 if(!IsSuspended(target->channel_info))
2836 {
2837 char reason2[MAXLEN];
09a3057c 2838 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2839 DelChannelUser(chanserv, channel, reason2, 0);
d76ed9a9 2840 }
09a3057c 2841
d76ed9a9 2842 UnlockChannel(channel);
2843 LockChannel(target);
09a3057c 2844 global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "CSMSG_CHANNEL_MOVED",
2845 channel->name, target->name, user->handle_info->handle);
2846
63c95a47 2847 reply("CSMSG_MOVE_SUCCESS", target->name);
d76ed9a9 2848 return 1;
2849}
2850
2851static void
2852merge_users(struct chanData *source, struct chanData *target)
2853{
2854 struct userData *suData, *tuData, *next;
2855 dict_iterator_t it;
2856 dict_t merge;
2857
2858 merge = dict_new();
2859
2860 /* Insert the source's users into the scratch area. */
2861 for(suData = source->users; suData; suData = suData->next)
c52639b4 2862 dict_insert(merge, suData->handle->handle, suData);
d76ed9a9 2863
2864 /* Iterate through the target's users, looking for
2865 users common to both channels. The lower access is
2866 removed from either the scratch area or target user
2867 list. */
2868 for(tuData = target->users; tuData; tuData = next)
2869 {
c52639b4 2870 struct userData *choice;
d76ed9a9 2871
c52639b4 2872 next = tuData->next;
d76ed9a9 2873
c52639b4
AS
2874 /* If a source user exists with the same handle as a target
2875 channel's user, resolve the conflict by removing one. */
2876 suData = dict_find(merge, tuData->handle->handle, NULL);
2877 if(!suData)
2878 continue;
d76ed9a9 2879
c52639b4
AS
2880 /* Pick the data we want to keep. */
2881 /* If the access is the same, use the later seen time. */
2882 if(suData->access == tuData->access)
2883 choice = (suData->seen > tuData->seen) ? suData : tuData;
2884 else /* Otherwise, keep the higher access level. */
2885 choice = (suData->access > tuData->access) ? suData : tuData;
d76ed9a9 2886
c52639b4
AS
2887 /* Remove the user that wasn't picked. */
2888 if(choice == tuData)
2889 {
2890 dict_remove(merge, suData->handle->handle);
2891 del_channel_user(suData, 0);
2892 }
2893 else
2894 del_channel_user(tuData, 0);
d76ed9a9 2895 }
2896
2897 /* Move the remaining users to the target channel. */
2898 for(it = dict_first(merge); it; it = iter_next(it))
2899 {
c52639b4 2900 suData = iter_data(it);
d76ed9a9 2901
c52639b4
AS
2902 /* Insert the user into the target channel's linked list. */
2903 suData->prev = NULL;
2904 suData->next = target->users;
d76ed9a9 2905 suData->channel = target;
2906
c52639b4
AS
2907 if(target->users)
2908 target->users->prev = suData;
2909 target->users = suData;
d76ed9a9 2910
c52639b4
AS
2911 /* Update the user counts for the target channel; the
2912 source counts are left alone. */
2913 target->userCount++;
d76ed9a9 2914 }
2915
2916 /* Possible to assert (source->users == NULL) here. */
2917 source->users = NULL;
2918 dict_delete(merge);
2919}
2920
2921static void
2922merge_bans(struct chanData *source, struct chanData *target)
2923{
2924 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2925
2926 /* Hold on to the original head of the target ban list
2927 to avoid comparing source bans with source bans. */
2928 tFront = target->bans;
2929
2930 /* Perform a totally expensive O(n*m) merge, ick. */
2931 for(sbData = source->bans; sbData; sbData = sNext)
2932 {
c52639b4
AS
2933 /* Flag to track whether the ban's been moved
2934 to the destination yet. */
2935 int moved = 0;
2936
2937 /* Possible to assert (sbData->prev == NULL) here. */
2938 sNext = sbData->next;
2939
2940 for(tbData = tFront; tbData; tbData = tNext)
2941 {
2942 tNext = tbData->next;
2943
2944 /* Perform two comparisons between each source
2945 and target ban, conflicts are resolved by
2946 keeping the broader ban and copying the later
2947 expiration and triggered time. */
2948 if(match_ircglobs(tbData->mask, sbData->mask))
2949 {
2950 /* There is a broader ban in the target channel that
2951 overrides one in the source channel; remove the
2952 source ban and break. */
2953 if(sbData->expires > tbData->expires)
2954 tbData->expires = sbData->expires;
2955 if(sbData->triggered > tbData->triggered)
2956 tbData->triggered = sbData->triggered;
2957 del_channel_ban(sbData);
2958 break;
2959 }
2960 else if(match_ircglobs(sbData->mask, tbData->mask))
2961 {
2962 /* There is a broader ban in the source channel that
2963 overrides one in the target channel; remove the
2964 target ban, fall through and move the source over. */
2965 if(tbData->expires > sbData->expires)
2966 sbData->expires = tbData->expires;
2967 if(tbData->triggered > sbData->triggered)
2968 sbData->triggered = tbData->triggered;
2969 if(tbData == tFront)
2970 tFront = tNext;
2971 del_channel_ban(tbData);
2972 }
2973
2974 /* Source bans can override multiple target bans, so
2975 we allow a source to run through this loop multiple
2976 times, but we can only move it once. */
2977 if(moved)
2978 continue;
2979 moved = 1;
2980
2981 /* Remove the source ban from the source ban list. */
2982 if(sbData->next)
2983 sbData->next->prev = sbData->prev;
2984
2985 /* Modify the source ban's associated channel. */
2986 sbData->channel = target;
2987
2988 /* Insert the ban into the target channel's linked list. */
2989 sbData->prev = NULL;
2990 sbData->next = target->bans;
2991
2992 if(target->bans)
2993 target->bans->prev = sbData;
2994 target->bans = sbData;
2995
2996 /* Update the user counts for the target channel. */
2997 target->banCount++;
2998 }
d76ed9a9 2999 }
3000
3001 /* Possible to assert (source->bans == NULL) here. */
3002 source->bans = NULL;
3003}
3004
3005static void
3006merge_data(struct chanData *source, struct chanData *target)
3007{
697f4c9a 3008 /* Use more recent visited and owner-transfer time; use older
3009 * registered time. Bitwise or may_opchan. Use higher max.
3010 * Do not touch last_refresh, ban count or user counts.
3011 */
d76ed9a9 3012 if(source->visited > target->visited)
c52639b4 3013 target->visited = source->visited;
697f4c9a 3014 if(source->registered < target->registered)
3015 target->registered = source->registered;
3016 if(source->ownerTransfer > target->ownerTransfer)
3017 target->ownerTransfer = source->ownerTransfer;
3018 if(source->may_opchan)
3019 target->may_opchan = 1;
3020 if(source->max > target->max)
3021 target->max = source->max;
d76ed9a9 3022}
3023
3024static void
3025merge_channel(struct chanData *source, struct chanData *target)
3026{
3027 merge_users(source, target);
3028 merge_bans(source, target);
3029 merge_data(source, target);
3030}
3031
3032static CHANSERV_FUNC(cmd_merge)
3033{
3034 struct userData *target_user;
3035 struct chanNode *target;
3036 char reason[MAXLEN];
b45fd7d7 3037 int nodelete = 0;
d76ed9a9 3038
3039 REQUIRE_PARAMS(2);
3040
3041 /* Make sure the target channel exists and is registered to the user
3042 performing the command. */
3043 if(!(target = GetChannel(argv[1])))
3044 {
3045 reply("MSG_INVALID_CHANNEL");
3046 return 0;
3047 }
3048
b45fd7d7 3049 if (argc > 2) {
c52639b4
AS
3050 if (!irccasecmp("nodelete", argv[2]))
3051 nodelete = 1;
b45fd7d7 3052 }
3053
d76ed9a9 3054 if(!target->channel_info)
3055 {
3056 reply("CSMSG_NOT_REGISTERED", target->name);
3057 return 0;
3058 }
3059
3060 if(IsProtected(channel->channel_info))
3061 {
3062 reply("CSMSG_MERGE_NODELETE");
3063 return 0;
3064 }
3065
3066 if(IsSuspended(target->channel_info))
3067 {
3068 reply("CSMSG_MERGE_SUSPENDED");
3069 return 0;
3070 }
3071
3072 if(channel == target)
3073 {
3074 reply("CSMSG_MERGE_SELF");
3075 return 0;
3076 }
3077
3078 target_user = GetChannelUser(target->channel_info, user->handle_info);
3079 if(!target_user || (target_user->access < UL_OWNER))
3080 {
3081 reply("CSMSG_MERGE_NOT_OWNER");
3082 return 0;
3083 }
3084
3085 /* Merge the channel structures and associated data. */
3086 merge_channel(channel->channel_info, target->channel_info);
63c95a47 3087 spamserv_cs_move_merge(user, channel, target, 0);
d76ed9a9 3088 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
b45fd7d7 3089 if (!nodelete)
c52639b4 3090 unregister_channel(channel->channel_info, reason);
d76ed9a9 3091 reply("CSMSG_MERGE_SUCCESS", target->name);
3092 return 1;
3093}
3094
3095static CHANSERV_FUNC(cmd_opchan)
3096{
3097 struct mod_chanmode change;
3098 if(!IsHelping(user) && !channel->channel_info->may_opchan)
3099 {
3100 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
3101 return 0;
3102 }
6ff42e24 3103 if(!IsInChannel(channel,chanserv)) {
3104 reply("CSMSG_NOT_IN_CHANNEL", channel->name);
3105 return 0;
3106 }
d76ed9a9 3107 channel->channel_info->may_opchan = 0;
3108 mod_chanmode_init(&change);
3109 change.argc = 1;
3110 change.args[0].mode = MODE_CHANOP;
a32da4c7 3111 change.args[0].u.member = GetUserMode(channel, chanserv);
1136f709 3112 if(!change.args[0].u.member)
3113 {
3114 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
3115 return 0;
3116 }
d76ed9a9 3117 mod_chanmode_announce(chanserv, channel, &change);
3118 reply("CSMSG_OPCHAN_DONE", channel->name);
3119 return 1;
3120}
3121
3122static CHANSERV_FUNC(cmd_adduser)
3123{
3124 struct userData *actee;
1136f709 3125 struct userData *actor, *real_actor;
3126 struct handle_info *handle = NULL;
3127 struct adduserPending *tmp;
3128 unsigned short access_level, override = 0;
d76ed9a9 3129
3130 REQUIRE_PARAMS(3);
3131
3132 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
3133 {
c52639b4
AS
3134 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
3135 return 0;
d76ed9a9 3136 }
3137
1136f709 3138 access_level = user_level_from_name(argv[2], UL_OWNER);
3139 if(!access_level)
d76ed9a9 3140 {
c52639b4
AS
3141 reply("CSMSG_INVALID_ACCESS", argv[2]);
3142 return 0;
d76ed9a9 3143 }
3144
3145 actor = GetChannelUser(channel->channel_info, user->handle_info);
1136f709 3146 real_actor = GetTrueChannelAccess(channel->channel_info, user->handle_info);
3147
3148 if(actor->access <= access_level)
d76ed9a9 3149 {
c52639b4
AS
3150 reply("CSMSG_NO_BUMP_ACCESS");
3151 return 0;
d76ed9a9 3152 }
3153
1136f709 3154 /* Trying to add someone with equal/more access */
3155 if (!real_actor || real_actor->access <= access_level)
3156 override = CMD_LOG_OVERRIDE;
3157
463e23c9 3158 if(!(handle = modcmd_get_handle_info(user, argv[1])))
ac3bdc8d 3159 {
1136f709 3160 /* 'kevin must first authenticate with AuthServ'. is sent to user */
ac3bdc8d 3161 struct userNode *unode;
3162 unode = GetUserH(argv[1]); /* find user struct by nick */
3163 if(unode)
3164 {
3165 if(find_adduser_pending(channel, unode)) {
3166 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel->name);
3167 }
3168 else {
3169 if(IsInChannel(channel, unode)) {
b208a99e 3170 reply("CSMSG_ADDUSER_PENDING", unode->nick);
1136f709 3171 tmp = add_adduser_pending(channel, unode, access_level);
ac3bdc8d 3172 send_message_type(1,unode, chanserv, "CSMSG_ADDUSER_PENDING_TARGET", user->nick, channel->name);
3173 }
3174 /* this results in user must auth AND not in chan errors. too confusing..
3175 else {
3176 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
3177 }
3178 */
3179 }
3180 }
d76ed9a9 3181 return 0;
ac3bdc8d 3182 }
d76ed9a9 3183
3184 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
3185 {
c52639b4
AS
3186 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, user_level_name_from_level(actee->access));
3187 return 0;
d76ed9a9 3188 }
3189
dfaa28a4 3190 time_t accessexpiry = 0;
fd20b142 3191 unsigned int duration = 0;
3192 if (argc > 3) {
3193 if ((duration = ParseInterval(argv[3])))
dfaa28a4 3194 accessexpiry = now + duration;
fd20b142 3195 }
3196
1136f709 3197 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL, accessexpiry);
d76ed9a9 3198 scan_user_presence(actee, NULL);
fd20b142 3199
3200 if (duration > 0)
dfaa28a4 3201 timeq_add(accessexpiry, chanserv_expire_tempuser, actee);
fd20b142 3202
463e23c9 3203 reply("CSMSG_ADDED_USER", handle->handle, channel->name, user_level_name_from_level(access_level), access_level);
1136f709 3204 return 1 | override;
d76ed9a9 3205}
3206
3207static CHANSERV_FUNC(cmd_clvl)
3208{
3209 struct handle_info *handle;
3210 struct userData *victim;
1136f709 3211 struct userData *actor, *real_actor;
3212 unsigned short new_access, override = 0;
d76ed9a9 3213 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3214
3215 REQUIRE_PARAMS(3);
3216
3217 actor = GetChannelUser(channel->channel_info, user->handle_info);
1136f709 3218 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
d76ed9a9 3219
3220 if(!(handle = modcmd_get_handle_info(user, argv[1])))
3221 return 0;
3222
3223 if(handle == user->handle_info && !privileged)
3224 {
c52639b4
AS
3225 reply("CSMSG_NO_SELF_CLVL");
3226 return 0;
d76ed9a9 3227 }
3228
3229 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
3230 {
c52639b4
AS
3231 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
3232 return 0;
d76ed9a9 3233 }
3234
3235 if(actor->access <= victim->access && !privileged)
3236 {
c52639b4
AS
3237 reply("MSG_USER_OUTRANKED", handle->handle);
3238 return 0;
d76ed9a9 3239 }
3240
3241 new_access = user_level_from_name(argv[2], UL_OWNER);
3242
3243 if(!new_access)
3244 {
c52639b4
AS
3245 reply("CSMSG_INVALID_ACCESS", argv[2]);
3246 return 0;
d76ed9a9 3247 }
3248
3249 if(new_access >= actor->access && !privileged)
3250 {
c52639b4
AS
3251 reply("CSMSG_NO_BUMP_ACCESS");
3252 return 0;
d76ed9a9 3253 }
3254
dfaa28a4 3255 time_t clvlexpiry = 0;
3256 unsigned int duration = 0;
3257 if (argc > 3) {
3258 if ((duration = ParseInterval(argv[3])))
3259 clvlexpiry = now + duration;
3260 }
3261
3262 if (duration > 0) {
3263 if (victim->accessexpiry > 0) {
3264 reply("CSMSG_NO_BUMP_EXPIRY");
3265 return 0;
3266 }
3267
3268 victim->clvlexpiry = clvlexpiry;
3269 victim->lastaccess = victim->access;
3270 timeq_add(clvlexpiry, chanserv_expire_tempclvl, victim);
3271 }
3272
1136f709 3273 /* Trying to clvl a equal/higher user */
3274 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
3275 override = CMD_LOG_OVERRIDE;
3276 /* Trying to clvl someone to equal/higher access */
3277 if(!real_actor || new_access >= real_actor->access)
3278 override = CMD_LOG_OVERRIDE;
3279 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
3280 * If they lower their own access it's not a big problem.
3281 */
d76ed9a9 3282 victim->access = new_access;
de9510bc 3283 reply("CSMSG_CHANGED_ACCESS", handle->handle, user_level_name_from_level(new_access), new_access, channel->name);
1136f709 3284 return 1 | override;
d76ed9a9 3285}
3286
3287static CHANSERV_FUNC(cmd_deluser)
3288{
3289 struct handle_info *handle;
3290 struct userData *victim;
1136f709 3291 struct userData *actor, *real_actor;
3292 unsigned short access_level, override = 0;
7d5ebc21 3293 unsigned short access_level_user = 0;
d76ed9a9 3294 char *chan_name;
3295
3296 REQUIRE_PARAMS(2);
3297
3298 actor = GetChannelUser(channel->channel_info, user->handle_info);
1136f709 3299 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
3300
d76ed9a9 3301 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
3302 return 0;
3303
3304 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
3305 {
c52639b4
AS
3306 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
3307 return 0;
d76ed9a9 3308 }
3309
3310 if(argc > 2)
3311 {
1136f709 3312 access_level = user_level_from_name(argv[1], UL_OWNER);
7b0150f8 3313 char *useraccess = user_level_name_from_level(victim->access);
7d5ebc21 3314 access_level_user = user_level_from_name(useraccess, UL_OWNER);
1136f709 3315 if(!access_level)
d76ed9a9 3316 {
3317 reply("CSMSG_INVALID_ACCESS", argv[1]);
3318 return 0;
3319 }
c52639b4
AS
3320 if(access_level != access_level_user)
3321 {
3322 reply("CSMSG_INCORRECT_ACCESS", handle->handle, useraccess, argv[1]);
3323 return 0;
3324 }
d76ed9a9 3325 }
3326 else
3327 {
1136f709 3328 access_level = victim->access;
d76ed9a9 3329 }
3330
3331 if((actor->access <= victim->access) && !IsHelping(user))
3332 {
c52639b4
AS
3333 reply("MSG_USER_OUTRANKED", victim->handle->handle);
3334 return 0;
d76ed9a9 3335 }
3336
1136f709 3337 /* If people delete themselves it is an override, but they could've used deleteme so we don't log it as an override */
3338 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
3339 override = CMD_LOG_OVERRIDE;
3340
d76ed9a9 3341 chan_name = strdup(channel->name);
3342 del_channel_user(victim, 1);
1136f709 3343 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
d76ed9a9 3344 free(chan_name);
1136f709 3345 return 1 | override;
d76ed9a9 3346}
3347
3348static int
3349cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
3350{
1136f709 3351 struct userData *actor, *real_actor, *uData, *next;
3352 unsigned int override = 0;
d76ed9a9 3353
3354 actor = GetChannelUser(channel->channel_info, user->handle_info);
1136f709 3355 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
3356
d76ed9a9 3357 if(min_access > max_access)
3358 {
3359 reply("CSMSG_BAD_RANGE", min_access, max_access);
3360 return 0;
3361 }
3362
3363 if((actor->access <= max_access) && !IsHelping(user))
3364 {
c52639b4
AS
3365 reply("CSMSG_NO_ACCESS");
3366 return 0;
d76ed9a9 3367 }
3368
1136f709 3369 if(!real_actor || real_actor->access <= max_access)
3370 override = CMD_LOG_OVERRIDE;
3371
d76ed9a9 3372 for(uData = channel->channel_info->users; uData; uData = next)
3373 {
c52639b4 3374 next = uData->next;
d76ed9a9 3375
c52639b4
AS
3376 if((uData->access >= min_access)
3377 && (uData->access <= max_access)
3378 && match_ircglob(uData->handle->handle, mask))
3379 {
3380 del_channel_user(uData, 1);
3381 }
d76ed9a9 3382 }
3383
3384 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
1136f709 3385 return 1 | override;
d76ed9a9 3386}
3387
3388static CHANSERV_FUNC(cmd_mdelowner)
3389{
3390 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
3391}
3392
3393static CHANSERV_FUNC(cmd_mdelcoowner)
3394{
11d3c79b 3395 return cmd_mdel_user(user, channel, UL_COOWNER, UL_OWNER-1, argv[1], cmd);
d76ed9a9 3396}
3397
4048352e 3398static CHANSERV_FUNC(cmd_mdelmanager)
d76ed9a9 3399{
11d3c79b 3400 return cmd_mdel_user(user, channel, UL_MANAGER, UL_COOWNER-1, argv[1], cmd);
d76ed9a9 3401}
3402
3403static CHANSERV_FUNC(cmd_mdelop)
3404{
11d3c79b 3405 return cmd_mdel_user(user, channel, UL_OP, UL_MANAGER-1, argv[1], cmd);
d76ed9a9 3406}
3407
11d3c79b 3408static CHANSERV_FUNC(cmd_mdelhalfop)
d76ed9a9 3409{
11d3c79b 3410 return cmd_mdel_user(user, channel, UL_HALFOP, UL_OP-1, argv[1], cmd);
d76ed9a9 3411}
3412
11d3c79b 3413static CHANSERV_FUNC(cmd_mdelpeon)
55342ce8 3414{
11d3c79b 3415 return cmd_mdel_user(user, channel, UL_PEON, UL_HALFOP-1, argv[1], cmd);
55342ce8 3416}
3417
2a4ca4f5 3418static CHANSERV_FUNC(cmd_mdelpal)
3419{
3420 return cmd_mdel_user(user, channel, UL_PEON, UL_HALFOP-1, argv[1], cmd);
3421}
55342ce8 3422
c9bf23fe 3423static CHANSERV_FUNC(cmd_levels)
3424{
3425 struct helpfile_table tbl;
3426 int ii = 0;
3427
3428 tbl.length = 6 + 1; // 6 levels
3429 tbl.width = 4;
3430 tbl.flags = 0;
3431 tbl.contents = calloc(tbl.length,sizeof(tbl.contents[0]));
3432 tbl.contents[0] = calloc(tbl.width,sizeof(tbl.contents[0][0]));
3433 tbl.contents[0][0] = "Level";
3434 tbl.contents[0][1] = "From";
3435 tbl.contents[0][2] = "-";
3436 tbl.contents[0][3] = "To";
3437
3438 tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
3439 tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_OWNER));
3440 tbl.contents[ii][1] = msnprintf(4, "%d", UL_OWNER);
7a278540 3441 tbl.contents[ii][2] = msnprintf(2, " ");
c9bf23fe 3442 tbl.contents[ii][3] = msnprintf(1, "");
3443
3444 tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
3445 tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_COOWNER));
3446 tbl.contents[ii][1] = msnprintf(4, "%d", UL_COOWNER);
7a278540 3447 tbl.contents[ii][2] = msnprintf(2, "-");
c9bf23fe 3448 tbl.contents[ii][3] = msnprintf(4, "%d", UL_OWNER-1);
3449
3450 tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
3451 tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_MANAGER));
3452 tbl.contents[ii][1] = msnprintf(4, "%d", UL_MANAGER);
7a278540 3453 tbl.contents[ii][2] = msnprintf(2, "-");
c9bf23fe 3454 tbl.contents[ii][3] = msnprintf(4, "%d", UL_COOWNER-1);
3455
3456 tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
3457 tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_OP));
3458 tbl.contents[ii][1] = msnprintf(4, "%d", UL_OP);
7a278540 3459 tbl.contents[ii][2] = msnprintf(2, "-");
c9bf23fe 3460 tbl.contents[ii][3] = msnprintf(4, "%d", UL_MANAGER-1);
3461
3462 tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
3463 tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_HALFOP));
3464 tbl.contents[ii][1] = msnprintf(4, "%d", UL_HALFOP);
7a278540 3465 tbl.contents[ii][2] = msnprintf(2, "-");
c9bf23fe 3466 tbl.contents[ii][3] = msnprintf(4, "%d", UL_OP-1);
3467
3468 tbl.contents[++ii] = calloc(tbl.width, sizeof(tbl.contents[0][0]));
3469 tbl.contents[ii][0] = strdup(user_level_name_from_level(UL_PEON));
3470 tbl.contents[ii][1] = msnprintf(4, "%d", UL_PEON);
7a278540 3471 tbl.contents[ii][2] = msnprintf(2, "-");
c9bf23fe 3472 tbl.contents[ii][3] = msnprintf(4, "%d", UL_HALFOP-1);
3473
3474 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3475 return 0;
3476
3477/*
3478 reply("CSMSG_LEVELS_HEADER");
3479 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OWNER), UL_OWNER, UL_OWNER);
3480 reply("CSMSG_LEVELS", user_level_name_from_level(UL_COOWNER), UL_COOWNER, UL_OWNER-1);
3481 reply("CSMSG_LEVELS", user_level_name_from_level(UL_MANAGER), UL_MANAGER, UL_COOWNER-1);
3482 reply("CSMSG_LEVELS", user_level_name_from_level(UL_OP), UL_OP, UL_MANAGER-1);
3483 reply("CSMSG_LEVELS", user_level_name_from_level(UL_HALFOP), UL_HALFOP, UL_OP-1);
3484 reply("CSMSG_LEVELS", user_level_name_from_level(UL_PEON), UL_PEON, UL_HALFOP-1);
3485 reply("CSMSG_BAR");
3486 */
3487}
3488
c8273589 3489/* trim_lamers.. */
d76ed9a9 3490static int
c092fcad 3491cmd_trim_bans(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, unsigned long duration)
d76ed9a9 3492{
3493 struct banData *bData, *next;
3494 char interval[INTERVALLEN];
3495 unsigned int count;
3496 time_t limit;
3497
3498 count = 0;
3499 limit = now - duration;
3500 for(bData = channel->channel_info->bans; bData; bData = next)
3501 {
c52639b4 3502 next = bData->next;
d76ed9a9 3503
3504 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
3505 continue;
3506
3507 del_channel_ban(bData);
3508 count++;
3509 }
3510
3511 intervalString(interval, duration, user->handle_info);
c092fcad 3512 reply("CSMSG_TRIMMED_LAMERS", count, channel->name, interval);
d76ed9a9 3513 return 1;
3514}
3515
3516static int
c092fcad 3517cmd_trim_users(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
d76ed9a9 3518{
3519 struct userData *actor, *uData, *next;
3520 char interval[INTERVALLEN];
3521 unsigned int count;
3522 time_t limit;
3523
1136f709 3524 actor = GetChannelAccess(channel->channel_info, user->handle_info);
d76ed9a9 3525 if(min_access > max_access)
3526 {
c092fcad 3527 reply("CSMSG_BAD_RANGE", min_access, max_access);
d76ed9a9 3528 return 0;
3529 }
3530
1136f709 3531 if(!actor || actor->access <= max_access)
d76ed9a9 3532 {
c52639b4
AS
3533 reply("CSMSG_NO_ACCESS");
3534 return 0;
d76ed9a9 3535 }
3536
3537 count = 0;
3538 limit = now - duration;
3539 for(uData = channel->channel_info->users; uData; uData = next)
3540 {
c52639b4 3541 next = uData->next;
d76ed9a9 3542
c52639b4 3543 if((uData->seen > limit)
2f61d1d7 3544 || uData->present
3545 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
c52639b4 3546 continue;
d76ed9a9 3547
c52639b4
AS
3548 if(((uData->access >= min_access) && (uData->access <= max_access))
3549 || (!max_access && (uData->access < actor->access)))
3550 {
3551 del_channel_user(uData, 1);
3552 count++;
3553 }
d76ed9a9 3554 }
3555
3556 if(!max_access)
3557 {
3558 min_access = 1;
a32da4c7 3559 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
d76ed9a9 3560 }
c092fcad 3561 reply("CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
d76ed9a9 3562 return 1;
3563}
3564
3565static CHANSERV_FUNC(cmd_trim)
3566{
3567 unsigned long duration;
3568 unsigned short min_level, max_level;
2f61d1d7 3569 int vacation;
d76ed9a9 3570
3571 REQUIRE_PARAMS(3);
3572
2f61d1d7 3573 vacation = argc > 3 && !strcmp(argv[3], "vacation");
d76ed9a9 3574 duration = ParseInterval(argv[2]);
3575 if(duration < 60)
3576 {
c52639b4
AS
3577 reply("CSMSG_CANNOT_TRIM");
3578 return 0;
d76ed9a9 3579 }
3580
c8273589 3581 if(!irccasecmp(argv[1], "lamers"))
d76ed9a9 3582 {
c52639b4
AS
3583 cmd_trim_bans(cmd, user, channel, duration); /* trim_lamers.. */
3584 return 1;
d76ed9a9 3585 }
3586 else if(!irccasecmp(argv[1], "users"))
3587 {
c52639b4
AS
3588 cmd_trim_users(cmd, user, channel, 0, 0, duration, vacation);
3589 return 1;
d76ed9a9 3590 }
3591 else if(parse_level_range(&min_level, &max_level, argv[1]))
3592 {
c52639b4
AS
3593 cmd_trim_users(cmd, user, channel, min_level, max_level, duration, vacation);
3594 return 1;
d76ed9a9 3595 }
3596 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3597 {
c52639b4
AS
3598 cmd_trim_users(cmd, user, channel, min_level, min_level, duration, vacation);
3599 return 1;
d76ed9a9 3600 }
3601 else
3602 {
3603 reply("CSMSG_INVALID_TRIM", argv[1]);
3604 return 0;
3605 }
3606}
3607
3608/* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3609 to the user. cmd_all takes advantage of this. */
3610static CHANSERV_FUNC(cmd_up)
3611{
3612 struct mod_chanmode change;
3613 struct userData *uData;
3614 const char *errmsg;
3615
3616 mod_chanmode_init(&change);
3617 change.argc = 1;
a32da4c7 3618 change.args[0].u.member = GetUserMode(channel, user);
3619 if(!change.args[0].u.member)
d76ed9a9 3620 {
3621 if(argc)
3622 reply("MSG_CHANNEL_ABSENT", channel->name);
c52639b4 3623 return 0;
d76ed9a9 3624 }
3625
3626 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3627 if(!uData)
3628 {
3629 if(argc)
3630 reply("CSMSG_GODMODE_UP", argv[0]);
3631 return 0;
3632 }
4b6129c0 3633 else if(uData->access >= UL_OP)
d76ed9a9 3634 {
3635 change.args[0].mode = MODE_CHANOP;
3636 errmsg = "CSMSG_ALREADY_OPPED";
3637 }
4b6129c0 3638 else if(uData->access >= UL_HALFOP )
55342ce8 3639 {
3640 change.args[0].mode = MODE_HALFOP;
3641 errmsg = "CSMSG_ALREADY_HALFOPPED";
3642 }
4b6129c0 3643 else if(uData->access >= UL_PEON && (channel->channel_info->chOpts[chAutomode] != 'm' ))
d76ed9a9 3644 {
3645 change.args[0].mode = MODE_VOICE;
3646 errmsg = "CSMSG_ALREADY_VOICED";
3647 }
3648 else
3649 {
3650 if(argc)
3651 reply("CSMSG_NO_ACCESS");
3652 return 0;
3653 }
a32da4c7 3654 change.args[0].mode &= ~change.args[0].u.member->modes;
d76ed9a9 3655 if(!change.args[0].mode)
3656 {
3657 if(argc)
3658 reply(errmsg, channel->name);
3659 return 0;
3660 }
3661 modcmd_chanmode_announce(&change);
3662 return 1;
3663}
3664
3665static CHANSERV_FUNC(cmd_down)
3666{
3667 struct mod_chanmode change;
3668
3669 mod_chanmode_init(&change);
3670 change.argc = 1;
a32da4c7 3671 change.args[0].u.member = GetUserMode(channel, user);
3672 if(!change.args[0].u.member)
d76ed9a9 3673 {
3674 if(argc)
3675 reply("MSG_CHANNEL_ABSENT", channel->name);
c52639b4 3676 return 0;
d76ed9a9 3677 }
3678
a32da4c7 3679 if(!change.args[0].u.member->modes)
d76ed9a9 3680 {
3681 if(argc)
3682 reply("CSMSG_ALREADY_DOWN", channel->name);
c52639b4 3683 return 0;
d76ed9a9 3684 }
3685
a32da4c7 3686 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
d76ed9a9 3687 modcmd_chanmode_announce(&change);
3688 return 1;
3689}
3690
3691static 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)
3692{
3693 struct userData *cList;
3694
3695 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3696 {
c52639b4
AS
3697 if(IsSuspended(cList->channel)
3698 || IsUserSuspended(cList)
3699 || !GetUserMode(cList->channel->channel, user))
3700 continue;
d76ed9a9 3701
c52639b4 3702 mcmd(user, cList->channel->channel, 0, NULL, cmd);
d76ed9a9 3703 }
3704
3705 return 1;
3706}
3707
3708static CHANSERV_FUNC(cmd_upall)
3709{
3710 return cmd_all(CSFUNC_ARGS, cmd_up);
3711}
3712
3713static CHANSERV_FUNC(cmd_downall)
3714{
3715 return cmd_all(CSFUNC_ARGS, cmd_down);
3716}
3717
c092fcad 3718typedef int validate_func_t(struct svccmd *cmd, struct userNode *user, struct chanNode *channel, struct userNode *victim);
d76ed9a9 3719typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3720
3721static int
3722modify_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)
3723{
3724 unsigned int ii, valid;
3725 struct userNode *victim;
3726 struct mod_chanmode *change;
3727
3728 change = mod_chanmode_alloc(argc - 1);
3729
3730 for(ii=valid=0; ++ii < argc; )
3731 {
c52639b4 3732 if(!(victim = GetUserH(argv[ii])))
d76ed9a9 3733 continue;
3734 change->args[valid].mode = mode;
a32da4c7 3735 change->args[valid].u.member = GetUserMode(channel, victim);
3736 if(!change->args[valid].u.member)
d76ed9a9 3737 continue;
c092fcad 3738 if(validate && !validate(cmd, user, channel, victim))
c52639b4 3739 continue;
d76ed9a9 3740 valid++;
3741 }
3742
3743 change->argc = valid;
3744 if(valid < (argc-1))
c52639b4 3745 reply("CSMSG_PROCESS_FAILED");
d76ed9a9 3746 if(valid)
3747 {
3748 modcmd_chanmode_announce(change);
3749 reply(action, channel->name);
3750 }
3751 mod_chanmode_free(change);
3752 return 1;
3753}
3754
3755static CHANSERV_FUNC(cmd_op)
3756{
3757 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3758}
3759
55342ce8 3760static CHANSERV_FUNC(cmd_hop)
3761{
3762 return modify_users(CSFUNC_ARGS, validate_halfop, MODE_HALFOP, "CSMSG_HALFOPPED_USERS");
3763}
3764
d76ed9a9 3765static CHANSERV_FUNC(cmd_deop)
3766{
3767 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3768}
3769
55342ce8 3770static CHANSERV_FUNC(cmd_dehop)
3771{
3772 return modify_users(CSFUNC_ARGS, validate_dehop, MODE_REMOVE|MODE_HALFOP, "CSMSG_DEHALFOPPED_USERS");
3773}
3774
d76ed9a9 3775static CHANSERV_FUNC(cmd_voice)
3776{
3777 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3778}
3779
3780static CHANSERV_FUNC(cmd_devoice)
3781{
3782 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3783}
3784
3785static int
697f4c9a 3786bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
d76ed9a9 3787{
3788 unsigned int ii;
3b7fa78b 3789 int b = 0;
d76ed9a9 3790
3791 if(victimCount)
3792 *victimCount = 0;
3793 for(ii=0; ii<channel->members.used; ii++)
3794 {
3795 struct modeNode *mn = channel->members.list[ii];
3796
3797 if(IsService(mn->user))
3798 continue;
3799
3b7fa78b 3800 b = user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE, 0);
3801 if (b == -1)
3802 return -1;
3803 else if (b == 0)
d76ed9a9 3804 continue;
3805
ff5f1ab2 3806 if(protect_user(mn->user, user, channel->channel_info, false))
d76ed9a9 3807 return 1;
3808
3809 if(victims)
3810 victims[(*victimCount)++] = mn;
3811 }
3812 return 0;
3813}
3814
be1ad341
AS
3815int is_extban(char *b) {
3816 if(*b == '~') {
3817 return 1;
3818 }
3819 return 0;
3820}
3821
d76ed9a9 3822static int
3823eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3824{
3825 struct userNode *victim;
3826 struct modeNode **victims;
3827 unsigned int offset, n, victimCount, duration = 0;
3b7fa78b 3828 int b = 0;
d3347099 3829 char *reason = "Bye.", *ban, *name;
d76ed9a9 3830 char interval[INTERVALLEN];
3831
c8273589 3832 offset = (action & ACTION_ADD_TIMED_LAMER) ? 3 : 2;
d76ed9a9 3833 REQUIRE_PARAMS(offset);
3834 if(argc > offset)
3835 {
c52639b4 3836 reason = unsplit_string(argv + offset, argc - offset, NULL);
d76ed9a9 3837 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3838 {
3839 /* Truncate the reason to a length of TOPICLEN, as
3840 the ircd does; however, leave room for an ellipsis
3841 and the kicker's nick. */
3842 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3843 }
3844 }
3845
3846 if((victim = GetUserH(argv[1])))
3847 {
3848 victims = alloca(sizeof(victims[0]));
3849 victims[0] = GetUserMode(channel, victim);
3850 /* XXX: The comparison with ACTION_KICK is just because all
3851 * other actions can work on users outside the channel, and we
3852 * want to allow those (e.g. unbans) in that case. If we add
3853 * some other ejection action for in-channel users, change
3854 * this too. */
c52639b4 3855 victimCount = victims[0] ? 1 : 0;
d76ed9a9 3856
c52639b4
AS
3857 if(IsService(victim))
3858 {
3859 if(cmd)
3860 reply("MSG_SERVICE_IMMUNE", victim->nick);
3861 return 0;
3862 }
d76ed9a9 3863
3864 if((action == ACTION_KICK) && !victimCount)
3865 {
c52639b4
AS
3866 if(cmd)
3867 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
d76ed9a9 3868 return 0;
3869 }
3870
c52639b4
AS
3871 if(protect_user(victim, user, channel->channel_info, false))
3872 {
3873 // This translates to send_message(user, cmd->parent->bot, ...)
3874 // if user is x3 (ctcp action) cmd is null and segfault.
3875 if(cmd)
3876 reply("CSMSG_USER_PROTECTED", victim->nick);
3877 return 0;
3878 }
d76ed9a9 3879
c52639b4
AS
3880 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3881 name = victim->nick;
d76ed9a9 3882 }
1136f709 3883 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3884 {
3885 struct handle_info *hi;
3886 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3887 const char *accountname = argv[1] + 1;
3888
3889 if(!(hi = get_handle_info(accountname)))
3890 {
3891 reply("MSG_HANDLE_UNKNOWN", accountname);
3892 return 0;
3893 }
3894
3895 snprintf(banmask, sizeof(banmask), "*!*@%s.*", hi->handle);
3896 victims = alloca(sizeof(victims[0]) * channel->members.used);
3897
3b7fa78b 3898 b = bad_channel_ban(channel, user, banmask, &victimCount, victims);
3899 if(b == 1)
1136f709 3900 {
3901 reply("CSMSG_MASK_PROTECTED", banmask);
3902 return 0;
c52639b4
AS
3903 }
3904 else if(b == -1)
3b7fa78b 3905 {
3906 reply("CSMSG_BAD_BAN", banmask);
3907 return 0;
1136f709 3908 }
3909
3910 if((action == ACTION_KICK) && (victimCount == 0))
3911 {
3912 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3913 return 0;
3914 }
3915
3916 name = ban = strdup(banmask);
3917 }
d76ed9a9 3918 else
3919 {
c52639b4
AS
3920 if(!is_ircmask(argv[1]))
3921 {
3922 if(cmd)
3923 reply("MSG_NICK_UNKNOWN", argv[1]);
3924 return 0;
3925 }
d76ed9a9 3926
c52639b4 3927 victims = alloca(sizeof(victims[0]) * channel->members.used);
d76ed9a9 3928
3b7fa78b 3929 b = bad_channel_ban(channel, user, argv[1], &victimCount, victims);
3930 if(cmd && (b == 1)) {
3931 reply("CSMSG_MASK_PROTECTED", argv[1]);
c52639b4
AS
3932 return 0;
3933 }
3b7fa78b 3934 else if(cmd && (b == -1)) {
3935 reply("CSMSG_BAD_BAN", argv[1]);
c52639b4
AS
3936 return 0;
3937 }
d1a65675 3938/* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3939// if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3940 And, ^^^^^^^^^ BAH!
3941 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3942 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3943 some creativity, but its not x3's job to be the ban censor anyway. */
3944 if(is_overmask(argv[1]))
d76ed9a9 3945 {
c52639b4 3946 if(cmd)
35caf917 3947 reply("CSMSG_LAME_MASK", argv[1]);
d76ed9a9 3948 return 0;
3949 }
be1ad341
AS
3950 //TODO: We have no support to do protection etc etc so for now we dont let you use x3 to set extended bans.
3951 if(is_extban(argv[1]))
3952 {
3953 if(cmd)
3954 reply("CSMSG_NO_EXTBANS", argv[1]);
3955 return 0;
3956 }
d76ed9a9 3957
3958 if((action == ACTION_KICK) && (victimCount == 0))
3959 {
c52639b4 3960 if(cmd)
35caf917 3961 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
d76ed9a9 3962 return 0;
3963 }
3964
c52639b4 3965 name = ban = strdup(argv[1]);
d76ed9a9 3966 }
3967
3968 /* Truncate the ban in place if necessary; we must ensure
3969 that 'ban' is a valid ban mask before sanitizing it. */
8062bfc3 3970 if (*ban != '~')
3971 sanitize_ircmask(ban);
d76ed9a9 3972
c8273589 3973 if(action & ACTION_ADD_LAMER)
d76ed9a9 3974 {
c52639b4
AS
3975 struct banData *bData, *next;
3976
3977 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans) /* ..lamers.. */
3978 {
3979 if(cmd)
3980 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf.max_chan_bans); /* ..lamers.. */
3981 free(ban);
3982 return 0;
3983 }
3984
3985 if(action & ACTION_ADD_TIMED_LAMER)
3986 {
3987 duration = ParseInterval(argv[2]);
3988
3989 if(duration < 15)
3990 {
3991 if(cmd)
3992 reply("CSMSG_DURATION_TOO_LOW");
3993 free(ban);
3994 return 0;
3995 }
3996 else if(duration > (86400 * 365 * 2))
3997 {
3998 if(cmd)
3999 reply("CSMSG_DURATION_TOO_HIGH");
4000 free(ban);
4001 return 0;
4002 }
4003 }
d76ed9a9 4004
c8273589 4005 /* lamers... */
c52639b4
AS
4006 for(bData = channel->channel_info->bans; bData; bData = next)
4007 {
4008 if(match_ircglobs(bData->mask, ban))
4009 {
4010 int exact = !irccasecmp(bData->mask, ban);
4011
4012 /* The ban is redundant; there is already a ban
4013 with the same effect in place. */
4014 if(exact)
4015 {
4016 if(bData->reason)
d76ed9a9 4017 free(bData->reason);
c52639b4 4018 bData->reason = strdup(reason);
d76ed9a9 4019 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
4020 if(cmd)
4021 reply("CSMSG_REASON_CHANGE", ban);
c52639b4 4022 if(!bData->expires)
d76ed9a9 4023 goto post_add_ban;
c52639b4
AS
4024 }
4025 if(exact && bData->expires)
4026 {
4027 int reset = 0;
4028
4029 /* If the ban matches an existing one exactly,
4030 extend the expiration time if the provided
4031 duration is longer. */
4032 if(duration && ((time_t)(now + duration) > bData->expires))
4033 {
4034 bData->expires = now + duration;
4035 reset = 1;
4036 }
4037 else if(!duration)
4038 {
4039 bData->expires = 0;
4040 reset = 1;
4041 }
4042
4043 if(reset)
4044 {
4045 /* Delete the expiration timeq entry and
4046 requeue if necessary. */
4047 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
4048
4049 if(bData->expires)
4050 timeq_add(bData->expires, expire_ban, bData);
d76ed9a9 4051
4052 if(!cmd)
4053 {
c52639b4 4054 /* automated kickban, dont reply */
d76ed9a9 4055 }
c52639b4
AS
4056 else if(duration)
4057 reply("CSMSG_LAMER_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
4058 else
4059 reply("CSMSG_LAMER_ADDED", name, channel->name);
4060
4061 goto post_add_ban;
4062 }
4063 }
d76ed9a9 4064 if(cmd)
c8273589 4065 reply("CSMSG_REDUNDANT_LAMER", name, channel->name);
d76ed9a9 4066
c52639b4
AS
4067 free(ban);
4068 return 0;
4069 }
d76ed9a9 4070
c52639b4
AS
4071 next = bData->next;
4072 if(match_ircglobs(ban, bData->mask))
4073 {
4074 /* The ban we are adding makes previously existing
4075 bans redundant; silently remove them. */
4076 del_channel_ban(bData);
4077 }
4078 }
d76ed9a9 4079
c52639b4 4080 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);
d76ed9a9 4081 free(ban);
4082 name = ban = strdup(bData->mask);
4083 }
4084 else if(ban)
4085 {
19fe6b99 4086 /* WHAT DOES THIS DO?? -Rubin */
d76ed9a9 4087 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
4088 {
4089 extern const char *hidden_host_suffix;
4090 const char *old_name = chanserv_conf.old_ban_names->list[n];
4091 char *new_mask;
4092 unsigned int l1, l2;
4093
4094 l1 = strlen(ban);
4095 l2 = strlen(old_name);
4096 if(l2+2 > l1)
4097 continue;
4098 if(irccasecmp(ban + l1 - l2, old_name))
4099 continue;
4100 new_mask = malloc(MAXLEN);
a32da4c7 4101 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
d76ed9a9 4102 free(ban);
4103 name = ban = new_mask;
4104 }
4105 }
4106
c52639b4 4107 post_add_ban:
d76ed9a9 4108 if(action & ACTION_BAN)
4109 {
c52639b4 4110 unsigned int exists;
d76ed9a9 4111 struct mod_chanmode *change;
4112
c52639b4
AS
4113 if(channel->banlist.used >= MAXBANS)
4114 {
d76ed9a9 4115 if(cmd)
4116 reply("CSMSG_BANLIST_FULL", channel->name);
c52639b4
AS
4117 free(ban);
4118 return 0;
4119 }
d76ed9a9 4120
4121 exists = ChannelBanExists(channel, ban);
4122 change = mod_chanmode_alloc(victimCount + 1);
4123 for(n = 0; n < victimCount; ++n)
4124 {
55342ce8 4125 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_HALFOP|MODE_VOICE;
a32da4c7 4126 change->args[n].u.member = victims[n];
d76ed9a9 4127 }
4128 if(!exists)
4129 {
4130 change->args[n].mode = MODE_BAN;
a32da4c7 4131 change->args[n++].u.hostmask = ban;
d76ed9a9 4132 }
4133 change->argc = n;
4134 if(cmd)
4135 modcmd_chanmode_announce(change);
4136 else
4137 mod_chanmode_announce(chanserv, channel, change);
4138 mod_chanmode_free(change);
4139
4140 if(exists && (action == ACTION_BAN))
c52639b4 4141 {
d76ed9a9 4142 if(cmd)
4143 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
4144 free(ban);
4145 return 0;
4146 }
4147 }
4148
ff5f1ab2 4149 if(action & ACTION_ADD_LAMER)
4150 {
4151 char kick_reason[MAXLEN];
c52639b4 4152 sprintf(kick_reason, "(%s) %s", user->nick, reason);
ff5f1ab2 4153
c52639b4
AS
4154 for(n = 0; n < victimCount; n++)
4155 {
4156 if(!protect_user(victims[n]->user, user, channel->channel_info, true))
4157 {
4158 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
ff5f1ab2 4159 }
4160 }
4161 }
4162 else if(action & ACTION_KICK)
d76ed9a9 4163 {
4164 char kick_reason[MAXLEN];
c52639b4 4165 sprintf(kick_reason, "(%s) %s", user->nick, reason);
d76ed9a9 4166
c52639b4
AS
4167 for(n = 0; n < victimCount; n++)
4168 {
4169 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
ff5f1ab2 4170 }
d76ed9a9 4171 }
4172
4173 if(!cmd)
4174 {
4175 /* No response, since it was automated. */
4176 }
c8273589 4177 else if(action & ACTION_ADD_LAMER)
d76ed9a9 4178 {
c52639b4
AS
4179 if(duration)
4180 reply("CSMSG_TIMED_LAMER_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
4181 else
4182 reply("CSMSG_LAMER_ADDED", name, channel->name);
d76ed9a9 4183 }
4184 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
c52639b4 4185 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
d76ed9a9 4186 else if(action & ACTION_BAN)
c52639b4 4187 reply("CSMSG_BAN_DONE", name, channel->name);
d76ed9a9 4188 else if(action & ACTION_KICK && victimCount)
c52639b4 4189 reply("CSMSG_KICK_DONE", name, channel->name);
d76ed9a9 4190
4191 free(ban);
4192 return 1;
4193}
4194
4195static CHANSERV_FUNC(cmd_kickban)
4196{
4197 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
4198}
4199
4200static CHANSERV_FUNC(cmd_kick)
4201{
4202 return eject_user(CSFUNC_ARGS, ACTION_KICK);
4203}
4204
4205static CHANSERV_FUNC(cmd_ban)
4206{
4207 return eject_user(CSFUNC_ARGS, ACTION_BAN);
4208}
4209
c8273589 4210static CHANSERV_FUNC(cmd_addlamer)
d76ed9a9 4211{
c8273589 4212 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_LAMER);
d76ed9a9 4213}
4214
c8273589 4215static CHANSERV_FUNC(cmd_addtimedlamer)
d76ed9a9 4216{
c8273589 4217 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_LAMER | ACTION_ADD_TIMED_LAMER);
d76ed9a9 4218}
4219
4220static struct mod_chanmode *
4221find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
4222{
4223 struct mod_chanmode *change;
4224 unsigned char *match;
4225 unsigned int ii, count;
4226
4227 match = alloca(bans->used);
4228 if(actee)
4229 {
4230 for(ii = count = 0; ii < bans->used; ++ii)
4231 {
2f61d1d7 4232 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
277ad996 4233 MATCH_USENICK | MATCH_VISIBLE, 0);
d76ed9a9 4234 if(match[ii])
4235 count++;
4236 }
4237 }
4238 else
4239 {
4240 for(ii = count = 0; ii < bans->used; ++ii)
4241 {
4242 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
4243 if(match[ii])
4244 count++;
4245 }
4246 }
4247 if(!count)
4248 return NULL;
4249 change = mod_chanmode_alloc(count);
4250 for(ii = count = 0; ii < bans->used; ++ii)
4251 {
4252 if(!match[ii])
4253 continue;
4254 change->args[count].mode = MODE_REMOVE | MODE_BAN;
ec1a68c8 4255 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
d76ed9a9 4256 }
ec1a68c8 4257 assert(count == change->argc);
d76ed9a9 4258 return change;
4259}
4260
7153d2c7 4261void expire_bans(UNUSED_ARG(void* data)) /* Real bans, not lamers */
4262{
4263 unsigned int jj, ii, count;
4264 struct banNode *bn;
4265 struct chanData *channel;
4266 time_t bantimeout;
4267 struct mod_chanmode *change;
4268
4269 log_module(CS_LOG, LOG_DEBUG, "Checking for expired bans");
4270 /* Walk through every channel */
4271 for(channel = channelList; channel; channel = channel->next) {
4272 switch(channel->chOpts[chBanTimeout])
4273 {
4274 default: case '0': continue; /* Dont remove bans in this chan */
4275 case '1': bantimeout = now - (10 * 60); break; /* 10 minutes */
4276 case '2': bantimeout = now - (2 * 60 * 60); break; /* 2 hours */
4277 case '3': bantimeout = now - (4 * 60 * 60); break; /* 4 hours */
4278 case '4': bantimeout = now - (24 * 60 * 60); break; /* 24 hours */
4279 case '5': bantimeout = now - (7 * 24 * 60 * 60); break; /* 1 week */
4280 }
4281 count = 0;
4282 /* First find out how many bans were going to unset */
4283 for (jj=0; jj < channel->channel->banlist.used; ++jj) {
be1ad341
AS
4284 //TODO: for now, were just not removing extended bans, but ultimately some types we should, some shouldn't...see below
4285 if(channel->channel->banlist.list[jj]->set < bantimeout && !is_extban(channel->channel->banlist.list[jj]->ban))
7153d2c7 4286 count++;
4287 }
4288 if(count > 0) {
4289 /* At least one ban, so setup a removal */
4290 change = mod_chanmode_alloc(count);
4291 ii = 0;
4292 /* Walk over every ban in this channel.. */
4293 for (jj=0; jj < channel->channel->banlist.used; ++jj) {
4294 bn = channel->channel->banlist.list[jj];
be1ad341
AS
4295 //TODO: for now, were just not removing extended bans, but ultimately some types we should, some shouldn't...see above
4296 if (bn->set < bantimeout && !is_extban(bn->ban)) {
7153d2c7 4297 log_module(CS_LOG, LOG_DEBUG, "Removing ban %s from %s", bn->ban, channel->channel->name);
4298
4299 /* Add this ban to the mode change */
4300 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
4301 change->args[ii].u.hostmask = strdup(bn->ban);
4302 ii++;
4303 /* Pull this ban out of the list */
4304 banList_remove(&(channel->channel->banlist), bn);
4305 jj--;
4306 free(bn);
4307 }
4308 }
4309 /* Send the modes to IRC */
4310 mod_chanmode_announce(chanserv, channel->channel, change);
4311
4312 /* free memory from strdup above */
4313 for(ii = 0; ii < count; ++ii)
4314 free((char*)change->args[ii].u.hostmask);
4315
4316 mod_chanmode_free(change);
4317 }
4318 }
4319 /* Set this function to run again */
4320 if(chanserv_conf.ban_timeout_frequency)
4321 timeq_add(now + chanserv_conf.ban_timeout_frequency, expire_bans, NULL);
4322}
4323
4324
d76ed9a9 4325static int
4326unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
4327{
4328 struct userNode *actee;
4329 char *mask = NULL;
4330 int acted = 0;
4331
4332 REQUIRE_PARAMS(2);
4333
4334 /* may want to allow a comma delimited list of users... */
4335 if(!(actee = GetUserH(argv[1])))
4336 {
1136f709 4337 if(!is_ircmask(argv[1]) && *argv[1] == '*')
4338 {
4339 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
4340 const char *accountname = argv[1] + 1;
4341
4342 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
4343 mask = strdup(banmask);
4344 }
4345 else if(!is_ircmask(argv[1]))
c52639b4
AS
4346 {
4347 reply("MSG_NICK_UNKNOWN", argv[1]);
4348 return 0;
4349 }
1136f709 4350 else
4351 {
4352 mask = strdup(argv[1]);
4353 }
d76ed9a9 4354 }
4355
4356 /* We don't sanitize the mask here because ircu
4357 doesn't do it. */
4358 if(action & ACTION_UNBAN)
4359 {
4360 struct mod_chanmode *change;
4361 change = find_matching_bans(&channel->banlist, actee, mask);
4362 if(change)
4363 {
ec1a68c8 4364 unsigned int ii;
4365
aa222db2 4366 modcmd_chanmode_announce(change);
ec1a68c8 4367 for(ii = 0; ii < change->argc; ++ii)
4368 free((char*)change->args[ii].u.hostmask);
d76ed9a9 4369 mod_chanmode_free(change);
4370 acted = 1;
4371 }
4372 }
4373
c8273589 4374 if(action & ACTION_DEL_LAMER)
d76ed9a9 4375 {
c52639b4
AS
4376 struct banData *ban, *next;
4377
4378 ban = channel->channel_info->bans; /* lamers */
4379 while(ban)
4380 {
4381 if(actee)
4382 for( ; ban && !user_matches_glob(actee, ban->mask, MATCH_USENICK | MATCH_VISIBLE, 0);
4383 ban = ban->next);
4384 else
4385 for( ; ban && !match_ircglobs(mask, ban->mask);
4386 ban = ban->next);
4387 if(!ban)
4388 break;
4389 next = ban->next;
4390 del_channel_ban(ban);
4391 ban = next;
4392 acted = 1;
4393 }
d76ed9a9 4394 }
4395
4396 if(!acted)
c52639b4 4397 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
d76ed9a9 4398 else
c52639b4 4399 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
d76ed9a9 4400 if(mask)
4401 free(mask);
4402 return 1;
4403}
4404
4405static CHANSERV_FUNC(cmd_unban)
4406{
4407 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
4408}
4409
c8273589 4410static CHANSERV_FUNC(cmd_dellamer)
d76ed9a9 4411{
4412 /* it doesn't necessarily have to remove the channel ban - may want
4413 to make that an option. */
c8273589 4414 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_LAMER);
d76ed9a9 4415}
4416
4417static CHANSERV_FUNC(cmd_unbanme)
4418{
4419 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
4420 long flags = ACTION_UNBAN;
4421
4422 /* remove permanent bans if the user has the proper access. */
4048352e 4423 if(uData->access >= UL_MANAGER)
c52639b4 4424 flags |= ACTION_DEL_LAMER;
d76ed9a9 4425
4426 argv[1] = user->nick;
4427 return unban_user(user, channel, 2, argv, cmd, flags);
4428}
4429
4430static CHANSERV_FUNC(cmd_unbanall)
4431{
4432 struct mod_chanmode *change;
4433 unsigned int ii;
4434
4435 if(!channel->banlist.used)
4436 {
c52639b4
AS
4437 reply("CSMSG_NO_BANS", channel->name);
4438 return 0;
d76ed9a9 4439 }
4440
be1ad341 4441 // TODO: dont remove some kinds of extended bans such as ~c
d76ed9a9 4442 change = mod_chanmode_alloc(channel->banlist.used);
4443 for(ii=0; ii<channel->banlist.used; ii++)
4444 {
4445 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
ec1a68c8 4446 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
d76ed9a9 4447 }
4448 modcmd_chanmode_announce(change);
ec1a68c8 4449 for(ii = 0; ii < change->argc; ++ii)
4450 free((char*)change->args[ii].u.hostmask);
d76ed9a9 4451 mod_chanmode_free(change);
4452 reply("CSMSG_BANS_REMOVED", channel->name);
4453 return 1;
4454}
4455
4456static CHANSERV_FUNC(cmd_open)
4457{
4458 struct mod_chanmode *change;
ec1a68c8 4459 unsigned int ii;
d76ed9a9 4460
4461 change = find_matching_bans(&channel->banlist, user, NULL);
4462 if(!change)
4463 change = mod_chanmode_alloc(0);
4464 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
4465 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4466 && channel->channel_info->modes.modes_set)
4467 change->modes_clear &= ~channel->channel_info->modes.modes_set;
4468 modcmd_chanmode_announce(change);
4469 reply("CSMSG_CHANNEL_OPENED", channel->name);
ec1a68c8 4470 for(ii = 0; ii < change->argc; ++ii)
4471 free((char*)change->args[ii].u.hostmask);
d76ed9a9 4472 mod_chanmode_free(change);
4473 return 1;
4474}
4475
4476static CHANSERV_FUNC(cmd_myaccess)
4477{
4478 static struct string_buffer sbuf;
4479 struct handle_info *target_handle;
4480 struct userData *uData;
4481
4482 if(argc < 2)
4483 target_handle = user->handle_info;
1136f709 4484 else if(!IsStaff(user))
d76ed9a9 4485 {
4486 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
4487 return 0;
4488 }
1136f709 4489 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
4490 return 0;
4491
4492 if(!oper_outranks(user, target_handle))
4493 return 0;
4494
d76ed9a9 4495 if(!target_handle->channels)
4496 {
4497 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
4498 return 1;
4499 }
4500
4501 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
4502 for(uData = target_handle->channels; uData; uData = uData->u_next)
4503 {
4504 struct chanData *cData = uData->channel;
4505
4506 if(uData->access > UL_OWNER)
4507 continue;
4508 if(IsProtected(cData)
4509 && (target_handle != user->handle_info)
4510 && !GetTrueChannelAccess(cData, user->handle_info))
4511 continue;
4512 sbuf.used = 0;
4513 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
6fee1be7 4514 if(uData->flags != 0)
d76ed9a9 4515 string_buffer_append(&sbuf, ',');
4516 if(IsUserSuspended(uData))
4517 string_buffer_append(&sbuf, 's');
4518 if(IsUserAutoOp(uData))
4519 {
4b6129c0 4520 if(uData->access >= UL_OP )
d76ed9a9 4521 string_buffer_append(&sbuf, 'o');
4b6129c0 4522 else if(uData->access >= UL_HALFOP )
55342ce8 4523 string_buffer_append(&sbuf, 'h');
4b6129c0 4524 else if(uData->access >= UL_PEON )
d76ed9a9 4525 string_buffer_append(&sbuf, 'v');
4526 }
4527 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
4528 string_buffer_append(&sbuf, 'i');
cd25f2e9 4529 if(IsUserAutoJoin(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
4530 string_buffer_append(&sbuf, 'j');
d76ed9a9 4531 if(uData->info)
4532 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
4533 else
4534 string_buffer_append_string(&sbuf, ")]");
4535 string_buffer_append(&sbuf, '\0');
a32da4c7 4536 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
d76ed9a9 4537 }
4538
4539 return 1;
4540}
4541
4542static CHANSERV_FUNC(cmd_access)
4543{
4544 struct userNode *target;
4545 struct handle_info *target_handle;
4546 struct userData *uData;
4547 int helping;
4548 char prefix[MAXLEN];
4549
4550 if(argc < 2)
4551 {
c52639b4 4552 target = user;
d76ed9a9 4553 target_handle = target->handle_info;
4554 }
4555 else if((target = GetUserH(argv[1])))
4556 {
4557 target_handle = target->handle_info;
4558 }
4559 else if(argv[1][0] == '*')
4560 {
4561 if(!(target_handle = get_handle_info(argv[1]+1)))
4562 {
4563 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
4564 return 0;
4565 }
4566 }
4567 else
4568 {
4569 reply("MSG_NICK_UNKNOWN", argv[1]);
4570 return 0;
4571 }
4572
4573 assert(target || target_handle);
4574
4575 if(target == chanserv)
4576 {
c52639b4
AS
4577 reply("CSMSG_IS_CHANSERV");
4578 return 1;
d76ed9a9 4579 }
4580
4581 if(!target_handle)
4582 {
4583 if(IsOper(target))
4584 {
4585 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
4586 return 0;
4587 }
c52639b4
AS
4588 if(target != user)
4589 {
4590 reply("MSG_USER_AUTHENTICATE", target->nick);
4591 return 0;
4592 }
4593 reply("MSG_AUTHENTICATE");
4594 return 0;
d76ed9a9 4595 }
4596
4597 if(target)
4598 {
4599 const char *epithet = NULL, *type = NULL;
4600 if(IsOper(target))
4601 {
4602 epithet = chanserv_conf.irc_operator_epithet;
1136f709 4603 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
d76ed9a9 4604 }
4605 else if(IsNetworkHelper(target))
4606 {
4607 epithet = chanserv_conf.network_helper_epithet;
1136f709 4608 type = user_find_message(user, "CSMSG_UC_H_TITLE");
d76ed9a9 4609 }
4610 else if(IsSupportHelper(target))
4611 {
4612 epithet = chanserv_conf.support_helper_epithet;
1136f709 4613 type = user_find_message(user, "CSMSG_LC_H_TITLE");
d76ed9a9 4614 }
4615 if(epithet)
4616 {
4617 if(target_handle->epithet)
4618 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
4619 else if(epithet)
4620 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
4621 }
4622 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
4623 }
4624 else
4625 {
4626 sprintf(prefix, "%s", target_handle->handle);
4627 }
4628
4629 if(!channel->channel_info)
4630 {
4631 reply("CSMSG_NOT_REGISTERED", channel->name);
4632 return 1;
4633 }
4634
4635 helping = HANDLE_FLAGGED(target_handle, HELPING)
4636 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
4637 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
4638 {
de9510bc 4639 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, user_level_name_from_level(uData->access), uData->access, channel->name);
d76ed9a9 4640 /* To prevent possible information leaks, only show infolines
4641 * if the requestor is in the channel or it's their own
4642 * handle. */
4643 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
4644 {
4645 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
4646 }
4647 /* Likewise, only say it's suspended if the user has active
4648 * access in that channel or it's their own entry. */
4649 if(IsUserSuspended(uData)
4650 && (GetChannelUser(channel->channel_info, user->handle_info)
4651 || (user->handle_info == uData->handle)))
4652 {
4653 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4654 }
4655 }
4656 else
4657 {
4658 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4659 }
4660
4661 return 1;
4662}
4663
23475fc6 4664/* This is never used...
d76ed9a9 4665static void
4666zoot_list(struct listData *list)
4667{
4668 struct userData *uData;
4669 unsigned int start, curr, highest, lowest;
4670 struct helpfile_table tmp_table;
4671 const char **temp, *msg;
4672
4673 if(list->table.length == 1)
4674 {
4675 if(list->search)
a915f7d4 4676 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
d76ed9a9 4677 else
a915f7d4 4678 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
d76ed9a9 4679 msg = user_find_message(list->user, "MSG_NONE");
4680 send_message_type(4, list->user, list->bot, " %s", msg);
4681 }
4682 tmp_table.width = list->table.width;
4683 tmp_table.flags = list->table.flags;
4684 list->table.contents[0][0] = " ";
4685 highest = list->highest;
4686 if(list->lowest != 0)
4687 lowest = list->lowest;
4688 else if(highest < 100)
4689 lowest = 1;
4690 else
4691 lowest = highest - 100;
4692 for(start = curr = 1; curr < list->table.length; )
4693 {
4694 uData = list->users[curr-1];
4695 list->table.contents[curr++][0] = " ";
4696 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4697 {
4698 if(list->search)
a915f7d4 4699 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, user_level_name_from_level(lowest), user_level_name_from_level(highest), list->search);
d76ed9a9 4700 else
a915f7d4 4701 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, user_level_name_from_level(lowest), user_level_name_from_level(highest));
d76ed9a9 4702 temp = list->table.contents[--start];
4703 list->table.contents[start] = list->table.contents[0];
4704 tmp_table.contents = list->table.contents + start;
4705 tmp_table.length = curr - start;
4706 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4707 list->table.contents[start] = temp;
4708 start = curr;
4709 highest = lowest - 1;
4710 lowest = (highest < 100) ? 0 : (highest - 99);
4711 }
4712 }
4713}
23475fc6 4714*/
d76ed9a9 4715
4716static void
338a82b5 4717normal_list(struct listData *list)
d76ed9a9 4718{
4719 const char *msg;
4720 if(list->search)
338a82b5 4721 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
d76ed9a9 4722 else
338a82b5 4723 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_NORMAL", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
d76ed9a9 4724 if(list->table.length == 1)
4725 {
4726 msg = user_find_message(list->user, "MSG_NONE");
4727 send_message_type(4, list->user, list->bot, " %s", msg);
4728 }
8e11460f 4729 else
4730 table_send(list->bot, list->user->nick, 0, NULL, list->table);
d76ed9a9 4731}
4732
338a82b5 4733/* if these need changed, uncomment and customize
4734static void
4735clean_list(struct listData *list)
4736{
4737 const char *msg;
4738 if(list->search)
4739 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
4740 else
4741 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLEAN", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
4742 if(list->table.length == 1)
4743 {
4744 msg = user_find_message(list->user, "MSG_NONE");
4745 send_message_type(4, list->user, list->bot, " %s", msg);
4746 }
4747 else
4748 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4749}
4750
4751static void
4752advanced_list(struct listData *list)
4753{
4754 const char *msg;
4755 if(list->search)
4756 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
4757 else
4758 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_ADVANCED", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
4759 if(list->table.length == 1)
4760 {
4761 msg = user_find_message(list->user, "MSG_NONE");
4762 send_message_type(4, list->user, list->bot, " %s", msg);
4763 }
4764 else
4765 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4766}
4767
d9896a83 4768static void
4769classic_list(struct listData *list)
4770{
4771 const char *msg;
4772 if(list->search)
4773 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
4774 else
4775 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
4776 if(list->table.length == 1)
4777 {
4778 msg = user_find_message(list->user, "MSG_NONE");
4779 send_message_type(4, list->user, list->bot, " %s", msg);
4780 }
4781 else
4782 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4783}
338a82b5 4784*/
4785
d76ed9a9 4786static int
4787userData_access_comp(const void *arg_a, const void *arg_b)
4788{
4789 const struct userData *a = *(struct userData**)arg_a;
4790 const struct userData *b = *(struct userData**)arg_b;
4791 int res;
4792 if(a->access != b->access)
4793 res = b->access - a->access;
4794 else
4795 res = irccasecmp(a->handle->handle, b->handle->handle);
4796 return res;
4797}
4798
4799static int
4800cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4801{
4802 void (*send_list)(struct listData *);
4803 struct userData *uData;
4804 struct listData lData;
4805 unsigned int matches;
4806 const char **ary;
338a82b5 4807 int i = 0;
4808 int seen_index;
d76ed9a9 4809
4810 lData.user = user;
4811 lData.bot = cmd->parent->bot;
4812 lData.channel = channel;
4813 lData.lowest = lowest;
4814 lData.highest = highest;
4815 lData.search = (argc > 1) ? argv[1] : NULL;
338a82b5 4816 send_list = normal_list;
e6892204 4817 /* What does the following line do exactly?? */
8e11460f 4818 /*(void)zoot_list; ** since it doesn't show user levels */
d76ed9a9 4819
338a82b5 4820 /*
d76ed9a9 4821 if(user->handle_info)
4822 {
338a82b5 4823 switch(user->handle_info->userlist_style)
4824 {
4825 case HI_STYLE_CLEAN:
4826 send_list = clean_list;
4827 break;
4828 case HI_STYLE_ADVANCED:
4829 send_list = advanced_list;
4830 break;
d9896a83 4831 case HI_STYLE_CLASSIC:
4832 send_list = classic_list;
4833 break;
338a82b5 4834 case HI_STYLE_NORMAL:
4835 default:
4836 send_list = normal_list;
4837 break;
4838 }
d76ed9a9 4839 }
e6892204 4840 */
338a82b5 4841 send_list = normal_list;
d76ed9a9 4842
4843 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4844 matches = 0;
4845 for(uData = channel->channel_info->users; uData; uData = uData->next)
4846 {
c52639b4
AS
4847 if((uData->access < lowest)
4848 || (uData->access > highest)
4849 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4850 continue;
4851 lData.users[matches++] = uData;
d76ed9a9 4852 }
4853 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4854
4855 lData.table.length = matches+1;
d76ed9a9 4856 lData.table.flags = TABLE_NO_FREE;
4857 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
338a82b5 4858
3498a411 4859 if(user->handle_info && user->handle_info->userlist_style == HI_STYLE_ADVANCED)
fd20b142 4860 lData.table.width = 6; /* with level = 6 */
338a82b5 4861 else
fd20b142 4862 lData.table.width = 5; /* without = 5 */
d76ed9a9 4863 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4864 lData.table.contents[0] = ary;
d9896a83 4865 if(user->handle_info) {
4866 switch(user->handle_info->userlist_style) {
4867 case HI_STYLE_CLASSIC:
4868 ary[i++] = "Level";
4869 break;
4870 case HI_STYLE_ADVANCED:
4871 ary[i++] = "Access";
4872 ary[i++] = "Level";
4873 break;
4874 case HI_STYLE_CLEAN:
4875 ary[i++] = "Access";
4876 break;
4877 case HI_STYLE_NORMAL:
4878 default:
4879 ary[i++] = "Access";
4880 break;
4881 }
4882 }
4883 else {
4884 ary[i++] = "Access";
4885 }
338a82b5 4886 ary[i++] = "Account";
4887 ary[i] = "Last Seen";
4888 seen_index = i++;
4889 ary[i++] = "Status";
fd20b142 4890 ary[i++] = "Expiry";
d76ed9a9 4891 for(matches = 1; matches < lData.table.length; ++matches)
4892 {
d76ed9a9 4893 char seen[INTERVALLEN];
4894
1136f709 4895
4896 uData = lData.users[matches-1];
338a82b5 4897 i = 0;
d76ed9a9 4898 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4899 lData.table.contents[matches] = ary;
d9896a83 4900 if(user->handle_info) {
4901 switch(user->handle_info->userlist_style) {
4902 case HI_STYLE_CLASSIC:
4903 ary[i++] = strtab(uData->access);
4904 break;
4905 case HI_STYLE_ADVANCED:
4906 ary[i++] = user_level_name_from_level(uData->access);
4907 ary[i++] = strtab(uData->access);
4908 break;
4909 case HI_STYLE_CLEAN:
4910 ary[i++] = user_level_name_from_level(uData->access);
4911 break;
4912 case HI_STYLE_NORMAL:
4913 default:
4914 ary[i++] = user_level_name_from_level(uData->access);
4915 break;
4916 }
4917 }
c52639b4 4918 else {
d9896a83 4919 ary[i++] = user_level_name_from_level(uData->access);
4920 }
338a82b5 4921 ary[i++] = uData->handle->handle;
d76ed9a9 4922 if(uData->present)
338a82b5 4923 ary[i] = "Here";
d76ed9a9 4924 else if(!uData->seen)
338a82b5 4925 ary[i] = "Never";
d76ed9a9 4926 else
338a82b5 4927 ary[i] = intervalString(seen, now - uData->seen, user->handle_info);
4928 ary[i] = strdup(ary[i]);
4929 i++;
d76ed9a9 4930 if(IsUserSuspended(uData))
338a82b5 4931 ary[i++] = "Suspended";
d76ed9a9 4932 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
338a82b5 4933 ary[i++] = "Vacation";
d76ed9a9 4934 else
338a82b5 4935 ary[i++] = "Normal";
fd20b142 4936
dfaa28a4 4937 if ((uData->accessexpiry > 0) || (uData->clvlexpiry > 0)) {
fd20b142 4938 char delay[INTERVALLEN];
4939 time_t diff;
4940
dfaa28a4 4941 if (uData->accessexpiry > 0) {
4942 diff = uData->accessexpiry - now;
4943 intervalString(delay, diff, user->handle_info);
4944 } else {
4945 diff = uData->clvlexpiry - now;
4946 intervalString(delay, diff, user->handle_info);
4947 }
fd20b142 4948 ary[i++] = delay;
c52639b4
AS
4949 }
4950 else
fd20b142 4951 ary[i++] = "Never";
d76ed9a9 4952 }
fd20b142 4953
d76ed9a9 4954 send_list(&lData);
4955 for(matches = 1; matches < lData.table.length; ++matches)
4956 {
338a82b5 4957 /* Free strdup above */
4958 free((char*)lData.table.contents[matches][seen_index]);
d76ed9a9 4959 free(lData.table.contents[matches]);
4960 }
4961 free(lData.table.contents[0]);
4962 free(lData.table.contents);
4963 return 1;
4964}
4965
1d957482 4966/* Remove this now that debugging is over? or improve it for
4967 * users? Would it be better tied into USERS somehow? -Rubin */
ac3bdc8d 4968static CHANSERV_FUNC(cmd_pending)
4969{
4970 struct adduserPending *ap;
1d957482 4971 reply("CSMSG_ADDUSER_PENDING_HEADER");
cde20a3f 4972 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
4973 reply("CSMSG_BAR");
ac3bdc8d 4974 for(ap = adduser_pendings;ap;ap = ap->next)
4975 reply("CSMSG_ADDUSER_PENDING_LIST", ap->channel->name, ap->user->nick);
1d957482 4976 reply("CSMSG_ADDUSER_PENDING_FOOTER");
ac3bdc8d 4977 return 1;
4978}
4979
d76ed9a9 4980static CHANSERV_FUNC(cmd_users)
4981{
4982 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4983}
4984
4985static CHANSERV_FUNC(cmd_wlist)
4986{
4987 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4988}
4989
4990static CHANSERV_FUNC(cmd_clist)
4991{
4992 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4993}
4994
4995static CHANSERV_FUNC(cmd_mlist)
4996{
4048352e 4997 return cmd_list_users(CSFUNC_ARGS, UL_MANAGER, UL_COOWNER-1);
d76ed9a9 4998}
4999
5000static CHANSERV_FUNC(cmd_olist)
5001{
4048352e 5002 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MANAGER-1);
d76ed9a9 5003}
5004
55342ce8 5005static CHANSERV_FUNC(cmd_hlist)
5006{
5007 return cmd_list_users(CSFUNC_ARGS, UL_HALFOP, UL_OP-1);
5008}
5009
d76ed9a9 5010static CHANSERV_FUNC(cmd_plist)
5011{
55342ce8 5012 return cmd_list_users(CSFUNC_ARGS, 1, UL_HALFOP-1);
d76ed9a9 5013}
5014
c8273589 5015static CHANSERV_FUNC(cmd_lamers)
d76ed9a9 5016{
1136f709 5017 struct userNode *search_u = NULL;
d76ed9a9 5018 struct helpfile_table tbl;
1136f709 5019 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
d76ed9a9 5020 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
5021 const char *msg_never, *triggered, *expires;
c8273589 5022 struct banData *ban, **bans; /* lamers */
d76ed9a9 5023
1136f709 5024 if(argc < 2)
d76ed9a9 5025 search = NULL;
1136f709 5026 else if(strchr(search = argv[1], '!'))
5027 {
c52639b4 5028 search = argv[1];
1136f709 5029 search_wilds = search[strcspn(search, "?*")];
5030 }
5031 else if(!(search_u = GetUserH(search)))
5032 reply("MSG_NICK_UNKNOWN", search);
d76ed9a9 5033
c8273589 5034 reply("CSMSG_LAMERS_HEADER", channel->name);
5035 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *)); /* lamers */
d76ed9a9 5036
c8273589 5037 /* lamers */
d76ed9a9 5038 for(ban = channel->channel_info->bans; ban; ban = ban->next)
5039 {
1136f709 5040 if(search_u)
5041 {
277ad996 5042 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE, 0))
1136f709 5043 continue;
5044 }
c52639b4 5045 else if(search)
1136f709 5046 {
5047 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
5048 continue;
5049 }
c52639b4
AS
5050 bans[matches++] = ban;
5051 if(ban->expires)
d76ed9a9 5052 timed = 1;
5053 }
5054
5055 tbl.length = matches + 1;
5056 tbl.width = 4 + timed;
5057 tbl.flags = 0;
5058 tbl.flags = TABLE_NO_FREE;
5059 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
5060 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
5061 tbl.contents[0][0] = "Mask";
5062 tbl.contents[0][1] = "Set By";
5063 tbl.contents[0][2] = "Triggered";
5064 if(timed)
5065 {
5066 tbl.contents[0][3] = "Expires";
5067 tbl.contents[0][4] = "Reason";
5068 }
5069 else
5070 tbl.contents[0][3] = "Reason";
5071 if(!matches)
5072 {
5073 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
c52639b4 5074 /* reply("MSG_NONE"); */
d76ed9a9 5075 free(tbl.contents[0]);
5076 free(tbl.contents);
c52639b4 5077 return 0;
d76ed9a9 5078 }
5079
5080 msg_never = user_find_message(user, "MSG_NEVER");
5081 for(ii = 0; ii < matches; )
5082 {
c52639b4 5083 ban = bans[ii];
d76ed9a9 5084
c52639b4
AS
5085 if(!timed)
5086 expires = "";
5087 else if(ban->expires)
5088 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
5089 else
5090 expires = msg_never;
d76ed9a9 5091
c52639b4
AS
5092 if(ban->triggered)
5093 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
5094 else
5095 triggered = msg_never;
d76ed9a9 5096
5097 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
5098 tbl.contents[ii][0] = ban->mask;
5099 tbl.contents[ii][1] = ban->owner;
5100 tbl.contents[ii][2] = strdup(triggered);
5101 if(timed)
5102 {
5103 tbl.contents[ii][3] = strdup(expires);
5104 tbl.contents[ii][4] = ban->reason;
5105 }
5106 else
5107 tbl.contents[ii][3] = ban->reason;
5108 }
5109 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
de9510bc 5110 /* reply("MSG_MATCH_COUNT", matches); */
d76ed9a9 5111 for(ii = 1; ii < tbl.length; ++ii)
5112 {
5113 free((char*)tbl.contents[ii][2]);
5114 if(timed)
5115 free((char*)tbl.contents[ii][3]);
5116 free(tbl.contents[ii]);
5117 }
5118 free(tbl.contents[0]);
5119 free(tbl.contents);
5120 return 1;
5121}
5122
b75e24a3 5123/* bad_topic
5124 *
5125 * return + if the user does NOT have the right to set the topic, and
5126 * the topic is changed.
5127 */
d76ed9a9 5128static int
5129bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
5130{
5131 struct chanData *cData = channel->channel_info;
5132 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
5133 return 0;
d76ed9a9 5134 else if(cData->topic)
5135 return irccasecmp(new_topic, cData->topic);
5136 else
5137 return 0;
5138}
5139
b75e24a3 5140/* conform_topic
5141 *
5142 * Makes a givin topic fit into a givin topic mask and returns
5143 * the results.
5144 *
5145 * topic_mask - the mask to conform to
5146 * topic - the topic to make conform
5147 * new_topic - the pre-allocated char* to put the new topic into
5148 *
5149 * modifies: new_topic
5150 */
5151void
5152conform_topic(char* topic_mask, char* topic, char *new_topic)
5153{
5154 //char *topic_mask = cData->topic_mask;
5155 char tchar;
5156 int pos=0, starpos=-1, dpos=0, len;
5157
5158 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
5159 {
5160 switch(tchar)
5161 {
5162 case '*':
5163 if(starpos != -1)
5164 {
5165 strcpy(new_topic, "");
5166 return;
5167 }
5168 len = strlen(topic);
5169 if((dpos + len) > TOPICLEN)
5170 len = TOPICLEN + 1 - dpos;
5171 memcpy(new_topic+dpos, topic, len);
5172 dpos += len;
5173 starpos = pos;
5174 break;
5175 case '\\': tchar = topic_mask[pos++]; /* and fall through */
5176 default: new_topic[dpos++] = tchar; break;
5177 }
5178 }
5179 if((dpos > TOPICLEN) || tchar)
5180 {
5181 strcpy(new_topic, "");
5182 return;
5183 }
5184 new_topic[dpos] = 0;
5185 return;
5186}
5187
d76ed9a9 5188static CHANSERV_FUNC(cmd_topic)
5189{
5190 struct chanData *cData;
5191 char *topic;
7fda2b52 5192 int p10 = 0;
5193
5194#ifdef WITH_PROTOCOL_P10
5195 p10 = 1;
5196#endif
d76ed9a9 5197
5198 cData = channel->channel_info;
5199 if(argc < 2)
5200 {
b75e24a3 5201 if(cData->topic)
5202 {
75ef8cdc 5203 /*XXX Why would we ever want to send chanserv as the setter? I dont understand -Rubin */
7fda2b52 5204 SetChannelTopic(channel, chanserv, p10 ? user : chanserv, cData->topic, 1);
b75e24a3 5205 reply("CSMSG_TOPIC_SET", cData->topic);
d76ed9a9 5206 return 1;
b75e24a3 5207 }
d76ed9a9 5208
5209 reply("CSMSG_NO_TOPIC", channel->name);
5210 return 0;
5211 }
5212
5213 topic = unsplit_string(argv + 1, argc - 1, NULL);
5214 /* If they say "!topic *", use an empty topic. */
5215 if((topic[0] == '*') && (topic[1] == 0))
5216 topic[0] = 0;
b75e24a3 5217
d76ed9a9 5218 if(bad_topic(channel, user, topic))
5219 {
b75e24a3 5220 reply("CSMSG_TOPIC_LOCKED", channel->name);
5221 return 0;
5222 }
5223 else
5224 {
5225 /* If there is a topicmask set, and the new topic doesnt match, make it */
5226 if(cData->topic_mask && !match_ircglob(topic, cData->topic_mask))
d76ed9a9 5227 {
b75e24a3 5228 char *topic_mask = cData->topic_mask;
5229 char new_topic[TOPICLEN+1];
d76ed9a9 5230
b75e24a3 5231 /* make a new topic fitting mask */
5232 conform_topic(topic_mask, topic, new_topic);
5233 if(!*new_topic)
d76ed9a9 5234 {
b75e24a3 5235 /* Topic couldnt fit into mask, was too long */
d76ed9a9 5236 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
5237 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
5238 return 0;
5239 }
7fda2b52 5240 SetChannelTopic(channel, chanserv, p10 ? user : chanserv, new_topic, 1);
d76ed9a9 5241 }
b75e24a3 5242 else /* No mask set, just set the topic */
7fda2b52 5243 SetChannelTopic(channel, chanserv, p10 ? user : chanserv, topic, 1);
d76ed9a9 5244 }
d76ed9a9 5245
5246 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
5247 {
5248 /* Grab the topic and save it as the default topic. */
5249 free(cData->topic);
5250 cData->topic = strdup(channel->topic);
5251 }
5252
5253 return 1;
5254}
5255
5256static CHANSERV_FUNC(cmd_mode)
5257{
2f61d1d7 5258 struct userData *uData;
d76ed9a9 5259 struct mod_chanmode *change;
2f61d1d7 5260 short base_oplevel;
d76ed9a9 5261
5262 if(argc < 2)
5263 {
c52639b4
AS
5264 if (checkDefCon(DEFCON_NO_MODE_CHANGE) && !IsOper(user))
5265 {
08895577 5266 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
5267 return 0;
5268 }
5269
d76ed9a9 5270 change = &channel->channel_info->modes;
c52639b4 5271 if(change->modes_set || change->modes_clear) {
d76ed9a9 5272 modcmd_chanmode_announce(change);
5273 reply("CSMSG_DEFAULTED_MODES", channel->name);
c52639b4
AS
5274 }
5275 else
5276 reply("CSMSG_NO_MODES", channel->name);
5277 return 1;
d76ed9a9 5278 }
5279
2f61d1d7 5280 uData = GetChannelUser(channel->channel_info, user->handle_info);
5281 if (!uData)
5282 base_oplevel = MAXOPLEVEL;
5283 else if (uData->access >= UL_OWNER)
5284 base_oplevel = 1;
5285 else
5286 base_oplevel = 1 + UL_OWNER - uData->access;
5287 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
5288
d76ed9a9 5289 if(!change)
5290 {
c52639b4
AS
5291 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
5292 return 0;
d76ed9a9 5293 }
5294
5295 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
5296 && mode_lock_violated(&channel->channel_info->modes, change))
5297 {
5298 char modes[MAXLEN];
5299 mod_chanmode_format(&channel->channel_info->modes, modes);
5300 reply("CSMSG_MODE_LOCKED", modes, channel->name);
5301 return 0;
5302 }
5303
5304 modcmd_chanmode_announce(change);
5305 mod_chanmode_free(change);
5306 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
5307 return 1;
5308}
5309
5310static CHANSERV_FUNC(cmd_invite)
5311{
5312 struct userData *uData;
5313 struct userNode *invite;
5314
5315 uData = GetChannelUser(channel->channel_info, user->handle_info);
5316
5317 if(argc > 1)
5318 {
5319 if(!(invite = GetUserH(argv[1])))
c52639b4 5320 {
d76ed9a9 5321 reply("MSG_NICK_UNKNOWN", argv[1]);
5322 return 0;
5323 }
5324 }
5325 else
5326 invite = user;
5327
5328 if(GetUserMode(channel, invite))
5329 {
c52639b4
AS
5330 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
5331 return 0;
d76ed9a9 5332 }
5333
5334 if(user != invite)
5335 {
5336 if(argc > 2)
5337 {
5338 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
5339 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
5340 }
5341 else
5342 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
5343 }
5f1600ab 5344
22d5d8cf 5345 if (invite->handle_info && invite->handle_info->ignores->used && (argc > 1)) {
5f1600ab 5346 unsigned int i;
c52639b4
AS
5347 for (i=0; i < invite->handle_info->ignores->used; i++)
5348 {
5349 if (user_matches_glob(user, invite->handle_info->ignores->list[i], MATCH_USENICK, 0))
5350 {
5351 reply("CSMSG_CANNOT_INVITE", argv[1], channel->name);
5352 return 0;
5f1600ab 5353 }
5354 }
5355 }
5356
d76ed9a9 5357 irc_invite(chanserv, invite, channel);
5358 if(argc > 1)
c52639b4 5359 reply("CSMSG_INVITED_USER", argv[1], channel->name);
d76ed9a9 5360
5361 return 1;
5362}
5363
5364static CHANSERV_FUNC(cmd_inviteme)
5365{
5366 if(GetUserMode(channel, user))
5367 {
c52639b4
AS
5368 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
5369 return 0;
d76ed9a9 5370 }
5371 if(channel->channel_info
5372 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
5373 {
5374 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
5375 return 0;
5376 }
5377 irc_invite(cmd->parent->bot, user, channel);
5378 return 1;
5379}
5380
5381static void
5382show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
5383{
5384 unsigned int combo;
5385 char buf1[INTERVALLEN], buf2[INTERVALLEN];
5386
5387 /* We display things based on two dimensions:
5388 * - Issue time: present or absent
5389 * - Expiration: revoked, expired, expires in future, or indefinite expiration
5390 * (in order of precedence, so something both expired and revoked
5391 * only counts as revoked)
5392 */
5393 combo = (suspended->issued ? 4 : 0)
5394 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
5395 switch(combo) {
c52639b4
AS
5396 case 0: /* no issue time, indefinite expiration */
5397 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
5398 break;
5399 case 1: /* no issue time, expires in future */
5400 intervalString(buf1, suspended->expires-now, user->handle_info);
5401 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
5402 break;
5403 case 2: /* no issue time, expired */
5404 intervalString(buf1, now-suspended->expires, user->handle_info);
5405 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
5406 break;
5407 case 3: /* no issue time, revoked */
5408 intervalString(buf1, now-suspended->revoked, user->handle_info);
5409 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
5410 break;
5411 case 4: /* issue time set, indefinite expiration */
5412 intervalString(buf1, now-suspended->issued, user->handle_info);
5413 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
5414 break;
5415 case 5: /* issue time set, expires in future */
5416 intervalString(buf1, now-suspended->issued, user->handle_info);
5417 intervalString(buf2, suspended->expires-now, user->handle_info);
5418 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
5419 break;
5420 case 6: /* issue time set, expired */
5421 intervalString(buf1, now-suspended->issued, user->handle_info);
5422 intervalString(buf2, now-suspended->expires, user->handle_info);
5423 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
5424 break;
5425 case 7: /* issue time set, revoked */
5426 intervalString(buf1, now-suspended->issued, user->handle_info);
5427 intervalString(buf2, now-suspended->revoked, user->handle_info);
5428 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
5429 break;
5430 default:
5431 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
5432 return;
d76ed9a9 5433 }
5434}
5435
82f37c08 5436static void
5437show_giveownership_info(struct svccmd *cmd, struct userNode *user, struct giveownership *giveownership)
5438{
5439 char buf[MAXLEN];
5440 const char *fmt = "%a %b %d %H:%M %Y";
5441 strftime(buf, sizeof(buf), fmt, localtime(&giveownership->issued));
5442
5443 if(giveownership->staff_issuer)
5444 {
5445 if(giveownership->reason)
5446 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership->old_owner,
5447 giveownership->target, giveownership->target_access,
5448 giveownership->staff_issuer, buf, giveownership->reason);
5449 else
5450 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership->old_owner,
5451 giveownership->target, giveownership->target_access,
5452 giveownership->staff_issuer, buf);
5453 }
5454 else
5455 {
5456 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership->old_owner, giveownership->target, giveownership->target_access, buf);
5457 }
5458}
5459
5460
d76ed9a9 5461static CHANSERV_FUNC(cmd_info)
5462{
5463 char modes[MAXLEN], buffer[INTERVALLEN];
5464 struct userData *uData, *owner;
5465 struct chanData *cData;
5466 struct do_not_register *dnr;
5467 struct note *note;
5468 dict_iterator_t it;
5469 int privileged;
5470
5471 cData = channel->channel_info;
5472 reply("CSMSG_CHANNEL_INFO", channel->name);
cde20a3f 5473 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
5474 reply("CSMSG_BAR");
d76ed9a9 5475
5476 uData = GetChannelUser(cData, user->handle_info);
b75e24a3 5477 if(uData && (uData->access >= UL_OP /*cData->lvlOpts[lvlGiveOps]*/))
d76ed9a9 5478 {
5479 mod_chanmode_format(&cData->modes, modes);
c52639b4
AS
5480 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
5481 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
d76ed9a9 5482 }
5483
5484 for(it = dict_first(cData->notes); it; it = iter_next(it))
5485 {
5486 int padding;
5487
5488 note = iter_data(it);
5489 if(!note_type_visible_to_user(cData, note->type, user))
5490 continue;
5491
5492 padding = PADLEN - 1 - strlen(iter_key(it));
5493 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
5494 }
5495
5496 reply("CSMSG_CHANNEL_MAX", cData->max);
5497 for(owner = cData->users; owner; owner = owner->next)
5498 if(owner->access == UL_OWNER)
5499 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
5500 reply("CSMSG_CHANNEL_USERS", cData->userCount);
c8273589 5501 reply("CSMSG_CHANNEL_LAMERS", cData->banCount);
d76ed9a9 5502 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
d76ed9a9 5503
5504 privileged = IsStaff(user);
c52639b4
AS
5505 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
5506 if(cData->registrar)
d76ed9a9 5507 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
5508
5509 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
5510 chanserv_show_dnrs(user, cmd, channel->name, NULL);
5511
5512 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
5513 {
5514 struct suspended *suspended;
5515 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
5516 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
5517 show_suspension_info(cmd, user, suspended);
5518 }
5519 else if(IsSuspended(cData))
5520 {
5521 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
5522 show_suspension_info(cmd, user, cData->suspended);
5523 }
82f37c08 5524 if(cData->giveownership && ((uData && (uData->access >= UL_COOWNER)) || IsStaff(user)))
5525 {
5526 struct giveownership *giveownership;
5527 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel->name);
5528 for(giveownership = cData->giveownership; giveownership; giveownership = giveownership->previous)
5529 show_giveownership_info(cmd, user, giveownership);
5530 }
cde20a3f 5531 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
5532 reply("CSMSG_CHANNEL_END");
5533 else
5534 reply("CSMSG_CHANNEL_END_CLEAN");
d76ed9a9 5535 return 1;
5536}
5537
5538static CHANSERV_FUNC(cmd_netinfo)
5539{
5540 extern time_t boot_time;
5541 extern unsigned long burst_length;
5542 char interval[INTERVALLEN];
5543
5544 reply("CSMSG_NETWORK_INFO");
5545 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
5546 reply("CSMSG_NETWORK_USERS", dict_size(clients));
dc92ef7c 5547 reply("CSMSG_NETWORK_OPERS", count_opers);
d76ed9a9 5548 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
c8273589 5549 reply("CSMSG_NETWORK_LAMERS", banCount);
d76ed9a9 5550 reply("CSMSG_NETWORK_CHANUSERS", userCount);
5551 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
5552 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
5553 return 1;
5554}
5555
5556static void
5557send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
5558{
5559 struct helpfile_table table;
5560 unsigned int nn;
5561 struct userNode *user;
5562 char *nick;
5563
5564 table.length = 0;
5565 table.width = 1;
5566 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
5567 table.contents = alloca(list->used*sizeof(*table.contents));
5568 for(nn=0; nn<list->used; nn++)
5569 {
5570 user = list->list[nn];
5571 if(user->modes & skip_flags)
5572 continue;
8b56c5ca 5573 if(IsBot(user) || IsHideOper(user))
d76ed9a9 5574 continue;
5575 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
5576 if(IsAway(user))
5577 {
5578 nick = alloca(strlen(user->nick)+3);
5579 sprintf(nick, "(%s)", user->nick);
5580 }
5581 else
5582 nick = user->nick;
5583 table.contents[table.length][0] = nick;
5584 table.length++;
5585 }
5586 table_send(chanserv, to->nick, 0, NULL, table);
5587}
5588
5589static CHANSERV_FUNC(cmd_ircops)
5590{
5591 reply("CSMSG_STAFF_OPERS");
dc92ef7c 5592 send_staff_list(user, &curr_opers, FLAGS_SERVICE | FLAGS_BOT);
d76ed9a9 5593 return 1;
5594}
5595
5596static CHANSERV_FUNC(cmd_helpers)
5597{
5598 reply("CSMSG_STAFF_HELPERS");
dc92ef7c 5599 send_staff_list(user, &curr_helpers, FLAGS_OPER | FLAGS_SERVICE | FLAGS_BOT);
d76ed9a9 5600 return 1;
5601}
5602
5603static CHANSERV_FUNC(cmd_staff)
5604{
5605 reply("CSMSG_NETWORK_STAFF");
5606 cmd_ircops(CSFUNC_ARGS);
5607 cmd_helpers(CSFUNC_ARGS);
5608 return 1;
5609}
5610
5611static CHANSERV_FUNC(cmd_peek)
5612{
5613 struct modeNode *mn;
5614 char modes[MODELEN];
5615 unsigned int n;
5616 struct helpfile_table table;
5617
5618 irc_make_chanmode(channel, modes);
5619
5620 reply("CSMSG_PEEK_INFO", channel->name);
cde20a3f 5621 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
5622 reply("CSMSG_BAR");
d76ed9a9 5623 reply("CSMSG_PEEK_TOPIC", channel->topic);
5624 reply("CSMSG_PEEK_MODES", modes);
5625 reply("CSMSG_PEEK_USERS", channel->members.used);
5626
5627 table.length = 0;
5628 table.width = 1;
5629 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
5630 table.contents = alloca(channel->members.used*sizeof(*table.contents));
5631 for(n = 0; n < channel->members.used; n++)
5632 {
c52639b4
AS
5633 mn = channel->members.list[n];
5634 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
5635 continue;
d76ed9a9 5636 table.contents[table.length] = alloca(sizeof(**table.contents));
5637 table.contents[table.length][0] = mn->user->nick;
5638 table.length++;
5639 }
5640 if(table.length)
5641 {
5642 reply("CSMSG_PEEK_OPS");
5643 table_send(chanserv, user->nick, 0, NULL, table);
5644 }
5645 else
5646 reply("CSMSG_PEEK_NO_OPS");
de9510bc 5647 reply("CSMSG_PEEK_END");
d76ed9a9 5648 return 1;
5649}
5650
5651static MODCMD_FUNC(cmd_wipeinfo)
5652{
5653 struct handle_info *victim;
1136f709 5654 struct userData *ud, *actor, *real_actor;
5655 unsigned int override = 0;
d76ed9a9 5656
5657 REQUIRE_PARAMS(2);
5658 actor = GetChannelUser(channel->channel_info, user->handle_info);
1136f709 5659 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
d76ed9a9 5660 if(!(victim = modcmd_get_handle_info(user, argv[1])))
5661 return 0;
5662 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
5663 {
5664 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
5665 return 0;
5666 }
5667 if((ud->access >= actor->access) && (ud != actor))
5668 {
5669 reply("MSG_USER_OUTRANKED", victim->handle);
5670 return 0;
5671 }
1136f709 5672 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
5673 override = CMD_LOG_OVERRIDE;
d76ed9a9 5674 if(ud->info)
5675 free(ud->info);
5676 ud->info = NULL;
5677 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
1136f709 5678 return 1 | override;
d76ed9a9 5679}
5680
7637f48f 5681static void
5682resync_channel(struct chanNode *channel)
d76ed9a9 5683{
5684 struct mod_chanmode *changes;
5685 struct chanData *cData = channel->channel_info;
5686 unsigned int ii, used;
5687
e3b2f789 5688 /* 6 = worst case -ovh+ovh on everyone */
5689 changes = mod_chanmode_alloc(channel->members.used * 6);
d76ed9a9 5690 for(ii = used = 0; ii < channel->members.used; ++ii)
5691 {
5692 struct modeNode *mn = channel->members.list[ii];
5693 struct userData *uData;
5694
5695 if(IsService(mn->user))
5696 continue;
5697
e3b2f789 5698
d76ed9a9 5699 uData = GetChannelAccess(cData, mn->user->handle_info);
e3b2f789 5700
5701 /* If the channel is in no-mode mode, de-mode EVERYONE */
5702 if(cData->chOpts[chAutomode] == 'n')
55342ce8 5703 {
e3b2f789 5704 if(mn->modes)
5705 {
5706 changes->args[used].mode = MODE_REMOVE | mn->modes;
5707 changes->args[used++].u.member = mn;
5708 }
55342ce8 5709 }
e3b2f789 5710 else /* Give various userlevels their modes.. */
d76ed9a9 5711 {
2285e0b6
MB
5712 /* If the user has autoop/autovoice disabled then ignore them */
5713 if(uData && !IsUserAutoOp(uData))
5714 continue;
6e00d1bf
MB
5715 if(uData && uData->access >= UL_PEON && cData->chOpts[chAutomode] == 'l')
5716 {
5717 if(!(mn->modes & MODE_VOICE))
5718 {
5719 changes->args[used].mode = MODE_VOICE;
5720 changes->args[used++].u.member = mn;
5721 }
5722 }
5723 else if(uData && uData->access >= UL_OP )
d76ed9a9 5724 {
e3b2f789 5725 if(!(mn->modes & MODE_CHANOP))
5726 {
5727 changes->args[used].mode = MODE_CHANOP;
5728 changes->args[used++].u.member = mn;
5729 }
d76ed9a9 5730 }
e3b2f789 5731 else if(uData && uData->access >= UL_HALFOP)
55342ce8 5732 {
e3b2f789 5733 if(mn->modes & MODE_CHANOP)
5734 {
5735 changes->args[used].mode = MODE_REMOVE | MODE_CHANOP;
5736 changes->args[used++].u.member = mn;
5737 }
5738 if(!(mn->modes & MODE_HALFOP))
5739 {
5740 changes->args[used].mode = MODE_HALFOP;
5741 changes->args[used++].u.member = mn;
5742 }
55342ce8 5743 }
e3b2f789 5744 else if(uData && uData->access >= UL_PEON )
d76ed9a9 5745 {
e3b2f789 5746 if(mn->modes & MODE_CHANOP)
5747 {
5748 changes->args[used].mode = MODE_REMOVE | MODE_CHANOP;
5749 changes->args[used++].u.member = mn;
5750 }
5751 if(mn->modes & MODE_HALFOP)
5752 {
5753 changes->args[used].mode = MODE_REMOVE | MODE_HALFOP;
5754 changes->args[used++].u.member = mn;
5755 }
5756 /* Don't voice peons if were in mode m */
5757 if( cData->chOpts[chAutomode] == 'm')
5758 {
5759 if(mn->modes & MODE_VOICE)
5760 {
5761 changes->args[used].mode = MODE_REMOVE | MODE_VOICE;
5762 changes->args[used++].u.member = mn;
5763 }
5764 }
5765 /* otherwise, make user they do have voice */
5766 else if(!(mn->modes & MODE_VOICE))
5767 {
5768 changes->args[used].mode = MODE_VOICE;
5769 changes->args[used++].u.member = mn;
5770 }
d76ed9a9 5771 }
e3b2f789 5772 else /* They arnt on the userlist.. */
d76ed9a9 5773 {
e3b2f789 5774 /* If we voice everyone, but they dont.. */
5775 if(cData->chOpts[chAutomode] == 'v')
5776 {
5777 /* Remove anything except v */
5778 if(mn->modes & ~MODE_VOICE)
5779 {
5780 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
5781 changes->args[used++].u.member = mn;
5782 }
5783 /* Add v */
5784 if(!(mn->modes & MODE_VOICE))
5785 {
5786 changes->args[used].mode = MODE_VOICE;
5787 changes->args[used++].u.member = mn;
5788 }
5789 }
5790 /* If we hop everyone, but they dont.. */
5791 else if(cData->chOpts[chAutomode] == 'h')
5792 {
5793 /* Remove anything except h */
5794 if(mn->modes & ~MODE_HALFOP)
5795 {
5796 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_HALFOP);
5797 changes->args[used++].u.member = mn;
5798 }
5799 /* Add h */
5800 if(!(mn->modes & MODE_HALFOP))
5801 {
5802 changes->args[used].mode = MODE_HALFOP;
5803 changes->args[used++].u.member = mn;
5804 }
5805 }
5806 /* If we op everyone, but they dont.. */
5807 else if(cData->chOpts[chAutomode] == 'o')
5808 {
5809 /* Remove anything except h */
5810 if(mn->modes & ~MODE_CHANOP)
5811 {
5812 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_CHANOP);
5813 changes->args[used++].u.member = mn;
5814 }
5815 /* Add h */
5816 if(!(mn->modes & MODE_CHANOP))
5817 {
5818 changes->args[used].mode = MODE_CHANOP;
5819 changes->args[used++].u.member = mn;
5820 }
5821 }
5822 /* they have no excuse for having modes, de-everything them */
5823 else
5824 {
5825 if(mn->modes)
5826 {
5827 changes->args[used].mode = MODE_REMOVE | mn->modes;
5828 changes->args[used++].u.member = mn;
5829 }
5830 }
d76ed9a9 5831 }
5832 }
5833 }
5834 changes->argc = used;
7637f48f 5835 mod_chanmode_announce(chanserv, channel, changes);
d76ed9a9 5836 mod_chanmode_free(changes);
7637f48f 5837}
5838
5839static CHANSERV_FUNC(cmd_resync)
5840{
5841 resync_channel(channel);
d76ed9a9 5842 reply("CSMSG_RESYNCED_USERS", channel->name);
5843 return 1;
5844}
5845
5846static CHANSERV_FUNC(cmd_seen)
5847{
5848 struct userData *uData;
5849 struct handle_info *handle;
5850 char seen[INTERVALLEN];
5851
5852 REQUIRE_PARAMS(2);
5853
5854 if(!irccasecmp(argv[1], chanserv->nick))
5855 {
c52639b4
AS
5856 reply("CSMSG_IS_CHANSERV");
5857 return 1;
d76ed9a9 5858 }
5859
5860 if(!(handle = get_handle_info(argv[1])))
5861 {
c52639b4
AS
5862 reply("MSG_HANDLE_UNKNOWN", argv[1]);
5863 return 0;
d76ed9a9 5864 }
5865
5866 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
5867 {
c52639b4
AS
5868 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
5869 return 0;
d76ed9a9 5870 }
5871
5872 if(uData->present)
c52639b4 5873 reply("CSMSG_USER_PRESENT", handle->handle);
d76ed9a9 5874 else if(uData->seen)
5875 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
5876 else
5877 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
5878
5879 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
5880 reply("CSMSG_USER_VACATION", handle->handle);
5881
5882 return 1;
5883}
5884
5885static MODCMD_FUNC(cmd_names)
5886{
5887 struct userNode *targ;
5888 struct userData *targData;
5889 unsigned int ii, pos;
5890 char buf[400];
5891
5892 for(ii=pos=0; ii<channel->members.used; ++ii)
5893 {
5894 targ = channel->members.list[ii]->user;
5895 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
5896 if(!targData)
5897 continue;
5898 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
5899 {
5900 buf[pos] = 0;
5901 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5902 pos = 0;
5903 }
5904 buf[pos++] = ' ';
5905 if(IsUserSuspended(targData))
5906 buf[pos++] = 's';
5907 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
5908 }
5909 buf[pos] = 0;
5910 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5911 reply("CSMSG_END_NAMES", channel->name);
5912 return 1;
5913}
5914
5915static int
5916note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
5917{
5918 switch(ntype->visible_type)
5919 {
c52639b4
AS
5920 case NOTE_VIS_ALL: return 1;
5921 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
5922 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
d76ed9a9 5923 }
5924}
5925
5926static int
5927note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
5928{
5929 struct userData *uData;
5930
5931 switch(ntype->set_access_type)
5932 {
338a82b5 5933 case NOTE_SET_CHANNEL_ACCESS:
5934 if(!user->handle_info)
5935 return 0;
5936 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
5937 return 0;
5938 return uData->access >= ntype->set_access.min_ulevel;
5939 case NOTE_SET_CHANNEL_SETTER:
5940 return check_user_level(channel, user, lvlSetters, 1, 0);
5941 case NOTE_SET_PRIVILEGED: default:
5942 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
d76ed9a9 5943 }
5944}
5945
5946static CHANSERV_FUNC(cmd_note)
5947{
5948 struct chanData *cData;
5949 struct note *note;
5950 struct note_type *ntype;
5951
5952 cData = channel->channel_info;
5953 if(!cData)
5954 {
5955 reply("CSMSG_NOT_REGISTERED", channel->name);
5956 return 0;
5957 }
5958
5959 /* If no arguments, show all visible notes for the channel. */
5960 if(argc < 2)
5961 {
5962 dict_iterator_t it;
5963 unsigned int count;
5964
5965 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5966 {
5967 note = iter_data(it);
5968 if(!note_type_visible_to_user(cData, note->type, user))
5969 continue;
5970 if(!count++)
5971 reply("CSMSG_NOTELIST_HEADER", channel->name);
5972 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5973 }
5974 if(count)
5975 reply("CSMSG_NOTELIST_END", channel->name);
5976 else
5977 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5978 }
5979 /* If one argument, show the named note. */
5980 else if(argc == 2)
5981 {
5982 if((note = dict_find(cData->notes, argv[1], NULL))
5983 && note_type_visible_to_user(cData, note->type, user))
5984 {
5985 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5986 }
5987 else if((ntype = dict_find(note_types, argv[1], NULL))
5988 && note_type_visible_to_user(NULL, ntype, user))
5989 {
5990 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5991 return 0;
5992 }
5993 else
5994 {
5995 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5996 return 0;
5997 }
5998 }
5999 /* Assume they're trying to set a note. */
6000 else
6001 {
6002 char *note_text;
6003 ntype = dict_find(note_types, argv[1], NULL);
6004 if(!ntype)
6005 {
6006 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
6007 return 0;
6008 }
6009 else if(note_type_settable_by_user(channel, ntype, user))
6010 {
6011 note_text = unsplit_string(argv+2, argc-2, NULL);
6012 if((note = dict_find(cData->notes, argv[1], NULL)))
6013 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
6014 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
6015 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
6016
6017 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
6018 {
6019 /* The note is viewable to staff only, so return 0
6020 to keep the invocation from getting logged (or
6021 regular users can see it in !events). */
6022 return 0;
6023 }
6024 }
6025 else
6026 {
6027 reply("CSMSG_NO_ACCESS");
6028 return 0;
6029 }
6030 }
6031 return 1;
6032}
6033
6034static CHANSERV_FUNC(cmd_delnote)
6035{
6036 struct note *note;
6037
6038 REQUIRE_PARAMS(2);
6039 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
6040 || !note_type_settable_by_user(channel, note->type, user))
6041 {
6042 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
6043 return 0;
6044 }
6045 dict_remove(channel->channel_info->notes, note->type->name);
6046 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
6047 return 1;
6048}
6049
23475fc6 6050static CHANSERV_FUNC(cmd_last)
6051{
6052 int numoflines;
6053
6054 REQUIRE_PARAMS(1);
6055
6056 numoflines = (argc > 1) ? atoi(argv[1]) : 10;
6057
6058 if(numoflines < 1 || numoflines > 200)
6059 {
6060 reply("CSMSG_LAST_INVALID");
6061 return 0;
6062 }
6063 ShowLog(user, channel, "*", "*", "*", "*", numoflines);
6064 return 1;
6065}
6066
d76ed9a9 6067static CHANSERV_FUNC(cmd_events)
6068{
6069 struct logSearch discrim;
6070 struct logReport report;
6071 unsigned int matches, limit;
6072
6073 limit = (argc > 1) ? atoi(argv[1]) : 10;
6074 if(limit < 1 || limit > 200)
6075 limit = 10;
6076
6077 memset(&discrim, 0, sizeof(discrim));
6078 discrim.masks.bot = chanserv;
6079 discrim.masks.channel_name = channel->name;
6080 if(argc > 2)
6081 discrim.masks.command = argv[2];
6082 discrim.limit = limit;
6083 discrim.max_time = INT_MAX;
6084 discrim.severities = 1 << LOG_COMMAND;
6085 report.reporter = chanserv;
6086 report.user = user;
de9510bc 6087 reply("CSMSG_EVENT_SEARCH_RESULTS", channel->name);
cde20a3f 6088 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
6089 reply("CSMSG_BAR");
d76ed9a9 6090 matches = log_entry_search(&discrim, log_report_entry, &report);
6091 if(matches)
c52639b4 6092 reply("MSG_MATCH_COUNT", matches);
d76ed9a9 6093 else
c52639b4 6094 reply("MSG_NO_MATCHES");
d76ed9a9 6095 return 1;
6096}
6097
6098static CHANSERV_FUNC(cmd_say)
6099{
6100 char *msg;
6101 if(channel)
6102 {
6103 REQUIRE_PARAMS(2);
6104 msg = unsplit_string(argv + 1, argc - 1, NULL);
6105 send_channel_message(channel, cmd->parent->bot, "%s", msg);
6106 }
1136f709 6107 else if(*argv[1] == '*' && argv[1][1] != '\0')
6108 {
6109 struct handle_info *hi;
6110 struct userNode *authed;
6111
6112 REQUIRE_PARAMS(3);
6113 msg = unsplit_string(argv + 2, argc - 2, NULL);
6114
6115 if (!(hi = get_handle_info(argv[1] + 1)))
6116 {
6117 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
6118 return 0;
6119 }
6120
6121 for (authed = hi->users; authed; authed = authed->next_authed)
6122 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
6123 }
d76ed9a9 6124 else if(GetUserH(argv[1]))
6125 {
6126 REQUIRE_PARAMS(3);
6127 msg = unsplit_string(argv + 2, argc - 2, NULL);
6128 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
6129 }
6130 else
6131 {
6132 reply("MSG_NOT_TARGET_NAME");
6133 return 0;
6134 }
6135 return 1;
6136}
6137
6138static CHANSERV_FUNC(cmd_emote)
6139{
6140 char *msg;
6141 assert(argc >= 2);
6142 if(channel)
6143 {
6144 /* CTCP is so annoying. */
6145 msg = unsplit_string(argv + 1, argc - 1, NULL);
6146 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
6147 }
1136f709 6148 else if(*argv[1] == '*' && argv[1][1] != '\0')
6149 {
6150 struct handle_info *hi;
6151 struct userNode *authed;
6152
6153 REQUIRE_PARAMS(3);
6154 msg = unsplit_string(argv + 2, argc - 2, NULL);
6155
6156 if (!(hi = get_handle_info(argv[1] + 1)))
6157 {
6158 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
6159 return 0;
6160 }
6161
6162 for (authed = hi->users; authed; authed = authed->next_authed)
6163 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
6164 }
d76ed9a9 6165 else if(GetUserH(argv[1]))
6166 {
6167 msg = unsplit_string(argv + 2, argc - 2, NULL);
6168 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
6169 }
6170 else
6171 {
6172 reply("MSG_NOT_TARGET_NAME");
6173 return 0;
6174 }
6175 return 1;
6176}
6177
6178struct channelList *
6179chanserv_support_channels(void)
6180{
6181 return &chanserv_conf.support_channels;
6182}
6183
6184static CHANSERV_FUNC(cmd_expire)
6185{
6186 int channel_count = registered_channels;
6187 expire_channels(NULL);
6188 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
6189 return 1;
6190}
6191
6192static void
6193chanserv_expire_suspension(void *data)
6194{
6195 struct suspended *suspended = data;
6196 struct chanNode *channel;
1136f709 6197 unsigned int ii;
d76ed9a9 6198
1136f709 6199 /* Update the channel registration data structure. */
d76ed9a9 6200 if(!suspended->expires || (now < suspended->expires))
6201 suspended->revoked = now;
6202 channel = suspended->cData->channel;
6203 suspended->cData->channel = channel;
6204 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
1136f709 6205
6206 /* If appropriate, re-join ChanServ to the channel. */
0d16e639 6207 if(!IsOffChannel(suspended->cData))
6208 {
63c95a47 6209 spamserv_cs_suspend(channel, 0, 0, NULL);
6210 ss_cs_join_channel(channel, 1);
0d16e639 6211 }
1136f709 6212
6213 /* Mark everyone currently in the channel as present. */
6214 for(ii = 0; ii < channel->members.used; ++ii)
6215 {
6216 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
6217 if(uData)
6218 {
6219 uData->present = 1;
6220 uData->seen = now;
6221 }
6222 }
d76ed9a9 6223}
6224
6225static CHANSERV_FUNC(cmd_csuspend)
6226{
6227 struct suspended *suspended;
6228 char reason[MAXLEN];
6229 time_t expiry, duration;
6230 struct userData *uData;
6231
6232 REQUIRE_PARAMS(3);
6233
6234 if(IsProtected(channel->channel_info))
6235 {
6236 reply("CSMSG_SUSPEND_NODELETE", channel->name);
6237 return 0;
6238 }
6239
6240 if(argv[1][0] == '!')
6241 argv[1]++;
6242 else if(IsSuspended(channel->channel_info))
6243 {
c52639b4 6244 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
d76ed9a9 6245 show_suspension_info(cmd, user, channel->channel_info->suspended);
c52639b4 6246 return 0;
d76ed9a9 6247 }
6248
6249 if(!strcmp(argv[1], "0"))
6250 expiry = 0;
6251 else if((duration = ParseInterval(argv[1])))
6252 expiry = now + duration;
6253 else
6254 {
6255 reply("MSG_INVALID_DURATION", argv[1]);
6256 return 0;
6257 }
6258
6259 unsplit_string(argv + 2, argc - 2, reason);
6260
6261 suspended = calloc(1, sizeof(*suspended));
6262 suspended->revoked = 0;
6263 suspended->issued = now;
6264 suspended->suspender = strdup(user->handle_info->handle);
6265 suspended->expires = expiry;
6266 suspended->reason = strdup(reason);
6267 suspended->cData = channel->channel_info;
6268 suspended->previous = suspended->cData->suspended;
6269 suspended->cData->suspended = suspended;
6270
6271 if(suspended->expires)
6272 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6273
6274 if(IsSuspended(channel->channel_info))
6275 {
6276 suspended->previous->revoked = now;
6277 if(suspended->previous->expires)
6278 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
09a3057c 6279
6280 global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "CSMSG_SUSPENSION_MODIFIED",
6281 channel->name, suspended->suspender);
d76ed9a9 6282 }
6283 else
6284 {
6285 /* Mark all users in channel as absent. */
6286 for(uData = channel->channel_info->users; uData; uData = uData->next)
6287 {
6288 if(uData->present)
6289 {
6290 uData->seen = now;
6291 uData->present = 0;
6292 }
6293 }
6294
6295 /* Mark the channel as suspended, then part. */
6296 channel->channel_info->flags |= CHANNEL_SUSPENDED;
63c95a47 6297 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
d76ed9a9 6298 DelChannelUser(chanserv, channel, suspended->reason, 0);
6299 reply("CSMSG_SUSPENDED", channel->name);
09a3057c 6300 global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "CSMSG_SUSPENDED_BY",
6301 channel->name, suspended->suspender);
d76ed9a9 6302 }
6303 return 1;
6304}
6305
6306static CHANSERV_FUNC(cmd_cunsuspend)
6307{
6308 struct suspended *suspended;
d76ed9a9 6309
6310 if(!IsSuspended(channel->channel_info))
6311 {
6312 reply("CSMSG_NOT_SUSPENDED", channel->name);
6313 return 0;
6314 }
6315
6316 suspended = channel->channel_info->suspended;
6317
6318 /* Expire the suspension and join ChanServ to the channel. */
6319 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
6320 chanserv_expire_suspension(suspended);
6321 reply("CSMSG_UNSUSPENDED", channel->name);
09a3057c 6322 global_message_args(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, "CSMSG_UNSUSPENDED_BY",
6323 channel->name, user->handle_info->handle);
d76ed9a9 6324 return 1;
6325}
6326
6327typedef struct chanservSearch
6328{
6329 char *name;
6330 char *registrar;
6331
6332 time_t unvisited;
6333 time_t registered;
6334
6335 unsigned long flags;
6336 unsigned int limit;
6337} *search_t;
6338
6339typedef void (*channel_search_func)(struct chanData *channel, void *data);
6340
6341static search_t
c092fcad 6342chanserv_search_create(struct svccmd *cmd, struct userNode *user, unsigned int argc, char *argv[])
d76ed9a9 6343{
6344 search_t search;
6345 unsigned int i;
6346
6347 search = malloc(sizeof(struct chanservSearch));
6348 memset(search, 0, sizeof(*search));
6349 search->limit = 25;
6350
6351 for(i = 0; i < argc; i++)
6352 {
c52639b4
AS
6353 /* Assume all criteria require arguments. */
6354 if(i == (argc - 1))
6355 {
6356 reply("MSG_MISSING_PARAMS", argv[i]);
6357 goto fail;
6358 }
6359
6360 if(!irccasecmp(argv[i], "name"))
6361 search->name = argv[++i];
6362 else if(!irccasecmp(argv[i], "registrar"))
6363 search->registrar = argv[++i];
6364 else if(!irccasecmp(argv[i], "unvisited"))
6365 search->unvisited = ParseInterval(argv[++i]);
6366 else if(!irccasecmp(argv[i], "registered"))
6367 search->registered = ParseInterval(argv[++i]);
6368 else if(!irccasecmp(argv[i], "flags"))
6369 {
6370 i++;
6371 if(!irccasecmp(argv[i], "nodelete"))
6372 search->flags |= CHANNEL_NODELETE;
6373 else if(!irccasecmp(argv[i], "suspended"))
6374 search->flags |= CHANNEL_SUSPENDED;
1136f709 6375 else if(!irccasecmp(argv[i], "unreviewed"))
6376 search->flags |= CHANNEL_UNREVIEWED;
c52639b4
AS
6377 else
6378 {
6379 reply("CSMSG_INVALID_CFLAG", argv[i]);
6380 goto fail;
6381 }
6382 }
6383 else if(!irccasecmp(argv[i], "limit"))
6384 search->limit = strtoul(argv[++i], NULL, 10);
6385 else
6386 {
6387 reply("MSG_INVALID_CRITERIA", argv[i]);
6388 goto fail;
6389 }
d76ed9a9 6390 }
6391
6392 if(search->name && !strcmp(search->name, "*"))
c52639b4 6393 search->name = 0;
d76ed9a9 6394 if(search->registrar && !strcmp(search->registrar, "*"))
c52639b4 6395 search->registrar = 0;
d76ed9a9 6396
6397 return search;
6398 fail:
6399 free(search);
6400 return NULL;
6401}
6402
6403static int
6404chanserv_channel_match(struct chanData *channel, search_t search)
6405{
6406 const char *name = channel->channel->name;
6407 if((search->name && !match_ircglob(name, search->name)) ||
6408 (search->registrar && !channel->registrar) ||
6409 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
6410 (search->unvisited && (now - channel->visited) < search->unvisited) ||
6411 (search->registered && (now - channel->registered) > search->registered) ||
6412 (search->flags && ((search->flags & channel->flags) != search->flags)))
c52639b4 6413 return 0;
d76ed9a9 6414
6415 return 1;
6416}
6417
6418static unsigned int
6419chanserv_channel_search(search_t search, channel_search_func smf, void *data)
6420{
6421 struct chanData *channel;
6422 unsigned int matches = 0;
6423
6424 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
6425 {
c52639b4 6426 if(!chanserv_channel_match(channel, search))
d76ed9a9 6427 continue;
c52639b4
AS
6428 matches++;
6429 smf(channel, data);
d76ed9a9 6430 }
6431
6432 return matches;
6433}
6434
6435static void
6436search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
6437{
6438}
6439
6440static void
6441search_print(struct chanData *channel, void *data)
6442{
6443 send_message_type(4, data, chanserv, "%s", channel->channel->name);
6444}
6445
6446static CHANSERV_FUNC(cmd_search)
6447{
6448 search_t search;
6449 unsigned int matches;
6450 channel_search_func action;
6451
6452 REQUIRE_PARAMS(3);
6453
6454 if(!irccasecmp(argv[1], "count"))
c52639b4 6455 action = search_count;
d76ed9a9 6456 else if(!irccasecmp(argv[1], "print"))
c52639b4 6457 action = search_print;
d76ed9a9 6458 else
6459 {
c52639b4
AS
6460 reply("CSMSG_ACTION_INVALID", argv[1]);
6461 return 0;
d76ed9a9 6462 }
6463
c092fcad 6464 search = chanserv_search_create(cmd, user, argc - 2, argv + 2);
d76ed9a9 6465 if(!search)
6466 return 0;
6467
6468 if(action == search_count)
c52639b4 6469 search->limit = INT_MAX;
d76ed9a9 6470
6471 if(action == search_print)
de9510bc 6472 {
c52639b4 6473 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
cde20a3f 6474 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
6475 reply("CSMSG_BAR");
de9510bc 6476 }
d76ed9a9 6477
6478 matches = chanserv_channel_search(search, action, user);
6479
6480 if(matches)
c52639b4 6481 reply("MSG_MATCH_COUNT", matches);
d76ed9a9 6482 else
c52639b4 6483 reply("MSG_NO_MATCHES");
d76ed9a9 6484
6485 free(search);
6486 return 1;
6487}
6488
6489static CHANSERV_FUNC(cmd_unvisited)
6490{
6491 struct chanData *cData;
6492 time_t interval = chanserv_conf.channel_expire_delay;
6493 char buffer[INTERVALLEN];
6494 unsigned int limit = 25, matches = 0;
6495
6496 if(argc > 1)
6497 {
c52639b4
AS
6498 interval = ParseInterval(argv[1]);
6499 if(argc > 2)
d76ed9a9 6500 limit = atoi(argv[2]);
6501 }
6502
6503 intervalString(buffer, interval, user->handle_info);
6504 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
6505
6506 for(cData = channelList; cData && matches < limit; cData = cData->next)
6507 {
c52639b4 6508 if((now - cData->visited) < interval)
d76ed9a9 6509 continue;
6510
c52639b4
AS
6511 intervalString(buffer, now - cData->visited, user->handle_info);
6512 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
6513 matches++;
d76ed9a9 6514 }
6515
6516 return 1;
6517}
6518
1136f709 6519static MODCMD_FUNC(chan_opt_unreviewed)
6520{
6521 struct chanData *cData = channel->channel_info;
6522 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
6523
6524 if(argc > 1)
6525 {
6526 int new_value;
6527
6528 /* The two directions can have different ACLs. */
6529 if(enabled_string(argv[1]))
6530 new_value = 1;
6531 else if(disabled_string(argv[1]))
6532 new_value = 0;
6533 else
c52639b4
AS
6534 {
6535 reply("MSG_INVALID_BINARY", argv[1]);
6536 return 0;
6537 }
1136f709 6538
6539 if (new_value != value)
6540 {
6541 struct svccmd *subcmd;
6542 char subcmd_name[32];
6543
6544 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
6545 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
6546 if(!subcmd)
6547 {
6548 reply("MSG_COMMAND_DISABLED", subcmd_name);
6549 return 0;
6550 }
6551 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
6552 return 0;
6553
6554 if (new_value)
6555 cData->flags |= CHANNEL_UNREVIEWED;
6556 else
6557 {
6558 free(cData->registrar);
6559 cData->registrar = strdup(user->handle_info->handle);
6560 cData->flags &= ~CHANNEL_UNREVIEWED;
6561 }
6562 value = new_value;
6563 }
6564 }
6565
6566 if(value)
6567 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
6568 else
6569 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
6570 return 1;
6571}
6572
d76ed9a9 6573static MODCMD_FUNC(chan_opt_defaulttopic)
6574{
6575 if(argc > 1)
6576 {
6577 char *topic;
6578
6579 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
6580 {
6581 reply("CSMSG_TOPIC_LOCKED", channel->name);
6582 return 0;
6583 }
6584
c52639b4 6585 topic = unsplit_string(argv+1, argc-1, NULL);
d76ed9a9 6586
6587 free(channel->channel_info->topic);
c52639b4
AS
6588 if(topic[0] == '*' && topic[1] == 0)
6589 {
d76ed9a9 6590 topic = channel->channel_info->topic = NULL;
c52639b4
AS
6591 }
6592 else
6593 {
6594 topic = channel->channel_info->topic = strdup(topic);
d76ed9a9 6595 if(channel->channel_info->topic_mask
6596 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
6597 reply("CSMSG_TOPIC_MISMATCH", channel->name);
c52639b4 6598 }
75ef8cdc 6599 SetChannelTopic(channel, chanserv, user, topic ? topic : "", 1);
d76ed9a9 6600 }
6601
6602 if(channel->channel_info->topic)
6603 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
6604 else
6605 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
6606 return 1;
6607}
6608
6609static MODCMD_FUNC(chan_opt_topicmask)
6610{
6611 if(argc > 1)
6612 {
6613 struct chanData *cData = channel->channel_info;
6614 char *mask;
6615
6616 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
6617 {
6618 reply("CSMSG_TOPIC_LOCKED", channel->name);
6619 return 0;
6620 }
6621
c52639b4 6622 mask = unsplit_string(argv+1, argc-1, NULL);
d76ed9a9 6623
6624 if(cData->topic_mask)
6625 free(cData->topic_mask);
c52639b4
AS
6626 if(mask[0] == '*' && mask[1] == 0)
6627 {
6628 cData->topic_mask = 0;
6629 }
6630 else
6631 {
d76ed9a9 6632 cData->topic_mask = strdup(mask);
6633 if(!cData->topic)
6634 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
6635 else if(!match_ircglob(cData->topic, cData->topic_mask))
6636 reply("CSMSG_TOPIC_MISMATCH", channel->name);
c52639b4 6637 }
d76ed9a9 6638 }
6639
6640 if(channel->channel_info->topic_mask)
6641 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
6642 else
6643 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
6644 return 1;
6645}
6646
6647int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
6648{
6649 if(argc > 1)
6650 {
6651 char *greeting = unsplit_string(argv+1, argc-1, NULL);
6652 char *previous;
6653
6654 previous = *data;
c52639b4
AS
6655 if(greeting[0] == '*' && greeting[1] == 0)
6656 *data = NULL;
6657 else
6658 {
6659 unsigned int length = strlen(greeting);
6660 if(length > chanserv_conf.greeting_length)
6661 {
6662 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
6663 return 0;
6664 }
6665 *data = strdup(greeting);
6666 }
d76ed9a9 6667 if(previous)
6668 free(previous);
6669 }
6670
6671 if(*data)
6672 reply(name, *data);
6673 else
6674 reply(name, user_find_message(user, "MSG_NONE"));
6675 return 1;
6676}
6677
6678static MODCMD_FUNC(chan_opt_greeting)
6679{
6680 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
6681}
6682
6683static MODCMD_FUNC(chan_opt_usergreeting)
6684{
6685 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
6686}
6687
8b9e7d45 6688static MODCMD_FUNC(chan_opt_maxsetinfo)
6689{
6690 unsigned int charmax;
6691
c52639b4
AS
6692 if(argc > 1)
6693 {
8b9e7d45 6694 charmax = atoi(argv[1]);
cb0071f3 6695 if ((charmax > 0) && (charmax <= chanserv_conf.max_userinfo_length))
8b9e7d45 6696 channel->channel_info->maxsetinfo = charmax;
6697 }
6698
6699 reply("CSMSG_SET_MAXSETINFO", channel->channel_info->maxsetinfo);
6700 return 1;
6701}
6702
d76ed9a9 6703static MODCMD_FUNC(chan_opt_modes)
6704{
6705 struct mod_chanmode *new_modes;
6706 char modes[MODELEN];
6707
6708 if(argc > 1)
6709 {
08895577 6710 if (checkDefCon(DEFCON_NO_MODE_CHANGE) && !IsOper(user)) {
6711 reply("CSMSG_DEFCON_NO_MODE_CHANGE");
6712 return 0;
6713 }
6714
d76ed9a9 6715 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
6716 {
6717 reply("CSMSG_NO_ACCESS");
6718 return 0;
6719 }
c52639b4
AS
6720 if(argv[1][0] == '*' && argv[1][1] == 0)
6721 {
6722 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
6723 }
2f61d1d7 6724 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1,MCP_KEY_FREE|MCP_REGISTERED, 0)))
c52639b4 6725 {
d76ed9a9 6726 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
6727 return 0;
6728 }
6729 else if(new_modes->argc > 1)
6730 {
6731 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
6732 mod_chanmode_free(new_modes);
6733 return 0;
6734 }
6735 else
6736 {
6737 channel->channel_info->modes = *new_modes;
6738 modcmd_chanmode_announce(new_modes);
6739 mod_chanmode_free(new_modes);
6740 }
6741 }
6742
6743 mod_chanmode_format(&channel->channel_info->modes, modes);
6744 if(modes[0])
6745 reply("CSMSG_SET_MODES", modes);
6746 else
6747 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
6748 return 1;
6749}
6750
6751#define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
6752static int
6753channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6754{
6755 struct chanData *cData = channel->channel_info;
6756 int value;
6757
6758 if(argc > 1)
6759 {
c52639b4
AS
6760 /* Set flag according to value. */
6761 if(enabled_string(argv[1]))
6762 {
6763 cData->flags |= mask;
6764 value = 1;
6765 }
6766 else if(disabled_string(argv[1]))
6767 {
6768 cData->flags &= ~mask;
6769 value = 0;
6770 }
6771 else
6772 {
6773 reply("MSG_INVALID_BINARY", argv[1]);
6774 return 0;
6775 }
d76ed9a9 6776 }
6777 else
6778 {
c52639b4
AS
6779 /* Find current option value. */
6780 value = (cData->flags & mask) ? 1 : 0;
d76ed9a9 6781 }
6782
6783 if(value)
6784 reply(name, user_find_message(user, "MSG_ON"));
6785 else
6786 reply(name, user_find_message(user, "MSG_OFF"));
6787 return 1;
6788}
6789
6790static MODCMD_FUNC(chan_opt_nodelete)
6791{
6792 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
6793 {
c52639b4
AS
6794 reply("MSG_SETTING_PRIVILEGED", argv[0]);
6795 return 0;
d76ed9a9 6796 }
6797
6798 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
6799}
6800
6801static MODCMD_FUNC(chan_opt_dynlimit)
6802{
97e51197 6803 struct mod_chanmode change;
6804
c52639b4
AS
6805 if (argc > 1)
6806 {
6807 if (disabled_string(argv[1]))
6808 {
97e51197 6809 mod_chanmode_init(&change);
6810 change.modes_clear |= MODE_LIMIT;
6811 mod_chanmode_announce(chanserv, channel, &change);
6812 }
6813 }
6814
d76ed9a9 6815 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
6816}
6817
6818static MODCMD_FUNC(chan_opt_offchannel)
6819{
6820 struct chanData *cData = channel->channel_info;
6821 int value;
6822
6823 if(argc > 1)
6824 {
c52639b4
AS
6825 /* Set flag according to value. */
6826 if(enabled_string(argv[1]))
6827 {
6828 if(!IsOffChannel(cData))
6829 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
6830 cData->flags |= CHANNEL_OFFCHANNEL;
6831 value = 1;
6832 }
6833 else if(disabled_string(argv[1]))
6834 {
6835 if(IsOffChannel(cData))
6836 {
6837 struct mod_chanmode change;
6838 mod_chanmode_init(&change);
6839 change.argc = 1;
6840 change.args[0].mode = MODE_CHANOP;
6841 change.args[0].u.member = AddChannelUser(chanserv, channel);
6842 mod_chanmode_announce(chanserv, channel, &change);
6843 }
6844 cData->flags &= ~CHANNEL_OFFCHANNEL;
6845 value = 0;
6846 }
6847 else
6848 {
6849 reply("MSG_INVALID_BINARY", argv[1]);
6850 return 0;
6851 }
d76ed9a9 6852 }
6853 else
6854 {
c52639b4
AS
6855 /* Find current option value. */
6856 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
d76ed9a9 6857 }
6858
6859 if(value)
6860 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
6861 else
6862 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
6863 return 1;
6864}
6865
6866static MODCMD_FUNC(chan_opt_defaults)
6867{
6868 struct userData *uData;
6869 struct chanData *cData;
6870 const char *confirm;
6871 enum levelOption lvlOpt;
6872 enum charOption chOpt;
6873
6874 cData = channel->channel_info;
6875 uData = GetChannelUser(cData, user->handle_info);
6876 if(!uData || (uData->access < UL_OWNER))
6877 {
6878 reply("CSMSG_OWNER_DEFAULTS", channel->name);
6879 return 0;
6880 }
6881 confirm = make_confirmation_string(uData);
6882 if((argc < 2) || strcmp(argv[1], confirm))
6883 {
6884 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
6885 return 0;
6886 }
1136f709 6887 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
6888 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
d76ed9a9 6889 cData->modes = chanserv_conf.default_modes;
6890 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6891 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6892 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6893 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
6894 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
6895 return 1;
6896}
6897
6898static int
6899channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6900{
6901 struct chanData *cData = channel->channel_info;
6902 struct userData *uData;
6903 unsigned short value;
6904
6905 if(argc > 1)
6906 {
6907 if(!check_user_level(channel, user, option, 1, 1))
6908 {
6909 reply("CSMSG_CANNOT_SET");
6910 return 0;
6911 }
6912 value = user_level_from_name(argv[1], UL_OWNER+1);
6913 if(!value && strcmp(argv[1], "0"))
c52639b4
AS
6914 {
6915 reply("CSMSG_INVALID_ACCESS", argv[1]);
d76ed9a9 6916 return 0;
6917 }
6918 uData = GetChannelUser(cData, user->handle_info);
6919 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
6920 {
6921 reply("CSMSG_BAD_SETLEVEL");
6922 return 0;
6923 }
6924 switch(option)
6925 {
4b6129c0 6926 case lvlSetters:
6927 /* This test only applies to owners, since non-owners
6928 * trying to set an option to above their level get caught
6929 * by the CSMSG_BAD_SETLEVEL test above.
6930 */
6931 if(value > uData->access)
6932 {
6933 reply("CSMSG_BAD_SETTERS");
6934 return 0;
6935 }
6936 break;
6937 default:
6938 break;
d76ed9a9 6939 }
6940 cData->lvlOpts[option] = value;
6941 }
6942 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
6943 return argc > 1;
6944}
6945
6946static MODCMD_FUNC(chan_opt_enfops)
6947{
6948 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
6949}
6950
55342ce8 6951static MODCMD_FUNC(chan_opt_enfhalfops)
6952{
6953 return channel_level_option(lvlEnfHalfOps, CSFUNC_ARGS);
6954}
d76ed9a9 6955static MODCMD_FUNC(chan_opt_enfmodes)
6956{
6957 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
6958}
6959
6960static MODCMD_FUNC(chan_opt_enftopic)
6961{
6962 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
6963}
6964
6965static MODCMD_FUNC(chan_opt_pubcmd)
6966{
6967 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
6968}
6969
6970static MODCMD_FUNC(chan_opt_setters)
6971{
6972 return channel_level_option(lvlSetters, CSFUNC_ARGS);
6973}
6974
d76ed9a9 6975static MODCMD_FUNC(chan_opt_userinfo)
6976{
6977 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
6978}
6979
d76ed9a9 6980static MODCMD_FUNC(chan_opt_topicsnarf)
6981{
6982 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6983}
6984
6985static MODCMD_FUNC(chan_opt_inviteme)
6986{
6987 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6988}
6989
31f23f13 6990/* TODO: Make look like this when no args are
6991 * given:
6992 * -X3- -------------------------------
6993 * -X3- BanTimeout: Bans are removed:
6994 * -X3- ----- * indicates current -----
6995 * -X3- 0: [*] Never.
6996 * -X3- 1: [ ] After 10 minutes.
6997 * -X3- 2: [ ] After 2 hours.
6998 * -X3- 3: [ ] After 4 hours.
6999 * -X3- 4: [ ] After 24 hours.
7000 * -X3- 5: [ ] After one week.
7001 * -X3- ------------- End -------------
7002 */
d76ed9a9 7003static int
7004channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
7005{
7006 struct chanData *cData = channel->channel_info;
1136f709 7007 int count = charOptions[option].count, idx;
d76ed9a9 7008
7009 if(argc > 1)
7010 {
1136f709 7011 idx = atoi(argv[1]);
d76ed9a9 7012
c52639b4
AS
7013 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
7014 {
7015 reply("CSMSG_INVALID_NUMERIC", idx);
d76ed9a9 7016 /* Show possible values. */
1136f709 7017 for(idx = 0; idx < count; idx++)
7018 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
c52639b4
AS
7019 return 0;
7020 }
d76ed9a9 7021
c52639b4 7022 cData->chOpts[option] = charOptions[option].values[idx].value;
d76ed9a9 7023 }
7024 else
7025 {
c52639b4 7026 /* Find current option value. */
d76ed9a9 7027 find_value:
c52639b4 7028 for(idx = 0;
1136f709 7029 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
7030 idx++);
7031 if(idx == count)
d76ed9a9 7032 {
7033 /* Somehow, the option value is corrupt; reset it to the default. */
7034 cData->chOpts[option] = charOptions[option].default_value;
7035 goto find_value;
7036 }
7037 }
7038
1136f709 7039 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
d76ed9a9 7040 return 1;
7041}
7042
4b6129c0 7043static MODCMD_FUNC(chan_opt_automode)
c8ca69a0 7044{
4b6129c0 7045 return channel_multiple_option(chAutomode, CSFUNC_ARGS);
c8ca69a0 7046}
7047
d76ed9a9 7048static MODCMD_FUNC(chan_opt_protect)
7049{
7050 return channel_multiple_option(chProtect, CSFUNC_ARGS);
7051}
7052
7053static MODCMD_FUNC(chan_opt_toys)
7054{
7055 return channel_multiple_option(chToys, CSFUNC_ARGS);
7056}
7057
7058static MODCMD_FUNC(chan_opt_ctcpreaction)
7059{
7060 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
7061}
7062
31f23f13 7063static MODCMD_FUNC(chan_opt_bantimeout)
7064{
7065 return channel_multiple_option(chBanTimeout, CSFUNC_ARGS);
7066}
7067
d76ed9a9 7068static MODCMD_FUNC(chan_opt_topicrefresh)
7069{
7070 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
7071}
7072
7637f48f 7073static MODCMD_FUNC(chan_opt_resync)
7074{
7075 return channel_multiple_option(chResync, CSFUNC_ARGS);
7076}
7077
d76ed9a9 7078static struct svccmd_list set_shows_list;
7079
7080static void
d77556d0 7081handle_svccmd_unbind(struct svccmd *target, UNUSED_ARG(void *extra)) {
d76ed9a9 7082 unsigned int ii;
7083 for(ii=0; ii<set_shows_list.used; ++ii)
7084 if(target == set_shows_list.list[ii])
7085 set_shows_list.used = 0;
7086}
7087
7088static CHANSERV_FUNC(cmd_set)
7089{
7090 struct svccmd *subcmd;
7091 char buf[MAXLEN];
7092 unsigned int ii;
7093
7094 /* Check if we need to (re-)initialize set_shows_list. */
7095 if(!set_shows_list.used)
7096 {
7097 if(!set_shows_list.size)
7098 {
7099 set_shows_list.size = chanserv_conf.set_shows->used;
7100 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
7101 }
7102 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
7103 {
7104 const char *name = chanserv_conf.set_shows->list[ii];
7105 sprintf(buf, "%s %s", argv[0], name);
7106 subcmd = dict_find(cmd->parent->commands, buf, NULL);
7107 if(!subcmd)
7108 {
7109 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
7110 continue;
7111 }
7112 svccmd_list_append(&set_shows_list, subcmd);
7113 }
7114 }
7115
7116 if(argc < 2)
7117 {
c52639b4 7118 reply("CSMSG_CHANNEL_OPTIONS", channel->name);
cde20a3f 7119 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
7120 reply("CSMSG_BAR");
d76ed9a9 7121 for(ii = 0; ii < set_shows_list.used; ii++)
7122 {
7123 subcmd = set_shows_list.list[ii];
7124 subcmd->command->func(user, channel, 1, argv+1, subcmd);
7125 }
de9510bc 7126 reply("CSMSG_CHANNEL_OPTIONS_END");
c52639b4 7127 return 1;
d76ed9a9 7128 }
7129
7130 sprintf(buf, "%s %s", argv[0], argv[1]);
7131 subcmd = dict_find(cmd->parent->commands, buf, NULL);
7132 if(!subcmd)
7133 {
7134 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
7135 return 0;
7136 }
7137 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
7138 {
7139 reply("CSMSG_NO_ACCESS");
7140 return 0;
7141 }
7142
1136f709 7143 argv[0] = "";
7144 argv[1] = buf;
d76ed9a9 7145 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
7146}
7147
7148static int
7149user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
7150{
7151 struct userData *uData;
7152
7153 uData = GetChannelAccess(channel->channel_info, user->handle_info);
7154 if(!uData)
7155 {
7156 reply("CSMSG_NOT_USER", channel->name);
7157 return 0;
7158 }
7159
7160 if(argc < 2)
7161 {
c52639b4 7162 /* Just show current option value. */
d76ed9a9 7163 }
7164 else if(enabled_string(argv[1]))
7165 {
7166 uData->flags |= mask;
7167 }
7168 else if(disabled_string(argv[1]))
7169 {
7170 uData->flags &= ~mask;
7171 }
7172 else
7173 {
7174 reply("MSG_INVALID_BINARY", argv[1]);
7175 return 0;
7176 }
7177
7178 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
7179 return 1;
7180}
7181
c8ca69a0 7182static MODCMD_FUNC(user_opt_autoop)
d76ed9a9 7183{
7184 struct userData *uData;
7185
7186 uData = GetChannelAccess(channel->channel_info, user->handle_info);
7187 if(!uData)
7188 {
7189 reply("CSMSG_NOT_USER", channel->name);
7190 return 0;
7191 }
90e75ffd 7192 if(uData->access < UL_HALFOP /*channel->channel_info->lvlOpts[lvlGiveOps]*/)
c8ca69a0 7193 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
d76ed9a9 7194 else
c8ca69a0 7195 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
d76ed9a9 7196}
7197
7198static MODCMD_FUNC(user_opt_autoinvite)
7199{
1136f709 7200 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
7201 {
7202 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
7203 }
d76ed9a9 7204 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
7205}
7206
cd25f2e9 7207static MODCMD_FUNC(user_opt_autojoin)
7208{
7209 return user_binary_option("CSMSG_USET_AUTOJOIN", USER_AUTO_JOIN, CSFUNC_ARGS);
7210}
7211
d76ed9a9 7212static MODCMD_FUNC(user_opt_info)
7213{
7214 struct userData *uData;
7215 char *infoline;
7216
7217 uData = GetChannelAccess(channel->channel_info, user->handle_info);
7218
7219 if(!uData)
7220 {
c52639b4 7221 /* If they got past the command restrictions (which require access)
d76ed9a9 7222 * but fail this test, we have some fool with security override on.
7223 */
c52639b4
AS
7224 reply("CSMSG_NOT_USER", channel->name);
7225 return 0;
d76ed9a9 7226 }
7227
7228 if(argc > 1)
7229 {
7230 size_t bp;
7231 infoline = unsplit_string(argv + 1, argc - 1, NULL);
8b9e7d45 7232 if(strlen(infoline) > channel->channel_info->maxsetinfo)
d76ed9a9 7233 {
8b9e7d45 7234 reply("CSMSG_INFOLINE_TOO_LONG", channel->channel_info->maxsetinfo);
d76ed9a9 7235 return 0;
7236 }
7237 bp = strcspn(infoline, "\001");
7238 if(infoline[bp])
7239 {
7240 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
7241 return 0;
7242 }
7243 if(uData->info)
7244 free(uData->info);
7245 if(infoline[0] == '*' && infoline[1] == 0)
7246 uData->info = NULL;
7247 else
7248 uData->info = strdup(infoline);
7249 }
7250 if(uData->info)
7251 reply("CSMSG_USET_INFO", uData->info);
7252 else
7253 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
7254 return 1;
7255}
7256
7257struct svccmd_list uset_shows_list;
7258
7259static CHANSERV_FUNC(cmd_uset)
7260{
7261 struct svccmd *subcmd;
7262 char buf[MAXLEN];
7263 unsigned int ii;
7264
7265 /* Check if we need to (re-)initialize uset_shows_list. */
7266 if(!uset_shows_list.used)
7267 {
7268 char *options[] =
7269 {
cd25f2e9 7270 "AutoOp", "AutoInvite", "AutoJoin", "Info"
d76ed9a9 7271 };
7272
7273 if(!uset_shows_list.size)
7274 {
7275 uset_shows_list.size = ArrayLength(options);
7276 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
7277 }
7278 for(ii = 0; ii < ArrayLength(options); ii++)
7279 {
7280 const char *name = options[ii];
7281 sprintf(buf, "%s %s", argv[0], name);
7282 subcmd = dict_find(cmd->parent->commands, buf, NULL);
7283 if(!subcmd)
7284 {
7285 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
7286 continue;
7287 }
7288 svccmd_list_append(&uset_shows_list, subcmd);
7289 }
7290 }
7291
7292 if(argc < 2)
7293 {
c52639b4
AS
7294 /* Do this so options are presented in a consistent order. */
7295 reply("CSMSG_USER_OPTIONS");
d76ed9a9 7296 for(ii = 0; ii < uset_shows_list.used; ii++)
7297 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
c52639b4 7298 return 1;
d76ed9a9 7299 }
7300
7301 sprintf(buf, "%s %s", argv[0], argv[1]);
7302 subcmd = dict_find(cmd->parent->commands, buf, NULL);
7303 if(!subcmd)
7304 {
7305 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
7306 return 0;
7307 }
7308
7309 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
7310}
7311
7312static CHANSERV_FUNC(cmd_giveownership)
7313{
7314 struct handle_info *new_owner_hi;
1136f709 7315 struct userData *new_owner;
7316 struct userData *curr_user;
7317 struct userData *invoker;
d76ed9a9 7318 struct chanData *cData = channel->channel_info;
7319 struct do_not_register *dnr;
82f37c08 7320 struct giveownership *giveownership;
1136f709 7321 const char *confirm;
82f37c08 7322 unsigned int force, override;
7323 unsigned short co_access, new_owner_old_access;
09a3057c 7324 char transfer_reason[MAXLEN];
d76ed9a9 7325
7326 REQUIRE_PARAMS(2);
7327 curr_user = GetChannelAccess(cData, user->handle_info);
7328 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
82f37c08 7329
7330 struct userData *uData = _GetChannelUser(channel->channel_info, user->handle_info, 1, 0);
7331 override = ((cmd->effective_flags & MODCMD_REQUIRE_CHANUSER)
7332 && (uData->access > 500)
7333 && (!(uData = _GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
7334 || uData->access < 500));
7335
7336
d76ed9a9 7337 if(!curr_user || (curr_user->access != UL_OWNER))
7338 {
7339 struct userData *owner = NULL;
7340 for(curr_user = channel->channel_info->users;
7341 curr_user;
7342 curr_user = curr_user->next)
7343 {
7344 if(curr_user->access != UL_OWNER)
7345 continue;
7346 if(owner)
7347 {
7348 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
7349 return 0;
7350 }
7351 owner = curr_user;
7352 }
7353 curr_user = owner;
7354 }
1136f709 7355 else if(!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
a32da4c7 7356 {
7357 char delay[INTERVALLEN];
7358 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
7359 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
7360 return 0;
7361 }
5678501c 7362 if (!curr_user) {
7363 reply("CSMSG_NO_OWNER", channel->name);
7364 return 0;
7365 }
d76ed9a9 7366 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
7367 return 0;
7368 if(new_owner_hi == user->handle_info)
7369 {
7370 reply("CSMSG_NO_TRANSFER_SELF");
7371 return 0;
7372 }
7373 new_owner = GetChannelAccess(cData, new_owner_hi);
7374 if(!new_owner)
7375 {
0d16e639 7376 if(force)
7377 {
1136f709 7378 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL, 0);
0d16e639 7379 }
7380 else
7381 {
7382 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
7383 return 0;
7384 }
d76ed9a9 7385 }
7386 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
7387 {
7388 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
7389 return 0;
7390 }
7391 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
7392 if(!IsHelping(user))
7393 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
7394 else
1117fc5a 7395 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
d76ed9a9 7396 return 0;
7397 }
82f37c08 7398
1136f709 7399 invoker = GetChannelUser(cData, user->handle_info);
7400 if(invoker->access <= UL_OWNER)
7401 {
7402 confirm = make_confirmation_string(curr_user);
7403 if((argc < 3) || strcmp(argv[2], confirm))
7404 {
7405 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
7406 return 0;
7407 }
7408 }
7409
82f37c08 7410 new_owner_old_access = new_owner->access;
d76ed9a9 7411 if(new_owner->access >= UL_COOWNER)
7412 co_access = new_owner->access;
7413 else
7414 co_access = UL_COOWNER;
7415 new_owner->access = UL_OWNER;
7416 if(curr_user)
7417 curr_user->access = co_access;
a32da4c7 7418 cData->ownerTransfer = now;
82f37c08 7419
7420 giveownership = calloc(1, sizeof(*giveownership));
7421 giveownership->issued = now;
8ce9df05 7422 giveownership->old_owner = strdup(curr_user->handle->handle);
7423 giveownership->target = strdup(new_owner_hi->handle);
82f37c08 7424 giveownership->target_access = new_owner_old_access;
7425 if(override)
7426 {
7427 if(argc > (2 + force))
7428 {
7429 unsplit_string(argv + 2 + force, argc - 2 - force, transfer_reason);
7430 giveownership->reason = strdup(transfer_reason);
7431 }
7432 giveownership->staff_issuer = strdup(user->handle_info->handle);
7433 }
7434
7435 giveownership->previous = channel->channel_info->giveownership;
7436 channel->channel_info->giveownership = giveownership;
7437
d76ed9a9 7438 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
09a3057c 7439 global_message_args(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, "CSMSG_OWNERSHIP_TRANSFERRED",
7440 channel->name, new_owner_hi->handle, user->handle_info->handle);
d76ed9a9 7441 return 1;
7442}
7443
b10abdb2 7444static void
7445chanserv_expire_user_suspension(void *data)
7446{
7447 struct userData *target = data;
7448
c52639b4
AS
7449 target->expires = 0;
7450 target->flags &= ~USER_SUSPENDED;
b10abdb2 7451}
7452
d76ed9a9 7453static CHANSERV_FUNC(cmd_suspend)
7454{
7455 struct handle_info *hi;
1136f709 7456 struct userData *actor, *real_actor, *target;
7457 unsigned int override = 0;
b10abdb2 7458 time_t expiry;
d76ed9a9 7459
b10abdb2 7460 REQUIRE_PARAMS(3);
d76ed9a9 7461 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
1136f709 7462 actor = GetChannelUser(channel->channel_info, user->handle_info);
7463 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
d76ed9a9 7464 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
7465 {
7466 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
7467 return 0;
7468 }
1136f709 7469 if(target->access >= actor->access)
d76ed9a9 7470 {
7471 reply("MSG_USER_OUTRANKED", hi->handle);
7472 return 0;
7473 }
7474 if(target->flags & USER_SUSPENDED)
7475 {
7476 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
7477 return 0;
7478 }
7479 if(target->present)
7480 {
7481 target->present = 0;
7482 target->seen = now;
7483 }
b10abdb2 7484 if(!strcmp(argv[2], "0"))
7485 expiry = 0;
7486 else
7487 {
7488 unsigned int duration;
7489 if(!(duration = ParseInterval(argv[2])))
7490 {
7491 reply("MSG_INVALID_DURATION", argv[2]);
7492 return 0;
7493 }
7494 expiry = now + duration;
7495 }
7496
c52639b4 7497 target->expires = expiry;
b10abdb2 7498
c52639b4
AS
7499 if(target->expires)
7500 timeq_add(target->expires, chanserv_expire_user_suspension, target);
b10abdb2 7501
1136f709 7502 if(!real_actor || target->access >= real_actor->access)
7503 override = CMD_LOG_OVERRIDE;
d76ed9a9 7504 target->flags |= USER_SUSPENDED;
7505 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
1136f709 7506 return 1 | override;
d76ed9a9 7507}
7508
7509static CHANSERV_FUNC(cmd_unsuspend)
7510{
7511 struct handle_info *hi;
1136f709 7512 struct userData *actor = NULL, *real_actor = NULL, *target;
7513 unsigned int override = 0;
d76ed9a9 7514
7515 REQUIRE_PARAMS(2);
c52639b4
AS
7516 if(!(hi = modcmd_get_handle_info(user, argv[1])))
7517 return 0;
aa8c38ad 7518 actor = GetChannelUser(channel->channel_info, user->handle_info);
1136f709 7519 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
d76ed9a9 7520 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
7521 {
7522 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
7523 return 0;
7524 }
1136f709 7525 if(target->access >= actor->access)
d76ed9a9 7526 {
7527 reply("MSG_USER_OUTRANKED", hi->handle);
7528 return 0;
7529 }
7530 if(!(target->flags & USER_SUSPENDED))
7531 {
7532 reply("CSMSG_NOT_SUSPENDED", hi->handle);
7533 return 0;
7534 }
1136f709 7535 if(!real_actor || target->access >= real_actor->access)
7536 override = CMD_LOG_OVERRIDE;
d76ed9a9 7537 target->flags &= ~USER_SUSPENDED;
a32da4c7 7538 scan_user_presence(target, NULL);
b10abdb2 7539 timeq_del(target->expires, chanserv_expire_user_suspension, target, 0);
d76ed9a9 7540 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
1136f709 7541 return 1 | override;
d76ed9a9 7542}
7543
7544static MODCMD_FUNC(cmd_deleteme)
7545{
7546 struct handle_info *hi;
7547 struct userData *target;
7548 const char *confirm_string;
7549 unsigned short access;
7550 char *channel_name;
7551
7552 hi = user->handle_info;
7553 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
7554 {
7555 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
7556 return 0;
7557 }
7558 if(target->access == UL_OWNER)
7559 {
7560 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
7561 return 0;
7562 }
7563 confirm_string = make_confirmation_string(target);
7564 if((argc < 2) || strcmp(argv[1], confirm_string))
7565 {
7566 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
7567 return 0;
7568 }
7569 access = target->access;
7570 channel_name = strdup(channel->name);
7571 del_channel_user(target, 1);
7572 reply("CSMSG_DELETED_YOU", access, channel_name);
7573 free(channel_name);
7574 return 1;
7575}
7576
7577static void
7578chanserv_refresh_topics(UNUSED_ARG(void *data))
7579{
1136f709 7580 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
d76ed9a9 7581 struct chanData *cData;
7582 char opt;
7583
7584 for(cData = channelList; cData; cData = cData->next)
7585 {
7586 if(IsSuspended(cData))
7587 continue;
7588 opt = cData->chOpts[chTopicRefresh];
7589 if(opt == 'n')
7590 continue;
7591 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
7592 continue;
7593 if(cData->topic)
7fda2b52 7594 SetChannelTopic(cData->channel, chanserv, chanserv, cData->topic, 1);
d76ed9a9 7595 cData->last_refresh = refresh_num;
7596 }
7597 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
7598}
7599
7637f48f 7600static void
7601chanserv_auto_resync(UNUSED_ARG(void *data))
7602{
1136f709 7603 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
7637f48f 7604 struct chanData *cData;
7605 char opt;
7606
7607 for(cData = channelList; cData; cData = cData->next)
7608 {
c52639b4
AS
7609 if(IsSuspended(cData))
7610 continue;
7637f48f 7611 opt = cData->chOpts[chResync];
c52639b4
AS
7612 if(opt == 'n')
7613 continue;
7614 if((refresh_num - cData->last_resync) < (unsigned int)(1 << (opt - '1')))
7615 continue;
7616 resync_channel(cData->channel);
7637f48f 7617 cData->last_resync = refresh_num;
7618 }
7619 timeq_add(now + chanserv_conf.refresh_period, chanserv_auto_resync, NULL);
7620}
7621
d76ed9a9 7622static CHANSERV_FUNC(cmd_unf)
7623{
7624 if(channel)
7625 {
7626 char response[MAXLEN];
7627 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
7628 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
7629 irc_privmsg(cmd->parent->bot, channel->name, response);
7630 }
7631 else
7632 reply("CSMSG_UNF_RESPONSE");
7633 return 1;
7634}
7635
7636static CHANSERV_FUNC(cmd_ping)
7637{
7638 if(channel)
7639 {
7640 char response[MAXLEN];
7641 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
7642 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
7643 irc_privmsg(cmd->parent->bot, channel->name, response);
7644 }
7645 else
7646 reply("CSMSG_PING_RESPONSE");
7647 return 1;
7648}
7649
7650static CHANSERV_FUNC(cmd_wut)
7651{
7652 if(channel)
7653 {
7654 char response[MAXLEN];
7655 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
7656 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
7657 irc_privmsg(cmd->parent->bot, channel->name, response);
7658 }
7659 else
7660 reply("CSMSG_WUT_RESPONSE");
7661 return 1;
7662}
7663
87708af4 7664static CHANSERV_FUNC(cmd_roulette)
7665{
c52639b4
AS
7666 if(channel)
7667 {
b404335b 7668 struct chanData *cData = channel->channel_info;
87708af4 7669
c52639b4
AS
7670 if (cData)
7671 {
7672 if (cData->roulette_chamber)
7673 {
7674 DelUser(user, chanserv, 1, "BANG - Don't stuff bullets into a loaded gun");
b404335b 7675 return 1;
7676 }
7677
7678 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_ROULETTE_LOADS");
7679 cData->roulette_chamber = 1 + rand() % 6;
7680 }
7681 }
7682
7683 return 1;
7684}
7685static CHANSERV_FUNC(cmd_shoot)
7686{
c52639b4
AS
7687 if(channel)
7688 {
b404335b 7689 struct chanData *cData = channel->channel_info;
7690
c52639b4
AS
7691 if (cData->roulette_chamber <= 0)
7692 {
a6fa0035 7693 struct service *service;
c52639b4
AS
7694 if ((service = service_find(chanserv->nick)))
7695 {
a6fa0035 7696 reply("CSMSG_ROULETTE_NEW", service->trigger);
7697 }
87708af4 7698 return 1;
7699 }
7700
b404335b 7701 cData->roulette_chamber--;
7702
c52639b4
AS
7703 if (cData->roulette_chamber == 0)
7704 {
b404335b 7705 reply("CSMSG_ROULETTE_BANG");
7706 reply("CSMSG_ROULETTE_BETTER_LUCK", user->nick);
fba880b8 7707 DelUser(user, chanserv, 1, "BANG!!!!");
c52639b4
AS
7708 }
7709 else
b404335b 7710 reply("CSMSG_ROULETTE_CLICK");
87708af4 7711 }
b404335b 7712
87708af4 7713 return 1;
7714}
b404335b 7715
7716static void
7717chanserv_remove_abuse(void *data)
87708af4 7718{
c64a32cb 7719 char *remnick = data;
7720 struct userNode *user;
7721 /* sometimes the clone was killed and maybe even the user took their nick back
7722 * (ie, an oper) so dont kill them here after all unless they are local. */
7723 if( (user = GetUserH(remnick)) )
7724 if(IsLocal(user) )
7725 DelUser(user, NULL, 1, "");
b404335b 7726}
87708af4 7727
e03ec3dc 7728int lamepart(struct userNode *nick) {
7729 struct modeNode *mn;
7730 unsigned int count, n;
7731
c52639b4
AS
7732 for (n=count=0; n<nick->channels.used; n++)
7733 {
e03ec3dc 7734 mn = nick->channels.list[n];
7735 irc_svspart(chanserv, nick, mn->channel);
7736 }
7737
7738 return 0;
7739}
7740
b404335b 7741static CHANSERV_FUNC(cmd_spin)
7742{
7743 if(!channel)
87708af4 7744 return 1;
b404335b 7745
4cb36ef0 7746 int type = 0, lamep = 1;
e03ec3dc 7747 char *tstr;
7748
7749 tstr = conf_get_data("server/type", RECDB_QSTRING);
7750 if(tstr) {
7751 type = atoi(tstr);
7752 if (type > 6)
7753 lamep = 0;
7754 }
7755
b404335b 7756
7757 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_WHEEL1", user->nick);
7758 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_WHEEL2");
7759 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_WHEEL3");
7760
c52639b4
AS
7761 if(chanserv_conf.wheel->used < 1)
7762 {
5e6460e4 7763 /* wheel actions not defined! eek */
7764 return 1;
7765 }
7766
7767 const char *wheel = chanserv_conf.wheel->list[ (int) ( (chanserv_conf.wheel->used) * (rand() / (RAND_MAX + 1.0)) ) ];
7768 if(!wheel && *wheel)
7769 return 1;
7770
5e6460e4 7771 /* connection reset by peer */
c52639b4
AS
7772 if (!strcasecmp(wheel, "peer"))
7773 {
5e6460e4 7774 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_PEER");
e91c6c1e 7775 if (type < 7)
7776 irc_kill(chanserv, user, "Connection reset by peer");
7777 else
5e6460e4 7778 irc_svsquit(chanserv, user, "Connection reset by peer");
b404335b 7779 }
5e6460e4 7780 /* part all channels */
c52639b4
AS
7781 else if (!strcasecmp(wheel, "partall"))
7782 {
5e6460e4 7783 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_PARTALL");
e03ec3dc 7784 if (lamep)
7785 lamepart(user);
7786 else
7787 sputsock("%s SJ %s 0 "FMT_TIME_T, self->numeric, user->numeric, now);
87708af4 7788 }
5e6460e4 7789 /* random time gline */
c52639b4
AS
7790 else if (!strcasecmp(wheel, "gline"))
7791 {
38c4bf0a 7792 char target[HOSTLEN + 3];
b404335b 7793 int wtime = 120 + rand() % 600;
87708af4 7794
5e6460e4 7795 strcpy(target, "*@");
7796 strcat(target, user->hostname);
7797 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_GLINE");
87708af4 7798
995043b4 7799 gline_add(chanserv->nick, target, wtime, "Reward for spinning the wheel of misfortune!", now, 1, 0);
b404335b 7800 }
5e6460e4 7801 /* random shun */
c52639b4
AS
7802 else if (!strcasecmp(wheel, "shun"))
7803 {
38c4bf0a 7804 char target[HOSTLEN + 3];
b404335b 7805 int wtime = 120 + rand() % 600;
87708af4 7806
5e6460e4 7807 strcpy(target, "*@");
7808 strcat(target, user->hostname);
7809 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_SHUN");
b404335b 7810
995043b4 7811 shun_add(chanserv->nick, target, wtime, "Reward for spinning the wheel of misfortune!", now, 1);
b404335b 7812 }
5e6460e4 7813 /* absolutely nothing */
c52639b4
AS
7814 else if (!strcasecmp(wheel, "nothing"))
7815 {
5e6460e4 7816 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_NOTHING");
b404335b 7817 }
5e6460e4 7818 /* join random chans and part em several times */
c52639b4
AS
7819 else if (!strcasecmp(wheel, "randjoin"))
7820 {
b404335b 7821 int complete = 0;
7822 int rndchans = 0;
7823 int chango = 0;
7824 int roundz0r = 0;
7825
5e6460e4 7826 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_RANDJOIN");
c52639b4
AS
7827 while(complete != 1)
7828 {
7829 if (rndchans != 15)
7830 {
b404335b 7831 chango = 120 + rand() % 600;
a837dcf5 7832 sputsock("%s SJ %s #%d "FMT_TIME_T, self->numeric, user->numeric, chango, now);
b404335b 7833 rndchans++;
c52639b4
AS
7834 }
7835 else
7836 {
7837 if (roundz0r != 1)
7838 {
e03ec3dc 7839 if (lamep)
7840 lamepart(user);
7841 else
7842 sputsock("%s SJ %s 0 "FMT_TIME_T, self->numeric, user->numeric, now);
b404335b 7843 roundz0r = 1;
7844 rndchans = 0;
c52639b4
AS
7845 }
7846 else
7847 {
e03ec3dc 7848 if (lamep)
7849 lamepart(user);
7850 else
7851 sputsock("%s SJ %s 0 "FMT_TIME_T, self->numeric, user->numeric, now);
7852 complete = 1;
b404335b 7853 }
7854 }
7855 }
7856 }
5e6460e4 7857 /* abuse line added to /whois */
c52639b4
AS
7858 else if (!strcasecmp(wheel, "abusewhois"))
7859 {
5e6460e4 7860 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_ABUSEWHOIS");
b404335b 7861 irc_swhois(chanserv, user, "is being defecated on by services");
7862 }
5e6460e4 7863 /* kick from each channel your in */
c52639b4
AS
7864 else if (!strcasecmp(wheel, "kickall"))
7865 {
b404335b 7866 unsigned int count, n;
7867 struct modeNode *mn;
7868
5e6460e4 7869 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_KICKALL");
b404335b 7870
7871 for (n=count=0; n<user->channels.used; n++) {
7872 mn = user->channels.list[n];
7873 irc_kick(chanserv, user, mn->channel, "Reward for spinning the wheel of misfortune!");
7874 }
7875 }
5e6460e4 7876 /* random nick change */
c52639b4
AS
7877 else if (!strcasecmp(wheel, "nickchange"))
7878 {
5e6460e4 7879 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_NICKCHANGE");
b404335b 7880
7881 char *oldnick = NULL;
7882 char *oldident = NULL;
b404335b 7883 char abusednick[NICKLEN] = "";
5e6460e4 7884 int abusednum = 1 + (int) (10000.0 * (rand() / (RAND_MAX + 1.0)));
b404335b 7885 struct userNode *clone;
7886
7887 oldnick = strdup(user->nick);
7888 oldident = strdup(user->ident);
b404335b 7889
5e6460e4 7890 //snprintf(abusednick, NICKLEN, "Abused%d", abusednum+(1 + rand() % 120));
c52639b4
AS
7891 while (1)
7892 {
b404335b 7893 snprintf(abusednick, NICKLEN, "Abused%d", abusednum+(1 + rand() % 120));
5e6460e4 7894 log_module(MAIN_LOG, LOG_DEBUG, "Abused Nick: %s, Client Nick: %s", abusednick, user->nick);
7895 if(!GetUserH(abusednick))
b404335b 7896 break;
7897 }
7898
cf8bedff 7899 SVSNickChange(user, abusednick);
b404335b 7900 irc_svsnick(chanserv, user, abusednick);
dc382fd2 7901 clone = AddLocalUser(oldnick, oldident, "abused.by.wheel.of.misfortune", "I got abused by the wheel of misfortune :D", "+i");
c64a32cb 7902 timeq_add(now + 300, chanserv_remove_abuse, clone->nick);
b404335b 7903 }
5e6460e4 7904 /* kill */
c52639b4
AS
7905 else if (!strcasecmp(wheel, "kill"))
7906 {
5e6460e4 7907 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_KILL");
b404335b 7908
c52639b4 7909 DelUser(user, chanserv, 1, "Reward for spinning the wheel of misfortune!");
8deb31d3 7910 //irc_kill(chanserv, user, "Reward for spinning the wheel of misfortune!");
b404335b 7911 }
5e6460e4 7912 /* service ignore */
c52639b4
AS
7913 else if (!strcasecmp(wheel, "svsignore"))
7914 {
7915 int gagged;
7916 int ignoretime = 0;
38c4bf0a 7917 char target[HOSTLEN + 13];
b404335b 7918
5e6460e4 7919 if(IsOper(user)) {
7920 /* we cant gag opers, so just verbally abuse them */
7921 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_SVSIGNORE_OPER");
7922 return 1;
7923 }
7924 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_SVSIGNORE");
b404335b 7925
5e6460e4 7926 strcpy(target, "*!*@");
7927 strcat(target, user->hostname);
b404335b 7928 ignoretime = now + (1 + rand() % 120);
7929
7930 gagged = gag_create(target, "wheelofabuse", "Reward for spinning the wheel of misfortune!", ignoretime);
7931 }
5e6460e4 7932 /* kick and ban from each channel your in */
c52639b4
AS
7933 else if (!strcasecmp(wheel, "kickbanall"))
7934 {
b404335b 7935 unsigned int count, n;
7936 struct modeNode *mn;
38c4bf0a 7937 //char ban[HOSTLEN + 1];
b404335b 7938
5e6460e4 7939 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_KICKBANALL");
b404335b 7940
5e6460e4 7941 //snprintf(ban, sizeof(ban), "*!*@%s", user->hostname);
c52639b4
AS
7942 for (n=count=0; n<user->channels.used; n++)
7943 {
5e6460e4 7944 struct mod_chanmode *change;
c52639b4
AS
7945/* struct banData *bData; */
7946 unsigned int exists;
7947/* int duration = 300; */
7948 char *ban;
92f63dd4 7949
c52639b4 7950 ban = generate_hostmask(user, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT|GENMASK_USENICK);
92f63dd4 7951
c52639b4 7952 log_module(MAIN_LOG, LOG_DEBUG, "Generated ban %s", ban);
b404335b 7953 mn = user->channels.list[n];
c52639b4
AS
7954 if(mn->channel->banlist.used >= MAXBANS)
7955 {
7956 reply("CSMSG_BANLIST_FULL", mn->channel->name);
7957 free(ban);
7958 continue;
5e6460e4 7959 }
7960
c52639b4 7961/* bData = add_channel_ban(mn->channel->channel_info, ban, chanserv->nick, now, now, now + duration, "Reward for spinning the wheel of misfortune!"); */
92f63dd4 7962
c52639b4
AS
7963 change = mod_chanmode_alloc(1);
7964 change->args[0].mode = MODE_REMOVE|MODE_CHANOP|MODE_HALFOP|MODE_VOICE;
7965 change->args[0].u.member = GetUserMode(mn->channel, user);
7966 change->argc = 1;
92f63dd4 7967
c52639b4
AS
7968 mod_chanmode_announce(chanserv, mn->channel, change);
7969 mod_chanmode_free(change);
92f63dd4 7970
c52639b4
AS
7971 exists = ChannelBanExists(mn->channel, ban);
7972 if(!exists) {
7973 change = mod_chanmode_alloc(1);
7974 change->args[0].mode = MODE_BAN;
7975 change->args[0].u.hostmask = ban;
7976 change->argc = 1;
7977 mod_chanmode_announce(chanserv, mn->channel, change);
7978 mod_chanmode_free(change);
92f63dd4 7979 }
7980
7981 if(exists) {
c52639b4
AS
7982 reply("CSMSG_REDUNDANT_BAN", ban, mn->channel->name);
7983 free(ban);
7984 }
5e6460e4 7985
b404335b 7986 irc_kick(chanserv, user, mn->channel, "Reward for spinning the wheel of misfortune!");
7987 }
7988 }
c52639b4
AS
7989 else
7990 {
5e6460e4 7991 send_target_message(1, channel->name, chanserv, "CSMSG_SPIN_UNKNOWN", wheel);
7992 }
b404335b 7993
5e6460e4 7994 return 1;
87708af4 7995}
7996
240a3274 7997#ifdef lame8ball
d76ed9a9 7998static CHANSERV_FUNC(cmd_8ball)
7999{
8000 unsigned int i, j, accum;
8001 const char *resp;
8002
8003 REQUIRE_PARAMS(2);
8004 accum = 0;
8005 for(i=1; i<argc; i++)
8006 for(j=0; argv[i][j]; j++)
8007 accum = (accum << 5) - accum + toupper(argv[i][j]);
8008 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
8009 if(channel)
8010 {
8011 char response[MAXLEN];
8012 sprintf(response, "\ 2%s\ 2: %s", user->nick, resp);
8013 irc_privmsg(cmd->parent->bot, channel->name, response);
8014 }
8015 else
8016 send_message_type(4, user, cmd->parent->bot, "%s", resp);
8017 return 1;
8018}
8019
240a3274 8020#else /* Use cool 8ball instead */
8021
8022void eightball(char *outcome, int method, unsigned int seed)
8023{
8024 int answer = 0;
8025
8026#define NUMOFCOLORS 18
c52639b4
AS
8027 char ballcolors[50][50] = {
8028 "blue", "red", "green", "yellow",
240a3274 8029 "white", "black", "grey", "brown",
8030 "yellow", "pink", "purple", "orange", "teal", "burgandy",
8031 "fuchsia","turquoise","magenta", "cyan"};
8032#define NUMOFLOCATIONS 50
8033 char balllocations[50][55] = {
c52639b4 8034 "Locke's house", "Oregon", "California", "Indiana", "Canada",
240a3274 8035 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
8036 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
8037 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
8038 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
8039 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
8040 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
8041 "your bra", "your hair", "your bed", "the couch", "the wall",
c52639b4
AS
8042 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
8043 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
240a3274 8044#define NUMOFPREPS 15
8045 char ballpreps[50][50] = {
c52639b4
AS
8046 "Near", "Somewhere near", "In", "In", "In",
8047 "In", "Hiding in", "Under", "Next to", "Over",
8048 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
240a3274 8049#define NUMOFNUMS 34
8050 char ballnums[50][50] = {
8051 "A hundred", "A thousand", "A few", "42",
8052 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
8053 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
8054 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
8055 };
8056#define NUMOFMULTS 8
8057 char ballmults[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
8058
8059 /* Method:
8060 * 0: normal (Not used in x3)
8061 * 1: color
8062 * 2: where is
8063 * 3: how many
8064 */
8065
8066 srand(seed);
8067 if (method == 1) /* A Color */
8068 {
8069 char tmp[MAXLEN];
8070
8071 answer = (rand() % 12); /* Make sure this is the # of entries */
8072 switch(answer)
8073 {
8074 case 0: strcpy(tmp, "Very bright %s, I'd say.");
8075 break;
8076 case 1: strcpy(tmp, "Sort of a light %s color.");
8077 break;
8078 case 2: strcpy(tmp, "Dark and dreary %s.");
8079 break;
8080 case 3: strcpy(tmp, "Quite a pale shade of %s.");
8081 break;
8082 case 4: strcpy(tmp, "A gross kind of mucky %s.");
8083 break;
8084 case 5: strcpy(tmp, "Brilliant whiteish %s.");
c52639b4 8085 break;
338a82b5 8086 case 6: case 7: case 8: case 9: strcpy(tmp, "%s.");
8087 break;
8088 case 10: strcpy(tmp, "Solid %s.");
8089 break;
8090 case 11: strcpy(tmp, "Transparent %s.");
c52639b4 8091 break;
240a3274 8092 default: strcpy(outcome, "An invalid random number was generated.");
8093 return;
8094 }
8095 sprintf(outcome, tmp, ballcolors[rand() % NUMOFCOLORS]);
8096 return;
8097 }
8098 else if (method == 2) /* Location */
8099 {
8100 sprintf(outcome, "%s %s.", ballpreps[rand() % NUMOFPREPS], balllocations[rand() % NUMOFLOCATIONS]);
8101 }
8102 else if (method == 3) /* Number of ___ */
8103 {
8104 sprintf(outcome, "%s%s.", ballnums[rand() % NUMOFNUMS], ballmults[rand() % NUMOFMULTS]);
8105 }
8106 else
8107 {
8108 //Debug(DBGWARNING, "Error in 8ball.");
8109 }
8110 return;
8111}
8112
8113static CHANSERV_FUNC(cmd_8ball)
8114{
c52639b4
AS
8115 char *word1, *word2, *word3;
8116 static char eb[MAXLEN];
8117 unsigned int accum, i, j;
240a3274 8118
c52639b4
AS
8119 REQUIRE_PARAMS(2);
8120 accum = 0;
8121 for(i=1; i<argc; i++)
8122 for(j=0; argv[i][j]; j++)
8123 accum = (accum << 5) - accum + toupper(argv[i][j]);
240a3274 8124
c52639b4
AS
8125 accum += time(NULL)/3600;
8126 word1 = argv[1];
8127 word2 = argc>2?argv[2]:"";
8128 word3 = argc>3?argv[3]:"";
240a3274 8129
8130/*** COLOR *****/
c52639b4
AS
8131 if((word2) && strcasecmp(word1, "what") == 0 && ((strcasecmp(word2, "color") == 0) || (strcasecmp(word2, "colour") == 0)))
8132 eightball(eb, 1, accum);
8133 else if((word3) && strcasecmp(word1, "what's") == 0 && strcasecmp(word2, "the") == 0 && ((strcasecmp(word2, "color") == 0) || (strcasecmp(word2, "colour") == 0)))
8134 eightball(eb, 1, accum);
8135 else if((word3) && strcasecmp(word1, "whats") == 0 && strcasecmp(word2, "the") == 0 && ((strcasecmp(word2, "color") == 0) || (strcasecmp(word2, "colour") == 0)))
8136 eightball(eb, 1, accum);
240a3274 8137/*** LOCATION *****/
c52639b4 8138 else if(
240a3274 8139 (
8140 word2 &&
8141 (
8142 (strcasecmp(word1, "where") == 0) &&
8143 (strcasecmp(word2, "is") == 0)
8144 )
8145 ) ||
8146 (
8147 strcasecmp(word1, "where's") == 0
8148 )
8149 )
8150 eightball(eb, 2, accum);
8151/*** NUMBER *****/
c52639b4
AS
8152 else if((word2) && strcasecmp(word1, "how") == 0 && strcasecmp(word2, "many") == 0)
8153 eightball(eb, 3, accum);
240a3274 8154/*** GENERIC *****/
c52639b4
AS
8155 else
8156 {
8157 /* Generic 8ball question.. so pull from x3.conf srvx style */
8158 const char *resp;
8159
8160 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
8161 if(channel)
8162 {
8163 char response[MAXLEN];
8164 sprintf(response, "\002%s\002: %s", user->nick, resp);
8165 irc_privmsg(cmd->parent->bot, channel->name, response);
8166 }
8167 else
8168 send_message_type(4, user, cmd->parent->bot, "%s", resp);
8169 return 1;
240a3274 8170 }
8171
8172 if(channel)
8173 {
8174 char response[MAXLEN];
8175 sprintf(response, "\002%s\002: %s", user->nick, eb);
8176 irc_privmsg(cmd->parent->bot, channel->name, response);
8177 }
8178 else
8179 send_message_type(4, user, cmd->parent->bot, "%s", eb);
8180 return 1;
8181}
8182#endif
8183
d76ed9a9 8184static CHANSERV_FUNC(cmd_d)
8185{
8186 unsigned long sides, count, modifier, ii, total;
8187 char response[MAXLEN], *sep;
8188 const char *fmt;
8189
8190 REQUIRE_PARAMS(2);
8191 if((count = strtoul(argv[1], &sep, 10)) < 1)
8192 goto no_dice;
8193 if(sep[0] == 0)
8194 {
8195 if(count == 1)
8196 goto no_dice;
8197 sides = count;
8198 count = 1;
8199 modifier = 0;
8200 }
8201 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
8202 && (sides = strtoul(sep+1, &sep, 10)) > 1)
8203 {
8204 if(sep[0] == 0)
8205 modifier = 0;
8206 else if((sep[0] == '-') && isdigit(sep[1]))
8207 modifier = strtoul(sep, NULL, 10);
8208 else if((sep[0] == '+') && isdigit(sep[1]))
8209 modifier = strtoul(sep+1, NULL, 10);
8210 else
8211 goto no_dice;
8212 }
8213 else
8214 {
8215 no_dice:
8216 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
8217 return 0;
8218 }
8219 if(count > 10)
8220 {
8221 reply("CSMSG_BAD_DICE_COUNT", count, 10);
8222 return 0;
8223 }
8224 for(total = ii = 0; ii < count; ++ii)
8225 total += (rand() % sides) + 1;
8226 total += modifier;
8227
8228 if((count > 1) || modifier)
8229 {
8230 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
8231 sprintf(response, fmt, total, count, sides, modifier);
8232 }
8233 else
8234 {
8235 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
8236 sprintf(response, fmt, total, sides);
8237 }
8238 if(channel)
8239 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
8240 else
8241 send_message_type(4, user, cmd->parent->bot, "%s", response);
8242 return 1;
8243}
8244
8245static CHANSERV_FUNC(cmd_huggle)
8246{
8247 /* CTCP must be via PRIVMSG, never notice */
8248 if(channel)
8249 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
8250 else
8251 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
8252 return 1;
8253}
8254
e5a8f7cd 8255static CHANSERV_FUNC(cmd_calc)
8256{
8257 char response[MAXLEN];
8258
8259 REQUIRE_PARAMS(2);
8260 do_math(response, unsplit_string(argv + 1, argc - 1, NULL));
8261
8262 if(channel)
d8cf9c21 8263 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
e5a8f7cd 8264 else
8265 send_message_type(4, user, cmd->parent->bot, "%s", response);
8266 return 1;
8267}
8268
33741441 8269static CHANSERV_FUNC(cmd_reply)
8270{
8271
8272 REQUIRE_PARAMS(2);
8273 unsplit_string(argv + 1, argc - 1, NULL);
8274
8275 if(channel)
8276 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, unsplit_string(argv + 1, argc - 1, NULL));
8277 else
8278 send_message_type(4, user, cmd->parent->bot, "%s", unsplit_string(argv + 1, argc - 1, NULL));
8279 return 1;
8280}
8281
d76ed9a9 8282static void
8283chanserv_adjust_limit(void *data)
8284{
8285 struct mod_chanmode change;
8286 struct chanData *cData = data;
8287 struct chanNode *channel = cData->channel;
8288 unsigned int limit;
8289
8290 if(IsSuspended(cData))
8291 return;
8292
8293 cData->limitAdjusted = now;
8294 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
8295 if(cData->modes.modes_set & MODE_LIMIT)
8296 {
8297 if(limit > cData->modes.new_limit)
8298 limit = cData->modes.new_limit;
8299 else if(limit == cData->modes.new_limit)
8300 return;
8301 }
8302
8303 mod_chanmode_init(&change);
8304 change.modes_set = MODE_LIMIT;
8305 change.new_limit = limit;
8306 mod_chanmode_announce(chanserv, channel, &change);
8307}
8308
8309static void
157ce145 8310handle_new_channel(struct chanNode *channel, UNUSED_ARG(void *extra))
d76ed9a9 8311{
8312 struct chanData *cData;
8313
8314 if(!(cData = channel->channel_info))
8315 return;
8316
8317 if(cData->modes.modes_set || cData->modes.modes_clear)
8318 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
8319
8320 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7fda2b52 8321 SetChannelTopic(channel, chanserv, chanserv, channel->channel_info->topic, 1);
d76ed9a9 8322}
8323
5aa400d2 8324int
8325trace_check_bans(struct userNode *user, struct chanNode *chan)
8326{
5aa400d2 8327 struct banData *bData;
27fa6acf 8328 struct mod_chanmode *change;
5aa400d2 8329
27fa6acf 8330 change = find_matching_bans(&chan->banlist, user, NULL);
8331 if (change)
8332 return 1;
8333
27fa6acf 8334 /* lamer list */
1e993296 8335 if (chan->channel_info) {
8336 for(bData = chan->channel_info->bans; bData; bData = bData->next) {
5aa400d2 8337
277ad996 8338 if(!user_matches_glob(user, bData->mask, MATCH_USENICK, 0))
1e993296 8339 continue;
8340
8341 if(bData)
8342 return 1;
5aa400d2 8343 }
8344 }
8345
8346 return 0;
8347}
8348
c99dcaf6 8349int
1b6b5e2f 8350check_bans(struct userNode *user, const char *channel)
c99dcaf6 8351{
8352 struct chanNode *chan;
8353 struct mod_chanmode change;
8354 struct chanData *cData;
8355 struct banData *bData;
8356
8357 if (!(chan = GetChannel(channel)))
8358 return 0;
8359
8360 if(!(cData = chan->channel_info))
8361 return 0;
8362
1b6b5e2f 8363 mod_chanmode_init(&change);
8364 change.argc = 1;
8365
c99dcaf6 8366 if(chan->banlist.used < MAXBANS)
8367 {
8368 /* Not joining through a ban. */
8369 for(bData = cData->bans;
277ad996 8370 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK, 0);
c99dcaf6 8371 bData = bData->next);
8372
8373 if(bData)
8374 {
8375 char kick_reason[MAXLEN];
8376 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
8377
8378 bData->triggered = now;
8379 if(bData != cData->bans)
8380 {
8381 /* Shuffle the ban to the head of the list. */
8382 if(bData->next)
8383 bData->next->prev = bData->prev;
8384 if(bData->prev)
8385 bData->prev->next = bData->next;
8386
8387 bData->prev = NULL;
8388 bData->next = cData->bans;
8389
8390 if(cData->bans)
8391 cData->bans->prev = bData;
8392
8393 cData->bans = bData;
8394 }
8395
8396 change.args[0].mode = MODE_BAN;
8397 change.args[0].u.hostmask = bData->mask;
8398 mod_chanmode_announce(chanserv, chan, &change);
8399 KickChannelUser(user, chan, chanserv, kick_reason);
8400 return 1;
8401 }
8402 }
8403 return 0;
8404}
8405
5aef35cf 8406int
8407channel_user_is_exempt(struct userNode *user, struct chanNode *channel)
8408{
8409 unsigned int ii;
8410 for(ii = 0; ii < channel->exemptlist.used; ii++)
8411 {
277ad996 8412 if(user_matches_glob(user, channel->exemptlist.list[ii]->exempt, MATCH_USENICK, 0))
5aef35cf 8413 return true;
8414 }
8415 return false;
8416}
8417
c99dcaf6 8418
d76ed9a9 8419/* Welcome to my worst nightmare. Warning: Read (or modify)
8420 the code below at your own risk. */
8421static int
fb38e2bc 8422handle_join(struct modeNode *mNode, UNUSED_ARG(void *extra))
d76ed9a9 8423{
8424 struct mod_chanmode change;
8425 struct userNode *user = mNode->user;
8426 struct chanNode *channel = mNode->channel;
8427 struct chanData *cData;
8428 struct userData *uData = NULL;
8429 struct banData *bData;
8430 struct handle_info *handle;
8431 unsigned int modes = 0, info = 0;
8432 char *greeting;
8433
ed7ac86b 8434 if(IsLocal(user) || !channel || !channel->channel_info || IsSuspended(channel->channel_info))
d76ed9a9 8435 return 0;
8436
8437 cData = channel->channel_info;
8438 if(channel->members.used > cData->max)
8439 cData->max = channel->members.used;
8440
12673a59 8441#ifdef notdef
2f61d1d7 8442 /* Check for bans. If they're joining through a ban, one of two
8443 * cases applies:
8444 * 1: Join during a netburst, by riding the break. Kick them
8445 * unless they have ops or voice in the channel.
8446 * 2: They're allowed to join through the ban (an invite in
8447 * ircu2.10, or a +e on Hybrid, or something).
8448 * If they're not joining through a ban, and the banlist is not
8449 * full, see if they're on the banlist for the channel. If so,
8450 * kickban them.
8451 */
8452 if(user->uplink->burst && !mNode->modes)
8453 {
8454 unsigned int ii;
8455 for(ii = 0; ii < channel->banlist.used; ii++)
8456 {
277ad996 8457 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK, 0))
2f61d1d7 8458 {
8459 /* Riding a netburst. Naughty. */
8460 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
8461 return 1;
8462 }
8463 }
8464 }
12673a59 8465#endif
2f61d1d7 8466
ff5f1ab2 8467 if(user->handle_info)
8468 {
8469 handle = user->handle_info;
8470 if(handle)
8471 {
8472 uData = GetTrueChannelAccess(cData, handle);
8473 }
8474 }
8475
8476
d76ed9a9 8477 mod_chanmode_init(&change);
8478 change.argc = 1;
d76ed9a9 8479
ff5f1ab2 8480 /* TODO: maybe only people above inviteme level? -Rubin */
8481 /* We don't kick people with access */
5aef35cf 8482 if(!uData && !channel_user_is_exempt(user, channel))
ff5f1ab2 8483 {
8484 if(channel->banlist.used < MAXBANS)
d76ed9a9 8485 {
ff5f1ab2 8486 /* Not joining through a ban. */
8487 for(bData = cData->bans;
277ad996 8488 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK, 0);
ff5f1ab2 8489 bData = bData->next);
d76ed9a9 8490
ff5f1ab2 8491 if(bData)
d76ed9a9 8492 {
ff5f1ab2 8493 char kick_reason[MAXLEN];
8494 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
d76ed9a9 8495
ff5f1ab2 8496 bData->triggered = now;
8497 if(bData != cData->bans)
8498 {
8499 /* Shuffle the ban to the head of the list. */
8500 if(bData->next)
8501 bData->next->prev = bData->prev;
8502 if(bData->prev)
8503 bData->prev->next = bData->next;
8504
8505 bData->prev = NULL;
8506 bData->next = cData->bans;
8507
8508 if(cData->bans)
8509 cData->bans->prev = bData;
8510 cData->bans = bData;
8511 }
8512
8513 change.args[0].mode = MODE_BAN;
8514 change.args[0].u.hostmask = bData->mask;
8515 mod_chanmode_announce(chanserv, channel, &change);
8516 KickChannelUser(user, channel, chanserv, kick_reason);
8517 return 1;
8518 }
d76ed9a9 8519 }
8520 }
8521
8522 /* ChanServ will not modify the limits in join-flooded channels.
8523 It will also skip DynLimit processing when the user (or srvx)
8524 is bursting in, because there are likely more incoming. */
8525 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
8526 && !user->uplink->burst
8527 && !channel->join_flooded
8528 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
8529 {
8530 /* The user count has begun "bumping" into the channel limit,
8531 so set a timer to raise the limit a bit. Any previous
8532 timers are removed so three incoming users within the delay
8533 results in one limit change, not three. */
8534
8535 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
8536 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
8537 }
8538
4b6129c0 8539 /* Give automodes exept during join-floods */
8540 if(!channel->join_flooded)
d76ed9a9 8541 {
4b6129c0 8542 if(cData->chOpts[chAutomode] == 'v')
8543 modes |= MODE_VOICE;
8544 else if(cData->chOpts[chAutomode] == 'h')
8545 modes |= MODE_HALFOP;
8546 else if(cData->chOpts[chAutomode] == 'o')
8547 modes |= MODE_CHANOP;
d76ed9a9 8548 }
c8ca69a0 8549
d76ed9a9 8550 greeting = cData->greeting;
8551 if(user->handle_info)
8552 {
ff5f1ab2 8553/* handle = user->handle_info; */
d76ed9a9 8554
8555 if(IsHelper(user) && !IsHelping(user))
8556 {
8557 unsigned int ii;
8558 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8559 {
8560 if(channel == chanserv_conf.support_channels.list[ii])
8561 {
8562 HANDLE_SET_FLAG(user->handle_info, HELPING);
8563 break;
8564 }
8565 }
8566 }
8567
ff5f1ab2 8568/* uData = GetTrueChannelAccess(cData, handle); */
d76ed9a9 8569 if(uData && !IsUserSuspended(uData))
8570 {
4b6129c0 8571 /* non users getting automodes are handled above. */
8572 if(IsUserAutoOp(uData) && cData->chOpts[chAutomode] != 'n')
d76ed9a9 8573 {
4d69a3b1 8574 /* just op everyone with access */
8575 if(uData->access >= UL_PEON && cData->chOpts[chAutomode] == 'l')
8576 modes |= MODE_VOICE;
8577 /* or do their access level */
8578 else if(uData->access >= UL_OP )
d76ed9a9 8579 modes |= MODE_CHANOP;
4b6129c0 8580 else if(uData->access >= UL_HALFOP )
55342ce8 8581 modes |= MODE_HALFOP;
4b6129c0 8582 else if(uData->access >= UL_PEON && cData->chOpts[chAutomode] != 'm')
d76ed9a9 8583 modes |= MODE_VOICE;
8584 }
8585 if(uData->access >= UL_PRESENT)
8586 cData->visited = now;
8587 if(cData->user_greeting)
8588 greeting = cData->user_greeting;
8589 if(uData->info
8590 && (uData->access >= cData->lvlOpts[lvlUserInfo])
8591 && ((now - uData->seen) >= chanserv_conf.info_delay)
8592 && !uData->present)
8593 info = 1;
8594 uData->seen = now;
8595 uData->present = 1;
8596 }
8597 }
2f61d1d7 8598
8599 /* If user joining normally (not during burst), apply op or voice,
8600 * and send greeting/userinfo as appropriate.
8601 */
d76ed9a9 8602 if(!user->uplink->burst)
8603 {
8604 if(modes)
8605 {
4b6129c0 8606 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
55342ce8 8607 if(modes & MODE_CHANOP) {
8608 modes &= ~MODE_HALFOP;
d76ed9a9 8609 modes &= ~MODE_VOICE;
55342ce8 8610 }
4b6129c0 8611 */
d76ed9a9 8612 change.args[0].mode = modes;
a32da4c7 8613 change.args[0].u.member = mNode;
d76ed9a9 8614 mod_chanmode_announce(chanserv, channel, &change);
8615 }
2f61d1d7 8616 if(greeting)
d76ed9a9 8617 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
8618 if(uData && info)
8619 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
8620 }
8621 return 0;
8622}
8623
dd019452 8624static void
ffb204b6 8625chanserv_autojoin_channels(struct userNode *user)
dd019452 8626{
8627 struct userData *channel;
dd019452 8628
8629 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
8630 {
8631 struct chanNode *cn;
8632 struct modeNode *mn;
8633
8634 if(IsUserSuspended(channel)
8635 || IsSuspended(channel->channel)
8636 || !(cn = channel->channel->channel))
8637 continue;
8638
8639 mn = GetUserMode(cn, user);
8640 if(!mn)
8641 {
8642 if(!IsUserSuspended(channel)
8643 && IsUserAutoJoin(channel)
8644 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
8645 && !self->burst
8646 && !user->uplink->burst)
8647 irc_svsjoin(chanserv, user, cn);
8648 }
8649 }
8650}
8651
d76ed9a9 8652static void
81ac4787 8653handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle), UNUSED_ARG(void *extra))
d76ed9a9 8654{
8655 struct mod_chanmode change;
8656 struct userData *channel;
5177fd21 8657 unsigned int ii, jj, i;
d76ed9a9 8658
8659 if(!user->handle_info)
c52639b4 8660 return;
d76ed9a9 8661
8662 mod_chanmode_init(&change);
8663 change.argc = 1;
8664 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
8665 {
8666 struct chanNode *cn;
10fb34f6 8667 struct chanData *cData;
d76ed9a9 8668 struct modeNode *mn;
8669 if(IsUserSuspended(channel)
8670 || IsSuspended(channel->channel)
8671 || !(cn = channel->channel->channel))
8672 continue;
8673
10fb34f6 8674 cData = cn->channel_info;
d76ed9a9 8675 mn = GetUserMode(cn, user);
8676 if(!mn)
8677 {
8678 if(!IsUserSuspended(channel)
8679 && IsUserAutoInvite(channel)
8680 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
8681 && !self->burst
8682 && !user->uplink->burst)
8683 irc_invite(chanserv, user, cn);
8684 continue;
8685 }
8686
c52639b4
AS
8687 if(channel->access >= UL_PRESENT)
8688 channel->channel->visited = now;
d76ed9a9 8689
10fb34f6 8690 if(IsUserAutoOp(channel) && cData->chOpts[chAutomode] != 'n')
d76ed9a9 8691 {
6e00d1bf
MB
8692 if (channel->access >= UL_PEON && cData->chOpts[chAutomode] == 'l')
8693 change.args[0].mode = MODE_VOICE;
8694 else if(channel->access >= UL_OP )
d76ed9a9 8695 change.args[0].mode = MODE_CHANOP;
4b6129c0 8696 else if(channel->access >= UL_HALFOP )
55342ce8 8697 change.args[0].mode = MODE_HALFOP;
4b6129c0 8698 else if(channel->access >= UL_PEON )
d76ed9a9 8699 change.args[0].mode = MODE_VOICE;
8700 else
8701 change.args[0].mode = 0;
a32da4c7 8702 change.args[0].u.member = mn;
d76ed9a9 8703 if(change.args[0].mode)
8704 mod_chanmode_announce(chanserv, cn, &change);
8705 }
8706
c52639b4
AS
8707 channel->seen = now;
8708 channel->present = 1;
d76ed9a9 8709 }
8710
8711 for(ii = 0; ii < user->channels.used; ++ii)
8712 {
1136f709 8713 struct chanNode *chan = user->channels.list[ii]->channel;
d76ed9a9 8714 struct banData *ban;
8715
55342ce8 8716 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE))
1136f709 8717 || !chan->channel_info
8718 || IsSuspended(chan->channel_info))
d76ed9a9 8719 continue;
1136f709 8720 if(protect_user(user, chanserv, chan->channel_info, true))
ff5f1ab2 8721 continue;
1136f709 8722 for(jj = 0; jj < chan->banlist.used; ++jj)
277ad996 8723 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK, 0))
d76ed9a9 8724 break;
1136f709 8725 if(jj < chan->banlist.used)
d76ed9a9 8726 continue;
1136f709 8727 for(ban = chan->channel_info->bans; ban; ban = ban->next)
d76ed9a9 8728 {
8729 char kick_reason[MAXLEN];
277ad996 8730 if(!user_matches_glob(user, ban->mask,MATCH_USENICK | MATCH_VISIBLE, 0))
d76ed9a9 8731 continue;
8732 change.args[0].mode = MODE_BAN;
a32da4c7 8733 change.args[0].u.hostmask = ban->mask;
1136f709 8734 mod_chanmode_announce(chanserv, chan, &change);
d76ed9a9 8735 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
1136f709 8736 KickChannelUser(user, chan, chanserv, kick_reason);
d76ed9a9 8737 ban->triggered = now;
8738 break;
8739 }
8740 }
8741
8742 if(IsSupportHelper(user))
8743 {
8744 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8745 {
8746 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
8747 {
8748 HANDLE_SET_FLAG(user->handle_info, HELPING);
8749 break;
8750 }
8751 }
8752 }
5177fd21 8753
8754 if (user->handle_info->ignores->used) {
8755 for (i=0; i < user->handle_info->ignores->used; i++) {
8756 irc_silence(user, user->handle_info->ignores->list[i], 1);
8757 }
8758 }
56958740 8759
8760 if (user->handle_info->epithet)
c52639b4 8761 irc_swhois(chanserv, user, user->handle_info->epithet);
dd019452 8762
c52639b4
AS
8763 /* process autojoin channels 5 seconds later as this sometimes
8764 happens before autohide */
8765 //timeq_add(now + 5, chanserv_autojoin_channels, user);
ffb204b6 8766 chanserv_autojoin_channels(user);
d76ed9a9 8767}
8768
8769static void
63637aea 8770handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason), UNUSED_ARG(void *extra))
d76ed9a9 8771{
8772 struct chanData *cData;
8773 struct userData *uData;
8774
8775 cData = mn->channel->channel_info;
8776 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
8777 return;
8778
8779 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
8780 {
c52639b4
AS
8781 /* Allow for a bit of padding so that the limit doesn't
8782 track the user count exactly, which could get annoying. */
8783 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
8784 {
8785 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
8786 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
8787 }
d76ed9a9 8788 }
8789
8790 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
8791 {
c52639b4 8792 scan_user_presence(uData, mn->user);
d76ed9a9 8793 uData->seen = now;
1136f709 8794 if (uData->access >= UL_PRESENT)
8795 cData->visited = now;
c52639b4 8796 }
d76ed9a9 8797
8798 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
8799 {
1136f709 8800 unsigned int ii;
c52639b4
AS
8801 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8802 {
57fdf922 8803 struct chanNode *channel;
8804 struct userNode *exclude;
8805 /* When looking at the channel that is being /part'ed, we
8806 * have to skip over the client that is leaving. For
8807 * other channels, we must not do that.
8808 */
8809 channel = chanserv_conf.support_channels.list[ii];
8810 exclude = (channel == mn->channel) ? mn->user : NULL;
8811 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
d76ed9a9 8812 break;
57fdf922 8813 }
8814 if(ii == chanserv_conf.support_channels.used)
d76ed9a9 8815 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
8816 }
8817}
8818
8819static void
91667658 8820handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel, UNUSED_ARG(void *extra))
d76ed9a9 8821{
8822 struct userData *uData;
8823
8824 if(!channel->channel_info || !kicker || IsService(kicker)
8825 || (kicker == victim) || IsSuspended(channel->channel_info)
8826 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
8827 return;
8828
ff5f1ab2 8829 if(protect_user(victim, kicker, channel->channel_info, false))
d76ed9a9 8830 {
31f23f13 8831 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_KICK");
c52639b4 8832 KickChannelUser(kicker, channel, chanserv, reason);
d76ed9a9 8833 }
8834
8835 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
8836 uData->seen = now;
8837}
8838
8839static int
5a2c7cf6 8840handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic, UNUSED_ARG(void *extra))
d76ed9a9 8841{
8842 struct chanData *cData;
8843
8844 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
8845 return 0;
8846
8847 cData = channel->channel_info;
8848 if(bad_topic(channel, user, channel->topic))
b75e24a3 8849 { /* User doesnt have privs to set topics. Undo it */
d76ed9a9 8850 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7fda2b52 8851 SetChannelTopic(channel, chanserv, chanserv, old_topic, 1);
d76ed9a9 8852 return 1;
8853 }
b75e24a3 8854 /* If there is a topic mask set, and the new topic doesnt match,
8855 * set the topic to mask + new_topic */
8856 if(cData->topic_mask && !match_ircglob(channel->topic, cData->topic_mask))
8857 {
8858 char new_topic[TOPICLEN+1];
8859 conform_topic(cData->topic_mask, channel->topic, new_topic);
8860 if(*new_topic)
8861 {
75ef8cdc 8862 SetChannelTopic(channel, chanserv, user, new_topic, 1);
b75e24a3 8863 /* and fall through to topicsnarf code below.. */
8864 }
8865 else /* Topic couldnt fit into mask, was too long */
8866 {
75ef8cdc 8867 SetChannelTopic(channel, chanserv, user, old_topic, 1);
b75e24a3 8868 send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT1", channel->name, cData->topic_mask);
8869 send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
8870 return 1;
8871 }
8872 }
d76ed9a9 8873 /* With topicsnarf, grab the topic and save it as the default topic. */
8874 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
8875 {
8876 free(cData->topic);
8877 cData->topic = strdup(channel->topic);
8878 }
8879 return 0;
8880}
8881
8882static void
f14b32b8 8883handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change, UNUSED_ARG(void *extra))
d76ed9a9 8884{
8885 struct mod_chanmode *bounce = NULL;
8886 unsigned int bnc, ii;
8887 char deopped = 0;
8888
8889 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
8890 return;
8891
8892 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
8893 && mode_lock_violated(&channel->channel_info->modes, change))
8894 {
8895 char correct[MAXLEN];
8896 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
8897 mod_chanmode_format(&channel->channel_info->modes, correct);
8898 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
8899 }
8900 for(ii = bnc = 0; ii < change->argc; ++ii)
8901 {
8902 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
8903 {
a32da4c7 8904 const struct userNode *victim = change->args[ii].u.member->user;
ff5f1ab2 8905 if(!protect_user(victim, user, channel->channel_info, false))
d76ed9a9 8906 continue;
8907 if(!bounce)
8908 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
8909 if(!deopped)
8910 {
8911 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
a32da4c7 8912 bounce->args[bnc].u.member = GetUserMode(channel, user);
8913 if(bounce->args[bnc].u.member)
d76ed9a9 8914 bnc++;
8915 deopped = 1;
8916 }
8917 bounce->args[bnc].mode = MODE_CHANOP;
a32da4c7 8918 bounce->args[bnc].u.member = change->args[ii].u.member;
d76ed9a9 8919 bnc++;
8920 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
8921 }
8922 else if(change->args[ii].mode & MODE_CHANOP)
8923 {
a32da4c7 8924 const struct userNode *victim = change->args[ii].u.member->user;
c092fcad 8925 if(IsService(victim) || validate_op(NULL, user, channel, (struct userNode*)victim))
d76ed9a9 8926 continue;
8927 if(!bounce)
8928 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
8929 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
a32da4c7 8930 bounce->args[bnc].u.member = change->args[ii].u.member;
d76ed9a9 8931 bnc++;
8932 }
8933 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
8934 {
a32da4c7 8935 const char *ban = change->args[ii].u.hostmask;
d76ed9a9 8936 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
8937 continue;
8938 if(!bounce)
8939 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
8940 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
ec1a68c8 8941 bounce->args[bnc].u.hostmask = strdup(ban);
d76ed9a9 8942 bnc++;
8943 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
8944 }
8945 }
8946 if(bounce)
8947 {
8948 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
8949 mod_chanmode_announce(chanserv, channel, bounce);
ec1a68c8 8950 for(ii = 0; ii < change->argc; ++ii)
8951 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
8952 free((char*)bounce->args[ii].u.hostmask);
d76ed9a9 8953 mod_chanmode_free(bounce);
8954 }
8955}
8956
8957static void
63189c10 8958handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick), UNUSED_ARG(void *extra))
d76ed9a9 8959{
8960 struct chanNode *channel;
8961 struct banData *bData;
8962 struct mod_chanmode change;
8963 unsigned int ii, jj;
8964 char kick_reason[MAXLEN];
8965
8966 mod_chanmode_init(&change);
8967 change.argc = 1;
8968 change.args[0].mode = MODE_BAN;
8969 for(ii = 0; ii < user->channels.used; ++ii)
8970 {
8971 channel = user->channels.list[ii]->channel;
8972 /* Need not check for bans if they're opped or voiced. */
4b6129c0 8973 /* TODO: does this make sense in automode v, h, and o? *
8974 * lets still enforce on voice people anyway, and see how that goes -Rubin */
ff5f1ab2 8975 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE ))
d76ed9a9 8976 continue;
8977 /* Need not check for bans unless channel registration is active. */
8978 if(!channel->channel_info || IsSuspended(channel->channel_info))
8979 continue;
8980 /* Look for a matching ban already on the channel. */
8981 for(jj = 0; jj < channel->banlist.used; ++jj)
277ad996 8982 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK, 0))
d76ed9a9 8983 break;
8984 /* Need not act if we found one. */
8985 if(jj < channel->banlist.used)
8986 continue;
ff5f1ab2 8987 /* don't kick someone on the userlist */
8988 if(protect_user(user, chanserv, channel->channel_info, true))
8989 continue;
d76ed9a9 8990 /* Look for a matching ban in this channel. */
8991 for(bData = channel->channel_info->bans; bData; bData = bData->next)
8992 {
277ad996 8993 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE, 0))
d76ed9a9 8994 continue;
a32da4c7 8995 change.args[0].u.hostmask = bData->mask;
d76ed9a9 8996 mod_chanmode_announce(chanserv, channel, &change);
8997 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
8998 KickChannelUser(user, channel, chanserv, kick_reason);
8999 bData->triggered = now;
9000 break; /* we don't need to check any more bans in the channel */
9001 }
9002 }
9003}
9004
3070719a 9005static void handle_rename(struct handle_info *handle, const char *old_handle, UNUSED_ARG(void *extra))
d76ed9a9 9006{
9007 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
9008
9009 if(dnr)
9010 {
9011 dict_remove2(handle_dnrs, old_handle, 1);
9012 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
9013 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
9014 }
9015}
9016
9017static void
974d3831 9018handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle, UNUSED_ARG(void *extra))
d76ed9a9 9019{
9020 struct userNode *h_user;
9021
9022 if(handle->channels)
9023 {
9024 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
9025 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
9026
9027 while(handle->channels)
9028 del_channel_user(handle->channels, 1);
9029 }
9030}
9031
0ab7b4bc 9032static int
9d59f196 9033handle_server_link(UNUSED_ARG(struct server *server), UNUSED_ARG(void *extra))
d76ed9a9 9034{
9035 struct chanData *cData;
9036
9037 for(cData = channelList; cData; cData = cData->next)
9038 {
9039 if(!IsSuspended(cData))
9040 cData->may_opchan = 1;
9041 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
9042 && !cData->channel->join_flooded
9043 && ((cData->channel->limit - cData->channel->members.used)
9044 < chanserv_conf.adjust_threshold))
9045 {
9046 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
9047 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
9048 }
9049 }
0ab7b4bc 9050 return 0;
d76ed9a9 9051}
9052
9053static void
9054chanserv_conf_read(void)
9055{
9056 dict_t conf_node;
9057 const char *str;
9058 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
9059 struct mod_chanmode *change;
9060 struct string_list *strlist;
9061 struct chanNode *chan;
9062 unsigned int ii;
9063
9064 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
9065 {
c52639b4
AS
9066 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
9067 return;
d76ed9a9 9068 }
9069 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
9070 UnlockChannel(chanserv_conf.support_channels.list[ii]);
9071 chanserv_conf.support_channels.used = 0;
9072 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
9073 {
9074 for(ii = 0; ii < strlist->used; ++ii)
9075 {
9076 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
9077 if(!str2)
9078 str2 = "+nt";
2aef5f4b 9079 chan = AddChannel(strlist->list[ii], now, str2, NULL, NULL);
d76ed9a9 9080 LockChannel(chan);
9081 channelList_append(&chanserv_conf.support_channels, chan);
9082 }
9083 }
9084 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
9085 {
9086 const char *str2;
9087 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
9088 if(!str2)
9089 str2 = "+nt";
2aef5f4b 9090 chan = AddChannel(str, now, str2, NULL, NULL);
d76ed9a9 9091 LockChannel(chan);
9092 channelList_append(&chanserv_conf.support_channels, chan);
9093 }
9094 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
9095 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
9096 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
9097 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
9098 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
1117fc5a 9099 chanserv_conf.greeting_length = str ? atoi(str) : 200;
d76ed9a9 9100 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
9101 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
9102 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
9103 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
9104 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
9105 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
31f23f13 9106 str = database_get_data(conf_node, KEY_BAN_TIMEOUT_FREQ, RECDB_QSTRING);
9107 chanserv_conf.ban_timeout_frequency = str ? ParseInterval(str) : 600;
d76ed9a9 9108 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
9109 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
1136f709 9110 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
9111 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
d76ed9a9 9112 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
9113 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
9114 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
9115 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
9116 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
9117 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
9118 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
9119 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
9120 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
9121 if(chanserv && str)
9122 NickChange(chanserv, str, 0);
9123 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
9124 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
a32da4c7 9125 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
9126 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
d76ed9a9 9127 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
9128 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
9129 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
9130 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
9131 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
9132 chanserv_conf.max_owned = str ? atoi(str) : 5;
9133 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
9134 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
9135 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
9136 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
9137 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
9138 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7637f48f 9139 str = database_get_data(conf_node, KEY_GOD_TIMEOUT, RECDB_QSTRING);
9140 god_timeout = str ? ParseInterval(str) : 60*15;
d76ed9a9 9141 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
9142 if(!str)
9143 str = "+nt";
9144 safestrncpy(mode_line, str, sizeof(mode_line));
9145 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
2f61d1d7 9146 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
9147 && (change->argc < 2))
d76ed9a9 9148 {
9149 chanserv_conf.default_modes = *change;
9150 mod_chanmode_free(change);
9151 }
d3abe0df 9152 str = database_get_data(conf_node, KEY_VALID_CHANNEL_REGEX, RECDB_QSTRING);
9153 if (chanserv_conf.valid_channel_regex_set)
9154 regfree(&chanserv_conf.valid_channel_regex);
9155 if (str) {
9156 int err = regcomp(&chanserv_conf.valid_channel_regex, str, REG_EXTENDED|REG_ICASE|REG_NOSUB);
9157 chanserv_conf.valid_channel_regex_set = !err;
9158 if (err) log_module(CS_LOG, LOG_ERROR, "Bad valid_channel_regex (error %d)", err);
c52639b4
AS
9159 }
9160 else {
d3abe0df 9161 chanserv_conf.valid_channel_regex_set = 0;
9162 }
5e6460e4 9163 free_string_list(chanserv_conf.wheel);
9164 strlist = database_get_data(conf_node, "wheel", RECDB_STRING_LIST);
9165 if(strlist)
9166 strlist = string_list_copy(strlist);
9167 else
9168 {
9169 static const char *list[] = {
9170 "peer", "partall", "gline", /* "shun", */
9171 "nothing", "randjoin", "abusewhois", "kickall",
19c83de8 9172 "nickchange", "kill", "svsignore", "kickbanall",
9173 NULL};
5e6460e4 9174 unsigned int ii;
9175 strlist = alloc_string_list(ArrayLength(list)-1);
9176 for(ii=0; list[ii]; ii++)
9177 string_list_append(strlist, strdup(list[ii]));
9178 }
9179 chanserv_conf.wheel = strlist;
9180
d76ed9a9 9181 free_string_list(chanserv_conf.set_shows);
9182 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
9183 if(strlist)
9184 strlist = string_list_copy(strlist);
9185 else
9186 {
9187 static const char *list[] = {
9188 /* free form text */
9189 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
9190 /* options based on user level */
4b6129c0 9191 "PubCmd", "InviteMe", "UserInfo","EnfOps",
9192 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
d76ed9a9 9193 /* multiple choice options */
d3347099 9194 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
d76ed9a9 9195 /* binary options */
31f23f13 9196 "DynLimit", "NoDelete", "BanTimeout",
d76ed9a9 9197 /* delimiter */
9198 NULL
9199 };
d76ed9a9 9200 strlist = alloc_string_list(ArrayLength(list)-1);
9201 for(ii=0; list[ii]; ii++)
9202 string_list_append(strlist, strdup(list[ii]));
9203 }
9204 chanserv_conf.set_shows = strlist;
9205 /* We don't look things up now, in case the list refers to options
9206 * defined by modules initialized after this point. Just mark the
9207 * function list as invalid, so it will be initialized.
9208 */
9209 set_shows_list.used = 0;
5e6460e4 9210
d76ed9a9 9211 free_string_list(chanserv_conf.eightball);
9212 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
9213 if(strlist)
9214 {
9215 strlist = string_list_copy(strlist);
9216 }
9217 else
9218 {
9219 strlist = alloc_string_list(4);
9220 string_list_append(strlist, strdup("Yes."));
9221 string_list_append(strlist, strdup("No."));
9222 string_list_append(strlist, strdup("Maybe so."));
9223 }
9224 chanserv_conf.eightball = strlist;
5e6460e4 9225
d76ed9a9 9226 free_string_list(chanserv_conf.old_ban_names);
9227 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
9228 if(strlist)
9229 strlist = string_list_copy(strlist);
9230 else
9231 strlist = alloc_string_list(2);
9232 chanserv_conf.old_ban_names = strlist;
d76ed9a9 9233 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
1117fc5a 9234 off_channel = str ? atoi(str) : 0;
d76ed9a9 9235}
9236
9237static void
9238chanserv_note_type_read(const char *key, struct record_data *rd)
9239{
9240 dict_t obj;
9241 struct note_type *ntype;
9242 const char *str;
9243
9244 if(!(obj = GET_RECORD_OBJECT(rd)))
9245 {
9246 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
9247 return;
9248 }
9249 if(!(ntype = chanserv_create_note_type(key)))
9250 {
9251 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
9252 return;
9253 }
9254
9255 /* Figure out set access */
9256 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
9257 {
9258 ntype->set_access_type = NOTE_SET_PRIVILEGED;
9259 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
9260 }
9261 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
9262 {
9263 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
9264 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
9265 }
9266 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
9267 {
9268 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
9269 }
9270 else
9271 {
9272 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
9273 ntype->set_access_type = NOTE_SET_PRIVILEGED;
9274 ntype->set_access.min_opserv = 0;
9275 }
9276
9277 /* Figure out visibility */
9278 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
9279 ntype->visible_type = NOTE_VIS_PRIVILEGED;
9280 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
9281 ntype->visible_type = NOTE_VIS_PRIVILEGED;
9282 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
9283 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
9284 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
9285 ntype->visible_type = NOTE_VIS_ALL;
9286 else
9287 ntype->visible_type = NOTE_VIS_PRIVILEGED;
9288
9289 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
9290 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
9291}
9292
9293static void
9294user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
9295{
9296 struct handle_info *handle;
9297 struct userData *uData;
dfaa28a4 9298 char *seen, *inf, *flags, *expires, *accessexpiry, *clvlexpiry, *lstacc;
d76ed9a9 9299 time_t last_seen;
1136f709 9300 unsigned short access_level, lastaccess = 0;
d76ed9a9 9301
9302 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
9303 {
c52639b4
AS
9304 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
9305 return;
d76ed9a9 9306 }
9307
1136f709 9308 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
9309 if(access_level > UL_OWNER)
d76ed9a9 9310 {
c52639b4
AS
9311 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
9312 return;
d76ed9a9 9313 }
9314
9315 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
9316 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
9317 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
9318 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
b10abdb2 9319 expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
dfaa28a4 9320 accessexpiry = database_get_data(rd->d.object, KEY_ACCESSEXPIRY, RECDB_QSTRING);
9321 clvlexpiry = database_get_data(rd->d.object, KEY_CLVLEXPIRY, RECDB_QSTRING);
9322 lstacc = database_get_data(rd->d.object, KEY_LASTLEVEL, RECDB_QSTRING);
9323 lastaccess = lstacc ? atoi(lstacc) : 0;
9324
d76ed9a9 9325 handle = get_handle_info(key);
9326 if(!handle)
9327 {
9328 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
9329 return;
9330 }
9331
1136f709 9332 uData = add_channel_user(chan, handle, access_level, last_seen, inf, 0);
d76ed9a9 9333 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
b10abdb2 9334 uData->expires = expires ? strtoul(expires, NULL, 0) : 0;
dfaa28a4 9335
9336 uData->accessexpiry = accessexpiry ? strtoul(accessexpiry, NULL, 0) : 0;
9337 if (uData->accessexpiry > 0)
9338 timeq_add(uData->accessexpiry, chanserv_expire_tempuser, uData);
9339
9340 uData->clvlexpiry = clvlexpiry ? strtoul(clvlexpiry, NULL, 0) : 0;
9341 if (uData->clvlexpiry > 0)
9342 timeq_add(uData->clvlexpiry, chanserv_expire_tempclvl, uData);
9343
9344 uData->lastaccess = lastaccess;
b10abdb2 9345
9346 if((uData->flags & USER_SUSPENDED) && uData->expires)
9347 {
9348 if(uData->expires > now)
9349 timeq_add(uData->expires, chanserv_expire_user_suspension, uData);
9350 else
9351 uData->flags &= ~USER_SUSPENDED;
9352 }
c8ca69a0 9353
9354 /* Upgrade: set autoop to the inverse of noautoop */
9355 if(chanserv_read_version < 2)
9356 {
9357 /* if noautoop is true, set autoop false, and vice versa */
9358 if(uData->flags & USER_NOAUTO_OP)
9359 uData->flags = uData->flags & ~USER_AUTO_OP;
9360 else
9361 uData->flags = uData->flags | USER_AUTO_OP;
9362 log_module(CS_LOG, LOG_INFO, "UPGRADE: to db version 2 from %u. Changing flag to %d for %s in %s.", chanserv_read_version, uData->flags, key, chan->channel->name);
9363 }
9364
d76ed9a9 9365}
9366
9367static void
9368ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
9369{
9370 struct banData *bData;
9371 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
9372 time_t set_time, triggered_time, expires_time;
9373
9374 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
9375 {
c52639b4
AS
9376 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
9377 return;
d76ed9a9 9378 }
9379
9380 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
9381 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
9382 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
9383 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
9384 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
9385 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
0d16e639 9386 if (!reason || !owner)
9387 return;
d76ed9a9 9388
9389 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
9390 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
9391 if(s_expires)
9392 expires_time = (time_t)strtoul(s_expires, NULL, 0);
9393 else if(s_duration)
9394 expires_time = set_time + atoi(s_duration);
9395 else
9396 expires_time = 0;
9397
ec1a68c8 9398 if(!reason || (expires_time && (expires_time < now)))
d76ed9a9 9399 return;
9400
9401 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
9402}
9403
9404static struct suspended *
9405chanserv_read_suspended(dict_t obj)
9406{
9407 struct suspended *suspended = calloc(1, sizeof(*suspended));
9408 char *str;
9409 dict_t previous;
9410
9411 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
9412 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
9413 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
9414 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
9415 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
9416 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
9417 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
9418 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
9419 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
9420 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
9421 return suspended;
9422}
9423
82f37c08 9424static struct giveownership *
9425chanserv_read_giveownership(dict_t obj)
9426{
9427 struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
9428 char *str;
9429 dict_t previous;
9430
9431 str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
9432 giveownership->staff_issuer = str ? strdup(str) : NULL;
9433
9434 giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
9435
9436 giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
9437 giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
9438
9439 str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
9440 giveownership->reason = str ? strdup(str) : NULL;
9441 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
9442 giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
9443
9444 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
9445 giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
9446 return giveownership;
9447}
9448
d76ed9a9 9449static int
9450chanserv_channel_read(const char *key, struct record_data *hir)
9451{
9452 struct suspended *suspended;
82f37c08 9453 struct giveownership *giveownership;
d76ed9a9 9454 struct mod_chanmode *modes;
9455 struct chanNode *cNode;
9456 struct chanData *cData;
9457 struct dict *channel, *obj;
9458 char *str, *argv[10];
9459 dict_iterator_t it;
9460 unsigned int argc;
9461
9462 channel = hir->d.object;
9463
9464 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
9465 if(!str)
9466 str = "<unknown>";
2aef5f4b 9467 cNode = AddChannel(key, now, NULL, NULL, NULL);
d76ed9a9 9468 if(!cNode)
9469 {
9470 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
9471 return 0;
9472 }
9473 cData = register_channel(cNode, str);
9474 if(!cData)
9475 {
9476 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
c52639b4 9477 return 0;
d76ed9a9 9478 }
9479
9480 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
9481 {
9482 enum levelOption lvlOpt;
9483 enum charOption chOpt;
9484
9485 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
9486 cData->flags = atoi(str);
9487
9488 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
9489 {
9490 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
9491 if(str)
9492 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
9493 else if(levelOptions[lvlOpt].old_flag)
9494 {
9495 if(cData->flags & levelOptions[lvlOpt].old_flag)
9496 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
9497 else
9498 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
9499 }
9500 }
9501
9502 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
9503 {
9504 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
9505 continue;
9506 cData->chOpts[chOpt] = str[0];
9507 }
9508 }
9509 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
9510 {
9511 enum levelOption lvlOpt;
9512 enum charOption chOpt;
9513 unsigned int count;
9514
c52639b4 9515 cData->flags = base64toint(str, 5);
d76ed9a9 9516 count = strlen(str += 5);
9517 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
9518 {
9519 unsigned short lvl;
9520 if(levelOptions[lvlOpt].old_flag)
9521 {
9522 if(cData->flags & levelOptions[lvlOpt].old_flag)
9523 lvl = levelOptions[lvlOpt].flag_value;
9524 else
9525 lvl = levelOptions[lvlOpt].default_value;
9526 }
9527 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
9528 {
c52639b4
AS
9529 case 'c': lvl = UL_COOWNER; break;
9530 case 'm': lvl = UL_MANAGER; break;
9531 case 'n': lvl = UL_OWNER+1; break;
9532 case 'o': lvl = UL_OP; break;
9533 case 'p': lvl = UL_PEON; break;
9534 case 'h': lvl = UL_HALFOP; break;
9535 case 'w': lvl = UL_OWNER; break;
9536 default: lvl = 0; break;
d76ed9a9 9537 }
9538 cData->lvlOpts[lvlOpt] = lvl;
9539 }
9540 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
9541 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
9542 }
9543
9544 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
9545 {
9546 suspended = chanserv_read_suspended(obj);
9547 cData->suspended = suspended;
9548 suspended->cData = cData;
9549 /* We could use suspended->expires and suspended->revoked to
9550 * set the CHANNEL_SUSPENDED flag, but we don't. */
9551 }
a32da4c7 9552 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
d76ed9a9 9553 {
9554 suspended = calloc(1, sizeof(*suspended));
9555 suspended->issued = 0;
9556 suspended->revoked = 0;
a32da4c7 9557 suspended->suspender = strdup(str);
d76ed9a9 9558 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
9559 suspended->expires = str ? atoi(str) : 0;
d76ed9a9 9560 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
9561 suspended->reason = strdup(str ? str : "No reason");
9562 suspended->previous = NULL;
9563 cData->suspended = suspended;
9564 suspended->cData = cData;
9565 }
9566 else
a32da4c7 9567 {
9568 cData->flags &= ~CHANNEL_SUSPENDED;
d76ed9a9 9569 suspended = NULL; /* to squelch a warning */
a32da4c7 9570 }
d76ed9a9 9571
9572 if(IsSuspended(cData)) {
9573 if(suspended->expires > now)
9574 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
9575 else if(suspended->expires)
9576 cData->flags &= ~CHANNEL_SUSPENDED;
9577 }
9578
82f37c08 9579 if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
9580 {
9581 giveownership = chanserv_read_giveownership(obj);
9582 cData->giveownership = giveownership;
9583 }
9584
d76ed9a9 9585 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
9586 struct mod_chanmode change;
9587 mod_chanmode_init(&change);
9588 change.argc = 1;
9589 change.args[0].mode = MODE_CHANOP;
a32da4c7 9590 change.args[0].u.member = AddChannelUser(chanserv, cNode);
d76ed9a9 9591 mod_chanmode_announce(chanserv, cNode, &change);
9592 }
9593
9594 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
9595 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
9596 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
9597 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
a32da4c7 9598 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
9599 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
d76ed9a9 9600 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
9601 cData->max = str ? atoi(str) : 0;
9602 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
9603 cData->greeting = str ? strdup(str) : NULL;
9604 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
9605 cData->user_greeting = str ? strdup(str) : NULL;
9606 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
9607 cData->topic_mask = str ? strdup(str) : NULL;
9608 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
9609 cData->topic = str ? strdup(str) : NULL;
9610
8b9e7d45 9611 str = database_get_data(channel, KEY_MAXSETINFO, RECDB_QSTRING);
9612 cData->maxsetinfo = str ? strtoul(str, NULL, 0) : chanserv_conf.max_userinfo_length;
9613
d76ed9a9 9614 if(!IsSuspended(cData)
9615 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
9616 && (argc = split_line(str, 0, ArrayLength(argv), argv))
c52639b4
AS
9617 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0)))
9618 {
d76ed9a9 9619 cData->modes = *modes;
c52639b4 9620 if(off_channel > 0)
a32da4c7 9621 cData->modes.modes_set |= MODE_REGISTERED;
d76ed9a9 9622 if(cData->modes.argc > 1)
9623 cData->modes.argc = 1;
9624 mod_chanmode_announce(chanserv, cNode, &cData->modes);
9625 mod_chanmode_free(modes);
9626 }
9627
9628 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
9629 for(it = dict_first(obj); it; it = iter_next(it))
c52639b4 9630 user_read_helper(iter_key(it), iter_data(it), cData);
d76ed9a9 9631
9632 if(!cData->users && !IsProtected(cData))
9633 {
9634 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
c52639b4 9635 unregister_channel(cData, "has empty user list.");
d76ed9a9 9636 return 0;
9637 }
9638
9639 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
9640 for(it = dict_first(obj); it; it = iter_next(it))
9641 ban_read_helper(iter_key(it), iter_data(it), cData);
9642
9643 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
9644 for(it = dict_first(obj); it; it = iter_next(it))
9645 {
9646 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
9647 struct record_data *rd = iter_data(it);
9648 const char *note, *setter;
9649
9650 if(rd->type != RECDB_OBJECT)
9651 {
9652 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
9653 }
9654 else if(!ntype)
9655 {
9656 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
9657 }
9658 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
9659 {
9660 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
9661 }
9662 else
9663 {
9664 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
9665 if(!setter) setter = "<unknown>";
9666 chanserv_add_channel_note(cData, ntype, setter, note);
9667 }
9668 }
9669
9670 return 0;
9671}
9672
9673static void
9674chanserv_dnr_read(const char *key, struct record_data *hir)
9675{
9676 const char *setter, *reason, *str;
9677 struct do_not_register *dnr;
1136f709 9678 time_t expiry;
d76ed9a9 9679
9680 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
9681 if(!setter)
9682 {
9683 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
9684 return;
9685 }
9686 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
9687 if(!reason)
9688 {
9689 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
9690 return;
9691 }
1136f709 9692 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
9693 expiry = str ? (time_t)strtoul(str, NULL, 0) : 0;
9694 if(expiry && expiry <= now)
9695 return;
9696 dnr = chanserv_add_dnr(key, setter, expiry, reason);
d76ed9a9 9697 if(!dnr)
9698 return;
9699 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
9700 if(str)
9701 dnr->set = atoi(str);
9702 else
9703 dnr->set = 0;
9704}
9705
c8ca69a0 9706static void
9707chanserv_version_read(struct dict *section)
9708{
9709 /* global var.. */
9710 char *str;
9711 str = database_get_data(section, KEY_VERSION_NUMBER, RECDB_QSTRING);
9712 if(str)
9713 chanserv_read_version = atoi(str);
9714 log_module(CS_LOG, LOG_DEBUG, "Chanserv db version is %d.", chanserv_read_version);
9715}
9716
d76ed9a9 9717static int
9718chanserv_saxdb_read(struct dict *database)
9719{
9720 struct dict *section;
9721 dict_iterator_t it;
9722
c8ca69a0 9723 if((section = database_get_data(database, KEY_VERSION_CONTROL, RECDB_OBJECT)))
9724 chanserv_version_read(section);
9725
d76ed9a9 9726 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
9727 for(it = dict_first(section); it; it = iter_next(it))
9728 chanserv_note_type_read(iter_key(it), iter_data(it));
9729
9730 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
c52639b4
AS
9731 for(it = dict_first(section); it; it = iter_next(it))
9732 chanserv_channel_read(iter_key(it), iter_data(it));
d76ed9a9 9733
9734 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
9735 for(it = dict_first(section); it; it = iter_next(it))
9736 chanserv_dnr_read(iter_key(it), iter_data(it));
9737
9738 return 0;
9739}
9740
9741static int
9742chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
9743{
9744 int high_present = 0;
9745 saxdb_start_record(ctx, KEY_USERS, 1);
9746 for(; uData; uData = uData->next)
9747 {
9748 if((uData->access >= UL_PRESENT) && uData->present)
9749 high_present = 1;
9750 saxdb_start_record(ctx, uData->handle->handle, 0);
9751 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
9752 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
dfaa28a4 9753 saxdb_write_int(ctx, KEY_ACCESSEXPIRY, uData->accessexpiry);
9754 saxdb_write_int(ctx, KEY_CLVLEXPIRY, uData->clvlexpiry);
9755 saxdb_write_int(ctx, KEY_LASTLEVEL, uData->lastaccess);
d76ed9a9 9756 if(uData->flags)
9757 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
b10abdb2 9758 if(uData->expires)
9759 saxdb_write_int(ctx, KEY_EXPIRES, uData->expires);
c52639b4 9760 if(uData->info)
d76ed9a9 9761 saxdb_write_string(ctx, KEY_INFO, uData->info);
9762 saxdb_end_record(ctx);
9763 }
9764 saxdb_end_record(ctx);
9765 return high_present;
9766}
9767
9768static void
9769chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
9770{
9771 if(!bData)
9772 return;
9773 saxdb_start_record(ctx, KEY_BANS, 1);
9774 for(; bData; bData = bData->next)
9775 {
9776 saxdb_start_record(ctx, bData->mask, 0);
9777 saxdb_write_int(ctx, KEY_SET, bData->set);
9778 if(bData->triggered)
9779 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
9780 if(bData->expires)
9781 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
9782 if(bData->owner[0])
9783 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
9784 if(bData->reason)
9785 saxdb_write_string(ctx, KEY_REASON, bData->reason);
9786 saxdb_end_record(ctx);
9787 }
9788 saxdb_end_record(ctx);
9789}
9790
9791static void
9792chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
9793{
9794 saxdb_start_record(ctx, name, 0);
9795 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
9796 saxdb_write_string(ctx, KEY_REASON, susp->reason);
9797 if(susp->issued)
9798 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
9799 if(susp->expires)
9800 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
9801 if(susp->revoked)
9802 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
9803 if(susp->previous)
9804 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
9805 saxdb_end_record(ctx);
9806}
9807
82f37c08 9808static void
9809chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
9810{
9811 saxdb_start_record(ctx, name, 0);
9812 if(giveownership->staff_issuer)
9813 saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
9814 if(giveownership->old_owner)
9815 saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
9816 if(giveownership->target)
9817 saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
9818 if(giveownership->target_access)
9819 saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
9820 if(giveownership->reason)
9821 saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
9822 if(giveownership->issued)
9823 saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
9824 if(giveownership->previous)
9825 chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
9826 saxdb_end_record(ctx);
9827}
9828
d76ed9a9 9829static void
9830chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
9831{
9832 char buf[MAXLEN];
9833 int high_present;
9834 enum levelOption lvlOpt;
9835 enum charOption chOpt;
9836
9837 saxdb_start_record(ctx, channel->channel->name, 1);
9838
9839 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
9840 saxdb_write_int(ctx, KEY_MAX, channel->max);
9841 if(channel->topic)
9842 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
9843 if(channel->registrar)
9844 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
9845 if(channel->greeting)
9846 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
9847 if(channel->user_greeting)
9848 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
9849 if(channel->topic_mask)
9850 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
9851 if(channel->suspended)
9852 chanserv_write_suspended(ctx, "suspended", channel->suspended);
82f37c08 9853 if(channel->giveownership)
9854 chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
d76ed9a9 9855
9856 saxdb_start_record(ctx, KEY_OPTIONS, 0);
9857 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
9858 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
9859 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
9860 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
9861 {
9862 buf[0] = channel->chOpts[chOpt];
9863 buf[1] = '\0';
9864 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
9865 }
9866 saxdb_end_record(ctx);
9867
8b9e7d45 9868 if (channel->maxsetinfo)
9869 saxdb_write_int(ctx, KEY_MAXSETINFO, channel->maxsetinfo);
9870
d76ed9a9 9871 if(channel->modes.modes_set || channel->modes.modes_clear)
9872 {
c52639b4 9873 mod_chanmode_format(&channel->modes, buf);
d76ed9a9 9874 saxdb_write_string(ctx, KEY_MODES, buf);
9875 }
9876
9877 high_present = chanserv_write_users(ctx, channel->users);
9878 chanserv_write_bans(ctx, channel->bans);
9879
9880 if(dict_size(channel->notes))
9881 {
9882 dict_iterator_t it;
9883
9884 saxdb_start_record(ctx, KEY_NOTES, 1);
9885 for(it = dict_first(channel->notes); it; it = iter_next(it))
9886 {
c52639b4 9887 struct note *note = iter_data(it);
d76ed9a9 9888 saxdb_start_record(ctx, iter_key(it), 0);
9889 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
9890 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
9891 saxdb_end_record(ctx);
9892 }
9893 saxdb_end_record(ctx);
9894 }
9895
a32da4c7 9896 if(channel->ownerTransfer)
9897 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
d76ed9a9 9898 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
9899 saxdb_end_record(ctx);
9900}
9901
9902static void
9903chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
9904{
9905 const char *str;
9906
9907 saxdb_start_record(ctx, ntype->name, 0);
9908 switch(ntype->set_access_type)
9909 {
338a82b5 9910 case NOTE_SET_CHANNEL_ACCESS:
9911 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
9912 break;
9913 case NOTE_SET_CHANNEL_SETTER:
9914 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
9915 break;
9916 case NOTE_SET_PRIVILEGED: default:
9917 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
9918 break;
d76ed9a9 9919 }
9920 switch(ntype->visible_type)
9921 {
338a82b5 9922 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
9923 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
9924 case NOTE_VIS_PRIVILEGED:
9925 default: str = KEY_NOTE_VIS_PRIVILEGED; break;
d76ed9a9 9926 }
9927 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
9928 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
9929 saxdb_end_record(ctx);
9930}
9931
9932static void
9933write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
9934{
9935 struct do_not_register *dnr;
1136f709 9936 dict_iterator_t it, next;
d76ed9a9 9937
1136f709 9938 for(it = dict_first(dnrs); it; it = next)
d76ed9a9 9939 {
1136f709 9940 next = iter_next(it);
d76ed9a9 9941 dnr = iter_data(it);
1136f709 9942 if(dnr->expires && dnr->expires <= now)
9943 continue;
d76ed9a9 9944 saxdb_start_record(ctx, dnr->chan_name, 0);
9945 if(dnr->set)
9946 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
1136f709 9947 if(dnr->expires)
9948 {
9949 dict_remove(dnrs, iter_key(it));
9950 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
9951 }
d76ed9a9 9952 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
9953 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
9954 saxdb_end_record(ctx);
9955 }
9956}
9957
9958static int
9959chanserv_saxdb_write(struct saxdb_context *ctx)
9960{
9961 dict_iterator_t it;
9962 struct chanData *channel;
9963
c8ca69a0 9964 /* Version Control*/
9965 saxdb_start_record(ctx, KEY_VERSION_CONTROL, 1);
c52639b4 9966 saxdb_write_int(ctx, KEY_VERSION_NUMBER, CHANSERV_DB_VERSION);
c8ca69a0 9967 saxdb_end_record(ctx);
9968
d76ed9a9 9969 /* Notes */
9970 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
9971 for(it = dict_first(note_types); it; it = iter_next(it))
9972 chanserv_write_note_type(ctx, iter_data(it));
9973 saxdb_end_record(ctx);
9974
9975 /* DNRs */
9976 saxdb_start_record(ctx, KEY_DNR, 1);
9977 write_dnrs_helper(ctx, handle_dnrs);
9978 write_dnrs_helper(ctx, plain_dnrs);
9979 write_dnrs_helper(ctx, mask_dnrs);
9980 saxdb_end_record(ctx);
9981
9982 /* Channels */
9983 saxdb_start_record(ctx, KEY_CHANNELS, 1);
9984 for(channel = channelList; channel; channel = channel->next)
9985 chanserv_write_channel(ctx, channel);
9986 saxdb_end_record(ctx);
9987
9988 return 0;
9989}
9990
9991static void
30874d66 9992chanserv_db_cleanup(UNUSED_ARG(void *extra)) {
d76ed9a9 9993 unsigned int ii;
63637aea 9994 unreg_part_func(handle_part, NULL);
d76ed9a9 9995 while(channelList)
9996 unregister_channel(channelList, "terminating.");
9997 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
9998 UnlockChannel(chanserv_conf.support_channels.list[ii]);
9999 free(chanserv_conf.support_channels.list);
10000 dict_delete(handle_dnrs);
10001 dict_delete(plain_dnrs);
10002 dict_delete(mask_dnrs);
10003 dict_delete(note_types);
10004 free_string_list(chanserv_conf.eightball);
10005 free_string_list(chanserv_conf.old_ban_names);
5e6460e4 10006 free_string_list(chanserv_conf.wheel);
d76ed9a9 10007 free_string_list(chanserv_conf.set_shows);
10008 free(set_shows_list.list);
10009 free(uset_shows_list.list);
10010 while(helperList)
10011 {
10012 struct userData *helper = helperList;
10013 helperList = helperList->next;
10014 free(helper);
10015 }
10016}
10017
1136f709 10018#if defined(GCC_VARMACROS)
10019# define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
10020#elif defined(C99_VARMACROS)
10021# define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
10022#endif
d76ed9a9 10023#define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
10024#define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
10025
10026void
10027init_chanserv(const char *nick)
10028{
7637f48f 10029 struct chanNode *chan;
e03ec3dc 10030 unsigned int i;
87708af4 10031
d76ed9a9 10032 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
10033 conf_register_reload(chanserv_conf_read);
10034
1136f709 10035 if (nick) {
9d59f196 10036 reg_server_link_func(handle_server_link, NULL);
157ce145 10037 reg_new_channel_func(handle_new_channel, NULL);
fb38e2bc 10038 reg_join_func(handle_join, NULL);
63637aea 10039 reg_part_func(handle_part, NULL);
ee8c50bd 10040 reg_kick_func(handle_kick, NULL);
5a2c7cf6 10041 reg_topic_func(handle_topic, NULL);
f14b32b8 10042 reg_mode_change_func(handle_mode, NULL);
63189c10 10043 reg_nick_change_func(handle_nick_change, NULL);
81ac4787 10044 reg_auth_func(handle_auth, NULL);
1136f709 10045 }
ff3b058a 10046
3070719a 10047 reg_handle_rename_func(handle_rename, NULL);
974d3831 10048 reg_unreg_func(handle_unreg, NULL);
d76ed9a9 10049
10050 handle_dnrs = dict_new();
10051 dict_set_free_data(handle_dnrs, free);
10052 plain_dnrs = dict_new();
10053 dict_set_free_data(plain_dnrs, free);
10054 mask_dnrs = dict_new();
10055 dict_set_free_data(mask_dnrs, free);
10056
d77556d0 10057 reg_svccmd_unbind_func(handle_svccmd_unbind, NULL);
d76ed9a9 10058 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
f21ec816 10059 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+channel", NULL);
d76ed9a9 10060 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
10061 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
1136f709 10062 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
10063 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
10064 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
10065 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
d76ed9a9 10066 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
d5f787f5
MB
10067 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN|MODCMD_IGNORE_CSUSPEND, "flags", "+helping", NULL);
10068 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN|MODCMD_IGNORE_CSUSPEND, "flags", "+helping", NULL);
d76ed9a9 10069 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
10070 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
10071
ac3bdc8d 10072 DEFINE_COMMAND(pending, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
10073
d76ed9a9 10074 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
10075 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
10076
4048352e 10077 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
10078 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
10079 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
10080 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 10081 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
10082
10083 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
10084 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
4048352e 10085 DEFINE_COMMAND(mdelmanager, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
10086 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
10087 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
2a4ca4f5 10088 DEFINE_COMMAND(mdelpal, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
55342ce8 10089 DEFINE_COMMAND(mdelhalfop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 10090
c9bf23fe 10091 DEFINE_COMMAND(levels, 1, 0, NULL);
10092
4048352e 10093 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 10094 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
4048352e 10095 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 10096 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
10097
10098 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
10099 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
10100 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
10101 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
55342ce8 10102 DEFINE_COMMAND(hop, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
d76ed9a9 10103 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
10104 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
55342ce8 10105 DEFINE_COMMAND(dehop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
d76ed9a9 10106 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
10107 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
10108
c8273589 10109 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
10110 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
10111 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
10112 DEFINE_COMMAND(unban, 2, 0, "template", "hop", NULL);
10113 DEFINE_COMMAND(unbanall, 1, 0, "template", "hop", NULL);
10114 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "hop", NULL);
d76ed9a9 10115 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
c8273589 10116 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "hop", "flags", "+never_csuspend", NULL);
d76ed9a9 10117 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
10118 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
4048352e 10119 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "manager", NULL);
d76ed9a9 10120 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
4048352e 10121 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
10122 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 10123
c8273589 10124 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "manager", NULL);
23475fc6 10125 DEFINE_COMMAND(last, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "manager", NULL);
c8273589 10126 DEFINE_COMMAND(addlamer, 2, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
10127 DEFINE_COMMAND(addtimedlamer, 3, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
10128
10129 /* if you change dellamer access, see also places
10130 * like unbanme which have manager hardcoded. */
10131 DEFINE_COMMAND(dellamer, 2, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
d76ed9a9 10132 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
10133
c8273589 10134 DEFINE_COMMAND(lamers, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
10135
d76ed9a9 10136 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
10137
10138 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
10139 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
10140 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
10141 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
10142 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
10143 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
10144 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
55342ce8 10145 DEFINE_COMMAND(hlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
d76ed9a9 10146 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
10147 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
10148 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
10149 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
10150
10151 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
10152 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
10153
10154 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
10155 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
10156 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
10157 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
10158
10159 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
10160 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
10161 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
10162 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
10163 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
10164
10165 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
10166 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
10167 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
10168 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
10169 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
10170 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
b8daef0f 10171 DEFINE_COMMAND(calc, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
33741441 10172 DEFINE_COMMAND(reply, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
87708af4 10173 DEFINE_COMMAND(roulette, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
10174 DEFINE_COMMAND(shoot, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
ea54b136 10175 DEFINE_COMMAND(spin, 1, MODCMD_REQUIRE_AUTHED, "spin", "+nolog,+toy,+acceptchan", NULL);
b404335b 10176
d76ed9a9 10177 /* Channel options */
10178 DEFINE_CHANNEL_OPTION(defaulttopic);
10179 DEFINE_CHANNEL_OPTION(topicmask);
10180 DEFINE_CHANNEL_OPTION(greeting);
10181 DEFINE_CHANNEL_OPTION(usergreeting);
10182 DEFINE_CHANNEL_OPTION(modes);
10183 DEFINE_CHANNEL_OPTION(enfops);
55342ce8 10184 DEFINE_CHANNEL_OPTION(enfhalfops);
4b6129c0 10185 DEFINE_CHANNEL_OPTION(automode);
d76ed9a9 10186 DEFINE_CHANNEL_OPTION(protect);
10187 DEFINE_CHANNEL_OPTION(enfmodes);
10188 DEFINE_CHANNEL_OPTION(enftopic);
10189 DEFINE_CHANNEL_OPTION(pubcmd);
d76ed9a9 10190 DEFINE_CHANNEL_OPTION(userinfo);
10191 DEFINE_CHANNEL_OPTION(dynlimit);
10192 DEFINE_CHANNEL_OPTION(topicsnarf);
10193 DEFINE_CHANNEL_OPTION(nodelete);
10194 DEFINE_CHANNEL_OPTION(toys);
10195 DEFINE_CHANNEL_OPTION(setters);
10196 DEFINE_CHANNEL_OPTION(topicrefresh);
7637f48f 10197 DEFINE_CHANNEL_OPTION(resync);
d76ed9a9 10198 DEFINE_CHANNEL_OPTION(ctcpreaction);
31f23f13 10199 DEFINE_CHANNEL_OPTION(bantimeout);
d76ed9a9 10200 DEFINE_CHANNEL_OPTION(inviteme);
1136f709 10201 DEFINE_CHANNEL_OPTION(unreviewed);
10202 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
10203 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8b9e7d45 10204 DEFINE_CHANNEL_OPTION(maxsetinfo);
1117fc5a 10205 if(off_channel > 1)
d76ed9a9 10206 DEFINE_CHANNEL_OPTION(offchannel);
10207 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
10208
10209 /* Alias set topic to set defaulttopic for compatibility. */
10210 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
10211
10212 /* User options */
d76ed9a9 10213 DEFINE_USER_OPTION(autoinvite);
cd25f2e9 10214 DEFINE_USER_OPTION(autojoin);
d76ed9a9 10215 DEFINE_USER_OPTION(info);
c8ca69a0 10216 DEFINE_USER_OPTION(autoop);
d76ed9a9 10217
10218 /* Alias uset autovoice to uset autoop. */
c8ca69a0 10219 modcmd_register(chanserv_module, "uset autovoice", user_opt_autoop, 1, 0, NULL);
d76ed9a9 10220
10221 note_types = dict_new();
10222 dict_set_free_data(note_types, chanserv_deref_note_type);
10223 if(nick)
10224 {
a32da4c7 10225 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
1136f709 10226 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
d76ed9a9 10227 service_register(chanserv)->trigger = '!';
1c14af0b 10228 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check, NULL);
d76ed9a9 10229 }
c8ca69a0 10230
d76ed9a9 10231 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
10232
10233 if(chanserv_conf.channel_expire_frequency)
c52639b4 10234 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
d76ed9a9 10235
1136f709 10236 if(chanserv_conf.dnr_expire_frequency)
10237 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
10238
31f23f13 10239 if(chanserv_conf.ban_timeout_frequency)
10240 timeq_add(now + chanserv_conf.ban_timeout_frequency, expire_bans, NULL);
10241
d76ed9a9 10242 if(chanserv_conf.refresh_period)
10243 {
10244 time_t next_refresh;
10245 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
10246 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7637f48f 10247 timeq_add(next_refresh, chanserv_auto_resync, NULL);
10248 }
10249
10250 if (autojoin_channels && chanserv) {
10251 for (i = 0; i < autojoin_channels->used; i++) {
10252 chan = AddChannel(autojoin_channels->list[i], now, "+nt", NULL, NULL);
10253 AddChannelUser(chanserv, chan)->modes |= MODE_CHANOP;
10254 }
d76ed9a9 10255 }
1117fc5a 10256
30874d66 10257 reg_exit_func(chanserv_db_cleanup, NULL);
d76ed9a9 10258 message_register_table(msgtab);
10259}
dfaa28a4 10260