phoenix-firestorm/indra/newview/NACLantispam.cpp

692 lines
16 KiB
C++

#include "llviewerprecompiledheaders.h"
#include "NACLantispam.h"
#include "llagent.h"
#include "llmutelist.h"
#include "llnotificationsutil.h"
#include "llslurl.h"
#include "llviewercontrol.h"
#include "llviewerobjectlist.h"
#include "llviewerregion.h"
#include "llworld.h"
#include <time.h>
#include <boost/regex.hpp>
const static boost::regex NEWLINES("\\n{1}");
// NACLAntiSpamQueueEntry
NACLAntiSpamQueueEntry::NACLAntiSpamQueueEntry() :
mEntryTime(0),
mEntryAmount(0),
mBlocked(false)
{
}
void NACLAntiSpamQueueEntry::clearEntry()
{
mEntryTime = 0;
mEntryAmount = 0;
mBlocked = false;
}
U32 NACLAntiSpamQueueEntry::getEntryAmount()
{
return mEntryAmount;
}
U32 NACLAntiSpamQueueEntry::getEntryTime()
{
return mEntryTime;
}
void NACLAntiSpamQueueEntry::updateEntryAmount()
{
mEntryAmount++;
}
void NACLAntiSpamQueueEntry::updateEntryTime()
{
mEntryTime = time(NULL);
}
void NACLAntiSpamQueueEntry::setBlocked()
{
mBlocked = true;
}
bool NACLAntiSpamQueueEntry::getBlocked()
{
return mBlocked;
}
// NACLAntiSpamQueue
NACLAntiSpamQueue::NACLAntiSpamQueue(U32 time, U32 amount) :
mQueueTime(time),
mQueueAmount(amount)
{
}
NACLAntiSpamQueue::~NACLAntiSpamQueue()
{
purgeEntries();
}
void NACLAntiSpamQueue::setAmount(U32 amount)
{
mQueueAmount = amount;
}
void NACLAntiSpamQueue::setTime(U32 time)
{
mQueueTime = time;
}
U32 NACLAntiSpamQueue::getTime()
{
return mQueueTime;
}
U32 NACLAntiSpamQueue::getAmount()
{
return mQueueAmount;
}
NACLAntiSpamQueueEntry* NACLAntiSpamQueue::getEntry(const LLUUID& source)
{
spam_queue_entry_map_t::iterator found = mEntries.find(source);
if (found != mEntries.end())
{
return found->second;
}
else
{
return NULL;
}
}
void NACLAntiSpamQueue::clearEntries()
{
for (spam_queue_entry_map_t::iterator it = mEntries.begin(); it != mEntries.end(); ++it)
{
//AO: Only clear entries that are not blocked.
if (!it->second->getBlocked())
{
it->second->clearEntry();
}
}
}
void NACLAntiSpamQueue::purgeEntries()
{
for (spam_queue_entry_map_t::iterator it = mEntries.begin(); it != mEntries.end(); ++it)
{
delete it->second;
}
mEntries.clear();
}
void NACLAntiSpamQueue::blockEntry(const LLUUID& source)
{
spam_queue_entry_map_t::iterator it = mEntries.find(source);
if (it == mEntries.end())
{
mEntries[source] = new NACLAntiSpamQueueEntry();
}
mEntries[source]->setBlocked();
}
S32 NACLAntiSpamQueue::checkEntry(const LLUUID& name, U32 multiplier)
// Returns 0 if unblocked, 1 if check results in a new block, 2 if by an existing block
{
spam_queue_entry_map_t::iterator it = mEntries.find(name);
if (it != mEntries.end())
{
if (it->second->getBlocked())
{
return 2;
}
U32 eTime = it->second->getEntryTime();
U32 currentTime = time(0);
if ((currentTime - eTime) <= mQueueTime)
{
it->second->updateEntryAmount();
U32 eAmount = it->second->getEntryAmount();
if (eAmount > (mQueueAmount * multiplier))
{
it->second->setBlocked();
return 1;
}
else
{
return 0;
}
}
else
{
it->second->clearEntry();
it->second->updateEntryAmount();
it->second->updateEntryTime();
return 0;
}
}
else
{
LL_DEBUGS("AntiSpam") << "New queue entry:" << name.asString() << LL_ENDL;
NACLAntiSpamQueueEntry* entry = new NACLAntiSpamQueueEntry();
entry->updateEntryAmount();
entry->updateEntryTime();
mEntries[name] = entry;
return 0;
}
}
// NACLAntiSpamRegistry
static const char* QUEUE_NAME[ANTISPAM_QUEUE_MAX] = {
"Chat",
"Inventory",
"Instant Message",
"Calling Card",
"Sound",
"Sound Preload",
"Script Dialog",
"Teleport"
};
NACLAntiSpamRegistry::NACLAntiSpamRegistry() :
mGlobalTime(gSavedSettings.getU32("_NACL_AntiSpamTime")),
mGlobalAmount(gSavedSettings.getU32("_NACL_AntiSpamAmount")),
mGlobalQueue(gSavedSettings.getBOOL("_NACL_AntiSpamGlobalQueue"))
{
for (S32 queue = 0; queue < ANTISPAM_QUEUE_MAX; ++queue)
{
mQueues[queue] = new NACLAntiSpamQueue(mGlobalTime, mGlobalAmount);
}
}
NACLAntiSpamRegistry::~NACLAntiSpamRegistry()
{
purgeAllQueues();
for (S32 queue = 0; queue < ANTISPAM_QUEUE_MAX; ++queue)
{
delete mQueues[queue];
}
}
const char* NACLAntiSpamRegistry::getQueueName(EAntispamQueue queue)
{
if (queue >= ANTISPAM_QUEUE_MAX)
{
return "Unknown";
}
return QUEUE_NAME[queue];
}
void NACLAntiSpamRegistry::setRegisteredQueueTime(EAntispamQueue queue, U32 time)
{
if (queue >= ANTISPAM_QUEUE_MAX || mQueues[queue] == NULL)
{
LL_ERRS("AntiSpam") << "CODE BUG: Attempting to use a antispam queue that was not created or was outside of the reasonable range of queues. Queue: " << getQueueName(queue) << LL_ENDL;
return;
}
mQueues[queue]->setTime(time);
}
void NACLAntiSpamRegistry::setRegisteredQueueAmount(EAntispamQueue queue, U32 amount)
{
if (queue >= ANTISPAM_QUEUE_MAX || mQueues[queue] == NULL)
{
LL_ERRS("AntiSpam") << "CODE BUG: Attempting to use a antispam queue that was not created or was outside of the reasonable range of queues. Queue: " << getQueueName(queue) << LL_ENDL;
return;
}
mQueues[queue]->setAmount(amount);
}
void NACLAntiSpamRegistry::setAllQueueTimes(U32 time)
{
mGlobalTime = time;
for (S32 queue = 0; queue < ANTISPAM_QUEUE_MAX; ++queue)
{
if (mQueues[queue])
{
mQueues[queue]->setTime(time);
}
}
}
void NACLAntiSpamRegistry::setAllQueueAmounts(U32 amount)
{
mGlobalAmount = amount;
for (S32 queue = 0; queue < ANTISPAM_QUEUE_MAX; ++queue)
{
if (!mQueues[queue])
{
continue;
}
if (queue == ANTISPAM_QUEUE_SOUND || queue == ANTISPAM_QUEUE_SOUND_PRELOAD)
{
mQueues[queue]->setAmount(amount * 5);
}
else
{
mQueues[queue]->setAmount(amount);
}
}
}
void NACLAntiSpamRegistry::clearRegisteredQueue(EAntispamQueue queue)
{
if (queue >= ANTISPAM_QUEUE_MAX || mQueues[queue] == NULL)
{
LL_ERRS("AntiSpam") << "CODE BUG: Attempting to use a antispam queue that was not created or was outside of the reasonable range of queues. Queue: " << getQueueName(queue) << LL_ENDL;
return;
}
mQueues[queue]->clearEntries();
}
void NACLAntiSpamRegistry::purgeRegisteredQueue(EAntispamQueue queue)
{
if (queue >= ANTISPAM_QUEUE_MAX || mQueues[queue] == NULL)
{
LL_ERRS("AntiSpam") << "CODE BUG: Attempting to use a antispam queue that was not created or was outside of the reasonable range of queues. Queue: " << getQueueName(queue) << LL_ENDL;
return;
}
mQueues[queue]->purgeEntries();
}
void NACLAntiSpamRegistry::blockOnQueue(EAntispamQueue queue, const LLUUID& source)
{
static LLCachedControl<bool> useAntiSpam(gSavedSettings, "UseAntiSpam");
if (!useAntiSpam)
{
return;
}
if (mGlobalQueue)
{
blockGlobalEntry(source);
}
else
{
if (queue >= ANTISPAM_QUEUE_MAX || mQueues[queue] == NULL)
{
LL_ERRS("AntiSpam") << "CODE BUG: Attempting to use a antispam queue that was not created or was outside of the reasonable range of queues. Queue: " << getQueueName(queue) << LL_ENDL;
return;
}
mQueues[queue]->blockEntry(source);
}
}
void NACLAntiSpamRegistry::blockGlobalEntry(const LLUUID& source)
{
spam_queue_entry_map_t::iterator it = mGlobalEntries.find(source);
if (it == mGlobalEntries.end())
{
mGlobalEntries[source] = new NACLAntiSpamQueueEntry();
}
mGlobalEntries[source]->setBlocked();
}
bool NACLAntiSpamRegistry::checkQueue(EAntispamQueue queue, const LLUUID& source, EAntispamSource sourcetype, U32 multiplier)
// returns TRUE if blocked, FALSE otherwise
{
// skip all checks if we're we've been administratively turned off
static LLCachedControl<bool> useAntiSpam(gSavedSettings, "UseAntiSpam");
static LLCachedControl<bool> useAntiSpamMine(gSavedSettings, "FSUseAntiSpamMine");
if (!useAntiSpam)
{
return false;
}
if (source.isNull() || gAgentID == source)
{
return false;
}
if (sourcetype == ANTISPAM_SOURCE_OBJECT)
{
LLViewerObject* obj = gObjectList.findObject(source);
if (obj && obj->permYouOwner() && !useAntiSpamMine)
{
return false;
}
}
S32 result = 0;
if (mGlobalQueue)
{
result = checkGlobalEntry(source, multiplier);
}
else
{
if (queue >= ANTISPAM_QUEUE_MAX || mQueues[queue] == NULL)
{
LL_ERRS("AntiSpam") << "CODE BUG: Attempting to use a antispam queue that was not created or was outside of the reasonable range of queues. Queue: " << getQueueName(queue) << LL_ENDL;
return false;
}
result = mQueues[queue]->checkEntry(source, multiplier);
}
if (result == 0) // safe
{
return false;
}
if (result == 2) // previously blocked
{
return true;
}
if (result == 1) // newly blocked, result == 1
{
if (!LLMuteList::getInstance()->isMuted(source))
{
AntispamObjectData data;
data.mName = source.asString();
data.mQueue = queue;
data.mCount = multiplier * mQueues[queue]->getAmount();
data.mPeriod = mQueues[queue]->getTime();
data.mNotificationId = "AntiSpamBlocked";
if (sourcetype == ANTISPAM_SOURCE_OBJECT)
{
bool sent = false;
for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin();
iter != LLWorld::getInstance()->getRegionList().end(); ++iter)
{
LLViewerRegion* region = *iter;
if (gMessageSystem && region && region->isAlive())
{
gMessageSystem->newMessage(_PREHASH_RequestObjectPropertiesFamily);
gMessageSystem->nextBlockFast(_PREHASH_AgentData);
gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgentID);
gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
gMessageSystem->addU32Fast(_PREHASH_RequestFlags, 0);
gMessageSystem->addUUIDFast(_PREHASH_ObjectID, source);
gMessageSystem->sendReliable(region->getHost());
sent = true;
}
}
if (sent)
{
mObjectData[source] = data;
}
}
else if (sourcetype == ANTISPAM_SOURCE_AGENT)
{
LLUUID request_id;
request_id.generate();
LLAvatarNameCache::callback_connection_t cb = LLAvatarNameCache::get(source, boost::bind(&NACLAntiSpamRegistry::onAvatarNameCallback, this, _1, _2, data, request_id));
mAvatarNameCallbackConnections.insert(std::make_pair(request_id, cb));
}
}
LL_INFOS("AntiSpam") << "Blocked " << source.asString() << " for spamming a " << getQueueName(queue) << " (" << multiplier * mQueues[queue]->getAmount() << ") times in " << mQueues[queue]->getTime() << " seconds." << LL_ENDL;
return true;
}
// fallback, should not get here
return false;
}
bool NACLAntiSpamRegistry::checkNewlineFlood(EAntispamQueue queue, const LLUUID& source, const std::string& message)
{
if (!isBlockedOnQueue(ANTISPAM_QUEUE_IM, source))
{
static LLCachedControl<U32> _NACL_AntiSpamNewlines(gSavedSettings, "_NACL_AntiSpamNewlines");
boost::sregex_iterator iter(message.begin(), message.end(), NEWLINES);
if ((std::abs(std::distance(iter, boost::sregex_iterator())) > _NACL_AntiSpamNewlines))
{
blockOnQueue(ANTISPAM_QUEUE_IM, source);
if (!LLMuteList::getInstance()->isMuted(source))
{
LL_INFOS("AntiSpam") << "[antispam] blocked owner due to too many newlines: " << source << LL_ENDL;
AntispamObjectData data;
data.mName = source.asString();
data.mQueue = queue;
data.mCount = _NACL_AntiSpamNewlines();
data.mPeriod = 0;
data.mNotificationId = (queue == ANTISPAM_QUEUE_IM ? "AntiSpamImNewLineFloodBlocked" : "AntiSpamChatNewLineFloodBlocked");
LLUUID request_id;
request_id.generate();
LLAvatarNameCache::callback_connection_t cb = LLAvatarNameCache::get(source,
boost::bind(&NACLAntiSpamRegistry::onAvatarNameCallback,
this, _1, _2, data, request_id));
mAvatarNameCallbackConnections.insert( std::make_pair( request_id, cb ) );
}
return true;
}
return false;
}
return true;
}
bool NACLAntiSpamRegistry::isBlockedOnQueue(EAntispamQueue queue, const LLUUID& source)
{
// skip all checks if we're we've been administratively turned off
static LLCachedControl<bool> useAntiSpam(gSavedSettings, "UseAntiSpam");
if (!useAntiSpam)
{
return false;
}
if (mGlobalQueue)
{
spam_queue_entry_map_t::iterator found = mGlobalEntries.find(source);
if (found != mGlobalEntries.end())
{
return (found->second->getBlocked());
}
else
{
return false;
}
}
else
{
if (queue >= ANTISPAM_QUEUE_MAX || mQueues[queue] == NULL)
{
LL_ERRS("AntiSpam") << "CODE BUG: Attempting to use a antispam queue that was not created or was outside of the reasonable range of queues. Queue: " << getQueueName(queue) << LL_ENDL;
return false;
}
NACLAntiSpamQueueEntry* entry = mQueues[queue]->getEntry(source);
return (entry && entry->getBlocked());
}
}
// Global queue
void NACLAntiSpamRegistry::setGlobalQueue(bool value)
{
purgeAllQueues();
mGlobalQueue = value;
}
void NACLAntiSpamRegistry::setGlobalAmount(U32 amount)
{
mGlobalAmount = amount;
}
void NACLAntiSpamRegistry::setGlobalTime(U32 time)
{
mGlobalTime = time;
}
void NACLAntiSpamRegistry::clearAllQueues()
{
if (mGlobalQueue)
{
clearGlobalEntries();
}
else
{
for (S32 queue = 0; queue < ANTISPAM_QUEUE_MAX; ++queue)
{
if (mQueues[queue])
{
mQueues[queue]->clearEntries();
}
}
}
}
void NACLAntiSpamRegistry::purgeAllQueues()
{
std::map<LLUUID, LLAvatarNameCache::callback_connection_t>::iterator it = mAvatarNameCallbackConnections.begin();
for (; it != mAvatarNameCallbackConnections.end(); ++it)
{
if (it->second.connected())
{
it->second.disconnect();
}
}
mAvatarNameCallbackConnections.clear();
if (mGlobalQueue)
{
purgeGlobalEntries();
}
else
{
for (S32 queue = 0; queue < ANTISPAM_QUEUE_MAX; ++queue)
{
if (mQueues[queue])
{
mQueues[queue]->purgeEntries();
}
}
}
mObjectData.clear();
}
S32 NACLAntiSpamRegistry::checkGlobalEntry(const LLUUID& source, U32 multiplier)
{
spam_queue_entry_map_t::iterator it = mGlobalEntries.find(source);
if (it != mGlobalEntries.end())
{
if (it->second->getBlocked())
{
return 2;
}
U32 eTime = it->second->getEntryTime();
U32 currentTime = time(NULL);
if ((currentTime - eTime) <= mGlobalTime)
{
it->second->updateEntryAmount();
U32 eAmount = it->second->getEntryAmount();
if (eAmount > (mGlobalAmount * multiplier))
{
return 1;
}
else
{
return 0;
}
}
else
{
it->second->clearEntry();
it->second->updateEntryAmount();
it->second->updateEntryTime();
return 0;
}
}
else
{
NACLAntiSpamQueueEntry* entry = new NACLAntiSpamQueueEntry();
entry->updateEntryAmount();
entry->updateEntryTime();
mGlobalEntries[source] = entry;
return 0;
}
}
void NACLAntiSpamRegistry::clearGlobalEntries()
{
for (spam_queue_entry_map_t::iterator it = mGlobalEntries.begin(); it != mGlobalEntries.end(); ++it)
{
it->second->clearEntry();
}
}
void NACLAntiSpamRegistry::purgeGlobalEntries()
{
for (spam_queue_entry_map_t::iterator it = mGlobalEntries.begin(); it != mGlobalEntries.end(); ++it)
{
delete it->second;
}
mGlobalEntries.clear();
}
void NACLAntiSpamRegistry::processObjectPropertiesFamily(LLMessageSystem* msg)
{
static LLCachedControl<bool> useAntiSpam(gSavedSettings, "UseAntiSpam");
if (!useAntiSpam)
{
return;
}
LLUUID id;
LLUUID owner_id;
std::string name;
msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, id);
msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_OwnerID, owner_id);
msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Name, name);
std::map<LLUUID, AntispamObjectData>::iterator found = mObjectData.find(id);
if (found != mObjectData.end())
{
AntispamObjectData data = found->second;
if (!isBlockedOnQueue(data.mQueue, owner_id))
{
data.mName = LLSLURL("objectim", id, "").getSLURLString() + "?name=" + LLURI::escape(name) + "&owner=" + owner_id.asString();
notify(data);
}
mObjectData.erase(found);
}
}
void NACLAntiSpamRegistry::onAvatarNameCallback(const LLUUID& av_id, const LLAvatarName& av_name, AntispamObjectData data, const LLUUID& request_id)
{
std::map<LLUUID, LLAvatarNameCache::callback_connection_t>::iterator found = mAvatarNameCallbackConnections.find(request_id);
if (found != mAvatarNameCallbackConnections.end())
{
if (found->second.connected())
{
found->second.disconnect();
}
mAvatarNameCallbackConnections.erase(found);
}
data.mName = LLSLURL("agent", av_id, "inspect").getSLURLString();
notify(data);
}
void NACLAntiSpamRegistry::notify(AntispamObjectData data)
{
LLSD args;
args["SOURCE"] = data.mName;
args["QUEUE"] = getQueueName(data.mQueue);
args["COUNT"] = llformat("%d", data.mCount);
args["PERIOD"] = llformat("%d", data.mPeriod);
LLNotificationsUtil::add(data.mNotificationId, args);
}