Adds SLURLs to antispam notifications Reduces weight of antispam to either global queue, multiple queues, or none Renames variables to make more sense Repositions code, cleans up code, uses modern C++, etc This fixes a bit of a leak or two, especially with antispam turned off! Also fixes the endless leak of queue expansion by cleaning up while idle When changing between global and individual queues, they're now purged. Also made some improvements to previous commit, woops. Also fix wrong logic for is_linden and chat source type for objects
313 lines
10 KiB
C++
313 lines
10 KiB
C++
/* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
* Version 2, December 2004
|
|
*
|
|
* Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
|
*
|
|
* Everyone is permitted to copy and distribute verbatim or modified
|
|
* copies of this license document, and changing it is allowed as long
|
|
* as the name is changed.
|
|
*
|
|
* DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
|
* TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
*
|
|
* 0. You just DO WHAT THE FUCK YOU WANT TO.
|
|
*/
|
|
|
|
#include "llviewerprecompiledheaders.h"
|
|
|
|
#include "NACLantispam.h"
|
|
#include "llagent.h"
|
|
#include "llavataractions.h"
|
|
#include "llcallbacklist.h" // For idle cleaning
|
|
#include "llnotificationsutil.h"
|
|
#include "lltrans.h"
|
|
#include "llviewerobjectlist.h"
|
|
|
|
#include <time.h>
|
|
#include <boost/lexical_cast.hpp>
|
|
|
|
class NACLAntiSpamQueue
|
|
{
|
|
friend class NACLAntiSpamRegistry;
|
|
public:
|
|
const U32& getAmount() const { return mAmount; }
|
|
const U32& getTime() const { return mTime; }
|
|
protected:
|
|
NACLAntiSpamQueue(const U32& time, const U32& amount) : mTime(time), mAmount(amount) {}
|
|
void setAmount(const U32& amount) { mAmount = amount; }
|
|
void setTime(const U32& time) { mTime = time; }
|
|
void block(const LLUUID& source) { mEntries[source.asString()].block(); }
|
|
void reset() { mEntries.clear(); }
|
|
// Returns 0 if unblocked/disabled, 1 if check results in a new block, 2 if by an existing block
|
|
U8 check(const LLUUID& source, const U32& multiplier)
|
|
{
|
|
const auto key = source.asString();
|
|
auto it = mEntries.find(key);
|
|
if (it != mEntries.end())
|
|
return it->second.blockIfNeeded(mAmount * multiplier, mTime);
|
|
mEntries[key]; // Default construct an Entry
|
|
return 0U;
|
|
}
|
|
void idle()
|
|
{
|
|
// Clean out old unblocked entries
|
|
const auto time_limit = mTime + 1; // One second after time has gone up, the next offense would reset anyway
|
|
for (auto it = mEntries.begin(); it != mEntries.end();)
|
|
{
|
|
const auto& entry = it->second;
|
|
if (entry.getBlocked() || entry.withinBlockTime(time_limit))
|
|
++it;
|
|
else it = mEntries.erase(it);
|
|
}
|
|
}
|
|
|
|
private:
|
|
class Entry
|
|
{
|
|
friend class NACLAntiSpamQueue;
|
|
protected:
|
|
void reset() { updateTime(); mAmount = 1; mBlocked = false; }
|
|
const U32& getAmount() const { return mAmount; }
|
|
const U32& getTime() const { return mTime; }
|
|
void updateTime() { mTime = time(nullptr); }
|
|
void block() { mBlocked = true; }
|
|
bool withinBlockTime(const U32& time_limit) const { return (time(nullptr) - mTime) <= time_limit; }
|
|
U8 blockIfNeeded(const U32& amount, const U32& time_limit)
|
|
{
|
|
if (mBlocked) return 2U; // Already blocked
|
|
if (withinBlockTime(time_limit))
|
|
{
|
|
if (++mAmount > amount)
|
|
{
|
|
block();
|
|
return 1U;
|
|
}
|
|
}
|
|
else reset(); // Enough time has passed to forgive or not already in list
|
|
return 0U;
|
|
}
|
|
bool getBlocked() const { return mBlocked; }
|
|
private:
|
|
U32 mAmount = 1;
|
|
std::time_t mTime = time(nullptr);
|
|
bool mBlocked = false;
|
|
};
|
|
boost::unordered_map<std::string, Entry> mEntries;
|
|
U32 mAmount, mTime;
|
|
};
|
|
|
|
bool can_block(const LLUUID& id)
|
|
{
|
|
if (id.isNull() || gAgentID == id) return false; // Can't block system or self.
|
|
if (const LLViewerObject* obj = gObjectList.findObject(id)) // From an object,
|
|
return !obj->permYouOwner(); // not own object.
|
|
return true;
|
|
}
|
|
|
|
bool is_collision_sound(const std::string& sound)
|
|
{
|
|
// The following sounds will be ignored for purposes of spam protection. They have been gathered from wiki documentation of frequent official sounds.
|
|
const std::array<const std::string, 29> COLLISION_SOUNDS = {
|
|
"dce5fdd4-afe4-4ea1-822f-dd52cac46b08",
|
|
"51011582-fbca-4580-ae9e-1a5593f094ec",
|
|
"68d62208-e257-4d0c-bbe2-20c9ea9760bb",
|
|
"75872e8c-bc39-451b-9b0b-042d7ba36cba",
|
|
"6a45ba0b-5775-4ea8-8513-26008a17f873",
|
|
"992a6d1b-8c77-40e0-9495-4098ce539694",
|
|
"2de4da5a-faf8-46be-bac6-c4d74f1e5767",
|
|
"6e3fb0f7-6d9c-42ca-b86b-1122ff562d7d",
|
|
"14209133-4961-4acc-9649-53fc38ee1667",
|
|
"bc4a4348-cfcc-4e5e-908e-8a52a8915fe6",
|
|
"9e5c1297-6eed-40c0-825a-d9bcd86e3193",
|
|
"e534761c-1894-4b61-b20c-658a6fb68157",
|
|
"8761f73f-6cf9-4186-8aaa-0948ed002db1",
|
|
"874a26fd-142f-4173-8c5b-890cd846c74d",
|
|
"0e24a717-b97e-4b77-9c94-b59a5a88b2da",
|
|
"75cf3ade-9a5b-4c4d-bb35-f9799bda7fb2",
|
|
"153c8bf7-fb89-4d89-b263-47e58b1b4774",
|
|
"55c3e0ce-275a-46fa-82ff-e0465f5e8703",
|
|
"24babf58-7156-4841-9a3f-761bdbb8e237",
|
|
"aca261d8-e145-4610-9e20-9eff990f2c12",
|
|
"0642fba6-5dcf-4d62-8e7b-94dbb529d117",
|
|
"25a863e8-dc42-4e8a-a357-e76422ace9b5",
|
|
"9538f37c-456e-4047-81be-6435045608d4",
|
|
"8c0f84c3-9afd-4396-b5f5-9bca2c911c20",
|
|
"be582e5d-b123-41a2-a150-454c39e961c8",
|
|
"c70141d4-ba06-41ea-bcbc-35ea81cb8335",
|
|
"7d1826f4-24c4-4aac-8c2e-eff45df37783",
|
|
"063c97d3-033a-4e9b-98d8-05c8074922cb",
|
|
"00000000-0000-0000-0000-000000000120"
|
|
};
|
|
|
|
for (const auto& collision : COLLISION_SOUNDS)
|
|
if (collision == sound)
|
|
return true;
|
|
return false;
|
|
}
|
|
// NaClAntiSpamRegistry
|
|
|
|
constexpr std::array<char*, NACLAntiSpamRegistry::QUEUE_MAX> QUEUE_NAME = {
|
|
"Chat",
|
|
"Inventory",
|
|
"Instant Message",
|
|
"calling card",
|
|
"sound",
|
|
"Sound Preload",
|
|
"Script Dialog",
|
|
"Teleport"
|
|
};
|
|
|
|
NACLAntiSpamRegistry::NACLAntiSpamRegistry()
|
|
{
|
|
auto control = gSavedSettings.getControl("_NACL_AntiSpamTime");
|
|
const U32 time = control->get().asInteger();
|
|
mConnections[0] = control->getSignal()->connect(boost::bind(&NACLAntiSpamRegistry::handleNaclAntiSpamTimeChanged, _2));
|
|
|
|
control = gSavedSettings.getControl("_NACL_AntiSpamAmount");
|
|
const U32 amount = control->get().asInteger();
|
|
mConnections[1] = control->getSignal()->connect(boost::bind(&NACLAntiSpamRegistry::handleNaclAntiSpamAmountChanged, _2));
|
|
|
|
control = gSavedSettings.getControl("_NACL_AntiSpamGlobalQueue");
|
|
mConnections[2] = control->getSignal()->connect(boost::bind(&NACLAntiSpamRegistry::handleNaclAntiSpamGlobalQueueChanged, _2));
|
|
initializeQueues(control->get(), time, amount);
|
|
}
|
|
|
|
void NACLAntiSpamRegistry::initializeQueues(bool global, const U32& time, const U32& amount)
|
|
{
|
|
if (global) // If Global, initialize global queue
|
|
mGlobalQueue.reset(new NACLAntiSpamQueue(time, amount));
|
|
else
|
|
{
|
|
mQueues.reset(new std::array<NACLAntiSpamQueue, QUEUE_MAX>{
|
|
NACLAntiSpamQueue(time, amount), // QUEUE_CHAT
|
|
NACLAntiSpamQueue(time, amount), // QUEUE_INVENTORY
|
|
NACLAntiSpamQueue(time, amount), // QUEUE_IM
|
|
NACLAntiSpamQueue(time, amount), // QUEUE_CALLING_CARD
|
|
NACLAntiSpamQueue(time, amount), // QUEUE_SOUND
|
|
NACLAntiSpamQueue(time, amount), // QUEUE_SOUND_PRELOAD
|
|
NACLAntiSpamQueue(time, amount), // QUEUE_SCRIPT_DIALOG
|
|
NACLAntiSpamQueue(time, amount) // QUEUE_TELEPORT
|
|
});
|
|
}
|
|
}
|
|
|
|
constexpr const char* getQueueName(const NACLAntiSpamRegistry::Type& name)
|
|
{
|
|
return name >= QUEUE_NAME.size() ? "Unknown" : QUEUE_NAME[name];
|
|
}
|
|
|
|
void NACLAntiSpamRegistry::setAllQueueTimes(U32 time)
|
|
{
|
|
if (mGlobalQueue) mGlobalQueue->setTime(time);
|
|
else for(auto& queue : *mQueues) queue.setTime(time);
|
|
}
|
|
|
|
void NACLAntiSpamRegistry::setAllQueueAmounts(U32 amount)
|
|
{
|
|
if (mGlobalQueue) mGlobalQueue->setAmount(amount);
|
|
else for (U8 queue = 0U; queue < QUEUE_MAX; ++queue)
|
|
{
|
|
auto& q = (*mQueues)[queue];
|
|
if (queue == QUEUE_SOUND || queue == QUEUE_SOUND_PRELOAD)
|
|
q.setAmount(amount*5);
|
|
else
|
|
q.setAmount(amount);
|
|
}
|
|
}
|
|
|
|
void NACLAntiSpamRegistry::blockOnQueue(const Type& name, const LLUUID& source)
|
|
{
|
|
if (name >= QUEUE_MAX)
|
|
LL_ERRS("AntiSpam") << "CODE BUG: Attempting to use a antispam queue that was outside of the reasonable range of queues. Queue: " << getQueueName(name) << LL_ENDL;
|
|
else (mGlobalQueue ? *mGlobalQueue : (*mQueues)[name]).block(source);
|
|
}
|
|
|
|
bool NACLAntiSpamRegistry::checkQueue(const Type& name, const LLUUID& source, const LFIDBearer::Type& type, const U32& multiplier)
|
|
//returns true if blocked
|
|
{
|
|
if (name >= QUEUE_MAX)
|
|
{
|
|
LL_ERRS("AntiSpam") << "CODE BUG: Attempting to check antispam queue that was outside of the reasonable range of queues. Queue: " << getQueueName(name) << LL_ENDL;
|
|
return false;
|
|
}
|
|
|
|
if (!can_block(source)) return false;
|
|
|
|
auto& queue = mGlobalQueue ? *mGlobalQueue : (*mQueues)[name];
|
|
const auto result = queue.check(source, multiplier);
|
|
if (!result) return false; // Safe
|
|
|
|
if (result != 2 // Not previously blocked
|
|
&& gSavedSettings.getBOOL("AntiSpamNotify")) // and Just blocked!
|
|
{
|
|
const std::string get_slurl_for(const LLUUID& id, const LFIDBearer::Type& type);
|
|
const auto slurl = get_slurl_for(source, type);
|
|
LLSD args;
|
|
args["SOURCE"] = slurl.empty() ? source.asString() : slurl;
|
|
args["TYPE"] = LLTrans::getString(getQueueName(name));
|
|
args["AMOUNT"] = (LLSD::Integer)(multiplier * queue.getAmount());
|
|
args["TIME"] = (LLSD::Integer)queue.getTime();
|
|
LLNotificationsUtil::add("AntiSpamBlock", args);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void NACLAntiSpamRegistry::idle()
|
|
{
|
|
if (mGlobalQueue) mGlobalQueue->idle();
|
|
else for (auto& queue : *mQueues) queue.idle();
|
|
}
|
|
|
|
void NACLAntiSpamRegistry::resetQueues()
|
|
{
|
|
if (mGlobalQueue) mGlobalQueue->reset();
|
|
else for (auto& queue : *mQueues) queue.reset();
|
|
|
|
LL_INFOS() << "AntiSpam Queues Reset" << LL_ENDL;
|
|
}
|
|
|
|
void NACLAntiSpamRegistry::purgeAllQueues()
|
|
{
|
|
// Note: These resets are upon the unique_ptr, not the Queue itself!
|
|
if (mGlobalQueue)
|
|
mGlobalQueue.reset();
|
|
else
|
|
mQueues.reset();
|
|
}
|
|
|
|
// Handlers
|
|
// static
|
|
void NACLAntiSpamRegistry::startup()
|
|
{
|
|
auto onAntiSpamToggle = [](const LLControlVariable*, const LLSD& value) {
|
|
if (value.asBoolean()) instance();
|
|
else deleteSingleton();
|
|
};
|
|
auto control = gSavedSettings.getControl("AntiSpamEnabled");
|
|
control->getSignal()->connect(onAntiSpamToggle);
|
|
onAntiSpamToggle(control, control->get());
|
|
}
|
|
|
|
// static
|
|
bool NACLAntiSpamRegistry::handleNaclAntiSpamGlobalQueueChanged(const LLSD& newvalue)
|
|
{
|
|
if (instanceExists())
|
|
{
|
|
auto& inst = instance();
|
|
inst.purgeAllQueues();
|
|
inst.initializeQueues(newvalue.asBoolean(), gSavedSettings.getU32("_NACL_AntiSpamTime"), gSavedSettings.getU32("_NACL_AntiSpamAmount"));
|
|
}
|
|
return true;
|
|
}
|
|
//static
|
|
bool NACLAntiSpamRegistry::handleNaclAntiSpamTimeChanged(const LLSD& newvalue)
|
|
{
|
|
if (auto inst = getIfExists()) inst->setAllQueueTimes(newvalue.asInteger());
|
|
return true;
|
|
}
|
|
//static
|
|
bool NACLAntiSpamRegistry::handleNaclAntiSpamAmountChanged(const LLSD& newvalue)
|
|
{
|
|
if (auto inst = getIfExists()) inst->setAllQueueAmounts(newvalue.asInteger());
|
|
return true;
|
|
} |