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