diff options
| author | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 21:25:21 +0200 |
|---|---|---|
| committer | Andrey Lihatskiy <alihatskiy@productengine.com> | 2024-05-22 22:40:26 +0300 |
| commit | e2e37cced861b98de8c1a7c9c0d3a50d2d90e433 (patch) | |
| tree | 1bb897489ce524986f6196201c10ac0d8861aa5f /indra/newview/llmutelist.cpp | |
| parent | 069ea06848f766466f1a281144c82a0f2bd79f3a (diff) | |
Fix line endlings
Diffstat (limited to 'indra/newview/llmutelist.cpp')
| -rw-r--r-- | indra/newview/llmutelist.cpp | 2024 |
1 files changed, 1012 insertions, 1012 deletions
diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp index abb7a50b0e..2d51acc063 100644 --- a/indra/newview/llmutelist.cpp +++ b/indra/newview/llmutelist.cpp @@ -1,1012 +1,1012 @@ -/**
- * @file llmutelist.cpp
- * @author Richard Nelson, James Cook
- * @brief Management of list of muted players
- *
- * $LicenseInfo:firstyear=2003&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-/*
- * How should muting work?
- * Mute an avatar
- * Mute a specific object (accidentally spamming)
- *
- * right-click avatar, mute
- * see list of recent chatters, mute
- * type a name to mute?
- *
- * show in list whether chatter is avatar or object
- *
- * need fast lookup by id
- * need lookup by name, doesn't have to be fast
- */
-
-#include "llviewerprecompiledheaders.h"
-
-#include "llmutelist.h"
-
-#include "pipeline.h"
-
-#include <boost/tokenizer.hpp>
-#include <boost/bind.hpp>
-#include <boost/algorithm/string/replace.hpp>
-
-#include "lldispatcher.h"
-#include "llxfermanager.h"
-
-#include "llagent.h"
-#include "llavatarnamecache.h"
-#include "llviewergenericmessage.h" // for gGenericDispatcher
-#include "llworld.h" //for particle system banning
-#include "llimview.h"
-#include "llnotifications.h"
-#include "llviewercontrol.h"
-#include "llviewerobjectlist.h"
-#include "lltrans.h"
-
-namespace
-{
- // This method is used to return an object to mute given an object id.
- // Its used by the LLMute constructor and LLMuteList::isMuted.
- LLViewerObject* get_object_to_mute_from_id(LLUUID object_id)
- {
- LLViewerObject *objectp = gObjectList.findObject(object_id);
- if ((objectp) && (!objectp->isAvatar()))
- {
- LLViewerObject *parentp = (LLViewerObject *)objectp->getParent();
- if (parentp && parentp->getID() != gAgent.getID())
- {
- objectp = parentp;
- }
- }
- return objectp;
- }
-}
-
-// "emptymutelist"
-class LLDispatchEmptyMuteList : public LLDispatchHandler
-{
-public:
- virtual bool operator()(
- const LLDispatcher* dispatcher,
- const std::string& key,
- const LLUUID& invoice,
- const sparam_t& strings)
- {
- LLMuteList::getInstance()->setLoaded();
- return true;
- }
-};
-
-static LLDispatchEmptyMuteList sDispatchEmptyMuteList;
-
-//-----------------------------------------------------------------------------
-// LLMute()
-//-----------------------------------------------------------------------------
-
-LLMute::LLMute(const LLUUID& id, const std::string& name, EType type, U32 flags)
- : mID(id),
- mName(name),
- mType(type),
- mFlags(flags)
-{
- // muting is done by root objects only - try to find this objects root
- LLViewerObject* mute_object = get_object_to_mute_from_id(id);
- if(mute_object && mute_object->getID() != id)
- {
- mID = mute_object->getID();
- LLNameValue* firstname = mute_object->getNVPair("FirstName");
- LLNameValue* lastname = mute_object->getNVPair("LastName");
- if (firstname && lastname)
- {
- mName = LLCacheName::buildFullName(
- firstname->getString(), lastname->getString());
- }
- mType = mute_object->isAvatar() ? AGENT : OBJECT;
- }
-
-}
-
-
-std::string LLMute::getDisplayType() const
-{
- switch (mType)
- {
- case BY_NAME:
- default:
- return LLTrans::getString("MuteByName");
- break;
- case AGENT:
- return LLTrans::getString("MuteAgent");
- break;
- case OBJECT:
- return LLTrans::getString("MuteObject");
- break;
- case GROUP:
- return LLTrans::getString("MuteGroup");
- break;
- case EXTERNAL:
- return LLTrans::getString("MuteExternal");
- break;
- }
-}
-
-//-----------------------------------------------------------------------------
-// LLMuteList()
-//-----------------------------------------------------------------------------
-LLMuteList::LLMuteList() :
- mIsLoaded(false)
-{
- gGenericDispatcher.addHandler("emptymutelist", &sDispatchEmptyMuteList);
-
- // Register our callbacks. We may be constructed before gMessageSystem, so
- // use callWhenReady() to register them as soon as gMessageSystem becomes
- // available.
- // When using bind(), must be explicit about default arguments such as
- // that last NULL.
- gMessageSystem.callWhenReady(boost::bind(&LLMessageSystem::setHandlerFuncFast, _1,
- _PREHASH_MuteListUpdate, processMuteListUpdate,
- static_cast<void**>(NULL)));
- gMessageSystem.callWhenReady(boost::bind(&LLMessageSystem::setHandlerFuncFast, _1,
- _PREHASH_UseCachedMuteList, processUseCachedMuteList,
- static_cast<void**>(NULL)));
-
- // make sure mute list's instance gets initialized before we start any name requests
- LLAvatarNameCache::getInstance()->setAccountNameChangedCallback([this](const LLUUID& id, const LLAvatarName& av_name)
- {
- // it would be better to just pass LLAvatarName instead of doing unnesssesary copies
- // but this way is just more convinient
- onAccountNameChanged(id, av_name.getUserName());
- });
-}
-
-//-----------------------------------------------------------------------------
-// ~LLMuteList()
-//-----------------------------------------------------------------------------
-LLMuteList::~LLMuteList()
-{
-
-}
-
-void LLMuteList::cleanupSingleton()
-{
- LLAvatarNameCache::getInstance()->setAccountNameChangedCallback(NULL);
-}
-
-bool LLMuteList::isLinden(const std::string& name)
-{
- std::string username = boost::replace_all_copy(name, ".", " ");
- typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
- boost::char_separator<char> sep(" ");
- tokenizer tokens(username, sep);
- tokenizer::iterator token_iter = tokens.begin();
-
- if (token_iter == tokens.end()) return false;
- token_iter++;
- if (token_iter == tokens.end()) return false;
-
- std::string last_name = *token_iter;
- LLStringUtil::toLower(last_name);
- return last_name == "linden";
-}
-
-static LLVOAvatar* find_avatar(const LLUUID& id)
-{
- LLViewerObject *obj = gObjectList.findObject(id);
- while (obj && obj->isAttachment())
- {
- obj = (LLViewerObject *)obj->getParent();
- }
-
- if (obj && obj->isAvatar())
- {
- return (LLVOAvatar*)obj;
- }
- else
- {
- return NULL;
- }
-}
-
-bool LLMuteList::add(const LLMute& mute, U32 flags)
-{
- // Can't mute text from Lindens
- if ((mute.mType == LLMute::AGENT)
- && isLinden(mute.mName) && (flags & LLMute::flagTextChat || flags == 0))
- {
- LL_WARNS() << "Trying to mute a Linden; ignored" << LL_ENDL;
- LLNotifications::instance().add("MuteLinden", LLSD(), LLSD());
- return false;
- }
-
- // Can't mute self.
- if (mute.mType == LLMute::AGENT
- && mute.mID == gAgent.getID())
- {
- LL_WARNS() << "Trying to self; ignored" << LL_ENDL;
- return false;
- }
-
- static LLCachedControl<S32> mute_list_limit(gSavedSettings, "MuteListLimit", 1000);
- if (getMutes().size() >= mute_list_limit)
- {
- LL_WARNS() << "Mute limit is reached; ignored" << LL_ENDL;
- LLSD args;
- args["MUTE_LIMIT"] = mute_list_limit;
- LLNotifications::instance().add(LLNotification::Params("MuteLimitReached").substitutions(args));
- return false;
- }
-
- if (mute.mType == LLMute::BY_NAME)
- {
- // Can't mute empty string by name
- if (mute.mName.empty())
- {
- LL_WARNS() << "Trying to mute empty string by-name" << LL_ENDL;
- return false;
- }
-
- // Null mutes must have uuid null
- if (mute.mID.notNull())
- {
- LL_WARNS() << "Trying to add by-name mute with non-null id" << LL_ENDL;
- return false;
- }
-
- std::pair<string_set_t::iterator, bool> result = mLegacyMutes.insert(mute.mName);
- if (result.second)
- {
- LL_INFOS() << "Muting by name " << mute.mName << LL_ENDL;
- updateAdd(mute);
- notifyObservers();
- notifyObserversDetailed(mute);
- return true;
- }
- else
- {
- LL_INFOS() << "duplicate mute ignored" << LL_ENDL;
- // was duplicate
- return false;
- }
- }
- else
- {
- // Need a local (non-const) copy to set up flags properly.
- LLMute localmute = mute;
-
- // If an entry for the same entity is already in the list, remove it, saving flags as necessary.
- mute_set_t::iterator it = mMutes.find(localmute);
- if (it != mMutes.end())
- {
- // This mute is already in the list. Save the existing entry's flags if that's warranted.
- localmute.mFlags = it->mFlags;
-
- mMutes.erase(it);
- // Don't need to call notifyObservers() here, since it will happen after the entry has been re-added below.
- }
- else
- {
- // There was no entry in the list previously. Fake things up by making it look like the previous entry had all properties unmuted.
- localmute.mFlags = LLMute::flagAll;
- }
-
- if(flags)
- {
- // The user passed some combination of flags. Make sure those flag bits are turned off (i.e. those properties will be muted).
- localmute.mFlags &= (~flags);
- }
- else
- {
- // The user passed 0. Make sure all flag bits are turned off (i.e. all properties will be muted).
- localmute.mFlags = 0;
- }
-
- // (re)add the mute entry.
- {
- std::pair<mute_set_t::iterator, bool> result = mMutes.insert(localmute);
- if (result.second)
- {
- LL_INFOS() << "Muting " << localmute.mName << " id " << localmute.mID << " flags " << localmute.mFlags << LL_ENDL;
- updateAdd(localmute);
- notifyObservers();
- notifyObserversDetailed(localmute);
-
- //mute local lights that are attached to the avatar
- LLVOAvatar *avatarp = find_avatar(localmute.mID);
- if (avatarp)
- {
- LLPipeline::removeMutedAVsLights(avatarp);
- }
- //remove agent's notifications as well
- if (localmute.mType == LLMute::AGENT)
- {
- LLNotifications::instance().cancelByOwner(localmute.mID);
- }
- return true;
- }
- }
- }
-
- // If we were going to return success, we'd have done it by now.
- return false;
-}
-
-void LLMuteList::updateAdd(const LLMute& mute)
-{
- // External mutes are local only, don't send them to the server.
- if (mute.mType == LLMute::EXTERNAL)
- {
- return;
- }
-
- // Update the database
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessageFast(_PREHASH_UpdateMuteListEntry);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->nextBlockFast(_PREHASH_MuteData);
- msg->addUUIDFast(_PREHASH_MuteID, mute.mID);
- msg->addStringFast(_PREHASH_MuteName, mute.mName);
- msg->addS32("MuteType", mute.mType);
- msg->addU32("MuteFlags", mute.mFlags);
- gAgent.sendReliableMessage();
-
- if (!mIsLoaded)
- {
- LL_WARNS() << "Added elements to non-initialized block list" << LL_ENDL;
- }
- mIsLoaded = true; // why is this here? -MG
-}
-
-
-bool LLMuteList::remove(const LLMute& mute, U32 flags)
-{
- bool found = false;
-
- // First, remove from main list.
- mute_set_t::iterator it = mMutes.find(mute);
- if (it != mMutes.end())
- {
- LLMute localmute = *it;
- bool remove = true;
- if(flags)
- {
- // If the user passed mute flags, we may only want to turn some flags on.
- localmute.mFlags |= flags;
-
- if(localmute.mFlags == LLMute::flagAll)
- {
- // Every currently available mute property has been masked out.
- // Remove the mute entry entirely.
- }
- else
- {
- // Only some of the properties are masked out. Update the entry.
- remove = false;
- }
- }
- else
- {
- // The caller didn't pass any flags -- just remove the mute entry entirely.
- // set flags to notify observers with (flag being present means that something is allowed)
- localmute.mFlags = LLMute::flagAll;
- }
-
- // Always remove the entry from the set -- it will be re-added with new flags if necessary.
- mMutes.erase(it);
-
- if(remove)
- {
- // The entry was actually removed. Notify the server.
- updateRemove(localmute);
- LL_INFOS() << "Unmuting " << localmute.mName << " id " << localmute.mID << " flags " << localmute.mFlags << LL_ENDL;
- }
- else
- {
- // Flags were updated, the mute entry needs to be retransmitted to the server and re-added to the list.
- mMutes.insert(localmute);
- updateAdd(localmute);
- LL_INFOS() << "Updating mute entry " << localmute.mName << " id " << localmute.mID << " flags " << localmute.mFlags << LL_ENDL;
- }
-
- // Must be after erase.
- notifyObservers();
- notifyObserversDetailed(localmute);
- }
- else
- {
- // Clean up any legacy mutes
- string_set_t::iterator legacy_it = mLegacyMutes.find(mute.mName);
- if (legacy_it != mLegacyMutes.end())
- {
- // Database representation of legacy mute is UUID null.
- LLMute mute(LLUUID::null, *legacy_it, LLMute::BY_NAME);
- updateRemove(mute);
- mLegacyMutes.erase(legacy_it);
- // Must be after erase.
- notifyObservers();
- notifyObserversDetailed(mute);
- }
- }
-
- return found;
-}
-
-
-void LLMuteList::updateRemove(const LLMute& mute)
-{
- // External mutes are not sent to the server anyway, no need to remove them.
- if (mute.mType == LLMute::EXTERNAL)
- {
- return;
- }
-
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessageFast(_PREHASH_RemoveMuteListEntry);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->nextBlockFast(_PREHASH_MuteData);
- msg->addUUIDFast(_PREHASH_MuteID, mute.mID);
- msg->addString("MuteName", mute.mName);
- gAgent.sendReliableMessage();
-}
-
-void notify_automute_callback(const LLUUID& agent_id, const LLAvatarName& full_name, LLMuteList::EAutoReason reason)
-{
- std::string notif_name;
- switch (reason)
- {
- default:
- case LLMuteList::AR_IM:
- notif_name = "AutoUnmuteByIM";
- break;
- case LLMuteList::AR_INVENTORY:
- notif_name = "AutoUnmuteByInventory";
- break;
- case LLMuteList::AR_MONEY:
- notif_name = "AutoUnmuteByMoney";
- break;
- }
-
- LLSD args;
- args["NAME"] = full_name.getUserName();
-
- LLNotificationPtr notif_ptr = LLNotifications::instance().add(notif_name, args, LLSD());
- if (notif_ptr)
- {
- std::string message = notif_ptr->getMessage();
-
- if (reason == LLMuteList::AR_IM)
- {
- LLIMModel::getInstance()->addMessage(agent_id, SYSTEM_FROM, LLUUID::null, message);
- }
- }
-}
-
-
-bool LLMuteList::autoRemove(const LLUUID& agent_id, const EAutoReason reason)
-{
- bool removed = false;
-
- if (isMuted(agent_id))
- {
- LLMute automute(agent_id, LLStringUtil::null, LLMute::AGENT);
- removed = true;
- remove(automute);
-
- LLAvatarName av_name;
- if (LLAvatarNameCache::get(agent_id, &av_name))
- {
- // name in cache, call callback directly
- notify_automute_callback(agent_id, av_name, reason);
- }
- else
- {
- // not in cache, lookup name from cache
- LLAvatarNameCache::get(agent_id,
- boost::bind(¬ify_automute_callback, _1, _2, reason));
- }
- }
-
- return removed;
-}
-
-
-std::vector<LLMute> LLMuteList::getMutes() const
-{
- std::vector<LLMute> mutes;
-
- for (mute_set_t::const_iterator it = mMutes.begin();
- it != mMutes.end();
- ++it)
- {
- mutes.push_back(*it);
- }
-
- for (string_set_t::const_iterator it = mLegacyMutes.begin();
- it != mLegacyMutes.end();
- ++it)
- {
- LLMute legacy(LLUUID::null, *it);
- mutes.push_back(legacy);
- }
-
- std::sort(mutes.begin(), mutes.end(), compare_by_name());
- return mutes;
-}
-
-//-----------------------------------------------------------------------------
-// loadFromFile()
-//-----------------------------------------------------------------------------
-bool LLMuteList::loadFromFile(const std::string& filename)
-{
- if(!filename.size())
- {
- LL_WARNS() << "Mute List Filename is Empty!" << LL_ENDL;
- return false;
- }
-
- LLFILE* fp = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/
- if (!fp)
- {
- LL_WARNS() << "Couldn't open mute list " << filename << LL_ENDL;
- return false;
- }
-
- // *NOTE: Changing the size of these buffers will require changes
- // in the scanf below.
- char id_buffer[MAX_STRING]; /*Flawfinder: ignore*/
- char name_buffer[MAX_STRING]; /*Flawfinder: ignore*/
- char buffer[MAX_STRING]; /*Flawfinder: ignore*/
- while (!feof(fp)
- && fgets(buffer, MAX_STRING, fp))
- {
- id_buffer[0] = '\0';
- name_buffer[0] = '\0';
- S32 type = 0;
- U32 flags = 0;
- sscanf( /* Flawfinder: ignore */
- buffer, " %d %254s %254[^|]| %u\n", &type, id_buffer, name_buffer,
- &flags);
- LLUUID id = LLUUID(id_buffer);
- LLMute mute(id, std::string(name_buffer), (LLMute::EType)type, flags);
- if (mute.mID.isNull()
- || mute.mType == LLMute::BY_NAME)
- {
- mLegacyMutes.insert(mute.mName);
- }
- else
- {
- mMutes.insert(mute);
- }
- }
- fclose(fp);
- setLoaded();
-
- // server does not maintain up-to date account names (not display names!)
- // in this list, so it falls to viewer.
- pending_names_t::iterator iter = mPendingAgentNameUpdates.begin();
- pending_names_t::iterator end = mPendingAgentNameUpdates.end();
- while (iter != end)
- {
- // this will send updates to server, make sure mIsLoaded is set
- onAccountNameChanged(iter->first, iter->second);
- iter++;
- }
- mPendingAgentNameUpdates.clear();
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// saveToFile()
-//-----------------------------------------------------------------------------
-bool LLMuteList::saveToFile(const std::string& filename)
-{
- if(!filename.size())
- {
- LL_WARNS() << "Mute List Filename is Empty!" << LL_ENDL;
- return false;
- }
-
- LLFILE* fp = LLFile::fopen(filename, "wb"); /*Flawfinder: ignore*/
- if (!fp)
- {
- LL_WARNS() << "Couldn't open mute list " << filename << LL_ENDL;
- return false;
- }
- // legacy mutes have null uuid
- std::string id_string;
- LLUUID::null.toString(id_string);
- for (string_set_t::iterator it = mLegacyMutes.begin();
- it != mLegacyMutes.end();
- ++it)
- {
- fprintf(fp, "%d %s %s|\n", (S32)LLMute::BY_NAME, id_string.c_str(), it->c_str());
- }
- for (mute_set_t::iterator it = mMutes.begin();
- it != mMutes.end();
- ++it)
- {
- // Don't save external mutes as they are not sent to the server and probably won't
- //be valid next time anyway.
- if (it->mType != LLMute::EXTERNAL)
- {
- it->mID.toString(id_string);
- const std::string& name = it->mName;
- fprintf(fp, "%d %s %s|%u\n", (S32)it->mType, id_string.c_str(), name.c_str(), it->mFlags);
- }
- }
- fclose(fp);
- return true;
-}
-
-
-bool LLMuteList::isMuted(const LLUUID& id, const std::string& name, U32 flags) const
-{
- // for objects, check for muting on their parent prim
- LLViewerObject* mute_object = get_object_to_mute_from_id(id);
- LLUUID id_to_check = (mute_object) ? mute_object->getID() : id;
-
- // don't need name or type for lookup
- LLMute mute(id_to_check);
- mute_set_t::const_iterator mute_it = mMutes.find(mute);
- if (mute_it != mMutes.end())
- {
- // If any of the flags the caller passed are set, this item isn't considered muted for this caller.
- if(flags & mute_it->mFlags)
- {
- return false;
- }
- return true;
- }
-
- // empty names can't be legacy-muted
- bool avatar = mute_object && mute_object->isAvatar();
- if (name.empty() || avatar) return false;
-
- // Look in legacy pile
- string_set_t::const_iterator legacy_it = mLegacyMutes.find(name);
- return legacy_it != mLegacyMutes.end();
-}
-
-bool LLMuteList::isMuted(const std::string& username, U32 flags) const
-{
- mute_set_t::const_iterator mute_iter = mMutes.begin();
- while(mute_iter != mMutes.end())
- {
- // can't convert "leha.test" into "LeHa TesT" so username comparison is more reliable
- if (mute_iter->mType == LLMute::AGENT
- && LLCacheName::buildUsername(mute_iter->mName) == username)
- {
- return true;
- }
- mute_iter++;
- }
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// requestFromServer()
-//-----------------------------------------------------------------------------
-void LLMuteList::requestFromServer(const LLUUID& agent_id)
-{
- std::string agent_id_string;
- std::string filename;
- agent_id.toString(agent_id_string);
- filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string) + ".cached_mute";
- LLCRC crc;
- crc.update(filename);
-
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessageFast(_PREHASH_MuteListRequest);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, agent_id);
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->nextBlockFast(_PREHASH_MuteData);
- msg->addU32Fast(_PREHASH_MuteCRC, crc.getCRC());
-
- if (gDisconnected)
- {
- LL_WARNS() << "Trying to request mute list when disconnected!" << LL_ENDL;
- return;
- }
- if (!gAgent.getRegion())
- {
- LL_WARNS() << "No region for agent yet, skipping mute list request!" << LL_ENDL;
- return;
- }
- // Double amount of retries due to this request happening during busy stage
- // Ideally this should be turned into a capability
- gMessageSystem->sendReliable(gAgent.getRegionHost(), LL_DEFAULT_RELIABLE_RETRIES * 2, true, LL_PING_BASED_TIMEOUT_DUMMY, NULL, NULL);
-}
-
-//-----------------------------------------------------------------------------
-// cache()
-//-----------------------------------------------------------------------------
-
-void LLMuteList::cache(const LLUUID& agent_id)
-{
- // Write to disk even if empty.
- if(mIsLoaded)
- {
- std::string agent_id_string;
- std::string filename;
- agent_id.toString(agent_id_string);
- filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string) + ".cached_mute";
- saveToFile(filename);
- }
-}
-
-//-----------------------------------------------------------------------------
-// Static message handlers
-//-----------------------------------------------------------------------------
-
-void LLMuteList::processMuteListUpdate(LLMessageSystem* msg, void**)
-{
- LL_INFOS() << "LLMuteList::processMuteListUpdate()" << LL_ENDL;
- LLUUID agent_id;
- msg->getUUIDFast(_PREHASH_MuteData, _PREHASH_AgentID, agent_id);
- if(agent_id != gAgent.getID())
- {
- LL_WARNS() << "Got an mute list update for the wrong agent." << LL_ENDL;
- return;
- }
- std::string unclean_filename;
- msg->getStringFast(_PREHASH_MuteData, _PREHASH_Filename, unclean_filename);
- std::string filename = LLDir::getScrubbedFileName(unclean_filename);
-
- std::string *local_filename_and_path = new std::string(gDirUtilp->getExpandedFilename( LL_PATH_CACHE, filename ));
- gXferManager->requestFile(*local_filename_and_path,
- filename,
- LL_PATH_CACHE,
- msg->getSender(),
- true, // make the remote file temporary.
- onFileMuteList,
- (void**)local_filename_and_path,
- LLXferManager::HIGH_PRIORITY);
-}
-
-void LLMuteList::processUseCachedMuteList(LLMessageSystem* msg, void**)
-{
- LL_INFOS() << "LLMuteList::processUseCachedMuteList()" << LL_ENDL;
-
- std::string agent_id_string;
- gAgent.getID().toString(agent_id_string);
- std::string filename;
- filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string) + ".cached_mute";
- LLMuteList::getInstance()->loadFromFile(filename);
-}
-
-void LLMuteList::onFileMuteList(void** user_data, S32 error_code, LLExtStat ext_status)
-{
- LL_INFOS() << "LLMuteList::processMuteListFile()" << LL_ENDL;
-
- std::string* local_filename_and_path = (std::string*)user_data;
- if(local_filename_and_path && !local_filename_and_path->empty() && (error_code == 0))
- {
- LLMuteList::getInstance()->loadFromFile(*local_filename_and_path);
- LLFile::remove(*local_filename_and_path);
- }
- delete local_filename_and_path;
-}
-
-void LLMuteList::onAccountNameChanged(const LLUUID& id, const std::string& username)
-{
- if (mIsLoaded)
- {
- LLMute mute(id, username, LLMute::AGENT);
- mute_set_t::iterator mute_it = mMutes.find(mute);
- if (mute_it != mMutes.end()
- && mute_it->mName != mute.mName
- && mute_it->mType == LLMute::AGENT) // just in case, it is std::set, not map
- {
- // existing mute, but name changed, copy data
- mute.mFlags = mute_it->mFlags;
-
- // erase old variant
- mMutes.erase(mute_it);
-
- // (re)add the mute entry.
- {
- std::pair<mute_set_t::iterator, bool> result = mMutes.insert(mute);
- if (result.second)
- {
- LL_INFOS() << "Muting " << mute.mName << " id " << mute.mID << " flags " << mute.mFlags << LL_ENDL;
- updateAdd(mute);
- // Do not notify observers here, observers do not know or need to handle name changes
- // Example: block list considers notifyObserversDetailed as a selection update;
- // Various chat/voice statuses care only about id and flags
- // Since apropriate update time for account names is considered to be in 'hours' it is
- // fine not to update UI (will be fine after restart or couple other changes)
-
- }
- }
- }
- }
- else
- {
- // Delay update until we load file
- // Ex: Buddies list can arrive too early since we prerequest
- // names from Buddies list before we load blocklist
- mPendingAgentNameUpdates[id] = username;
- }
-}
-
-void LLMuteList::addObserver(LLMuteListObserver* observer)
-{
- mObservers.insert(observer);
-}
-
-void LLMuteList::removeObserver(LLMuteListObserver* observer)
-{
- mObservers.erase(observer);
-}
-
-void LLMuteList::setLoaded()
-{
- mIsLoaded = true;
- notifyObservers();
-}
-
-void LLMuteList::notifyObservers()
-{
- for (observer_set_t::iterator it = mObservers.begin();
- it != mObservers.end();
- )
- {
- LLMuteListObserver* observer = *it;
- observer->onChange();
- // In case onChange() deleted an entry.
- it = mObservers.upper_bound(observer);
- }
-}
-
-void LLMuteList::notifyObserversDetailed(const LLMute& mute)
-{
- for (observer_set_t::iterator it = mObservers.begin();
- it != mObservers.end();
- )
- {
- LLMuteListObserver* observer = *it;
- observer->onChangeDetailed(mute);
- // In case onChange() deleted an entry.
- it = mObservers.upper_bound(observer);
- }
-}
-
-LLRenderMuteList::LLRenderMuteList()
-{}
-
-bool LLRenderMuteList::saveToFile()
-{
- std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "render_mute_settings.txt");
- LLFILE* fp = LLFile::fopen(filename, "wb");
- if (!fp)
- {
- LL_WARNS() << "Couldn't open render mute list file: " << filename << LL_ENDL;
- return false;
- }
-
- for (std::map<LLUUID, S32>::iterator it = sVisuallyMuteSettingsMap.begin(); it != sVisuallyMuteSettingsMap.end(); ++it)
- {
- if (it->second != 0)
- {
- std::string id_string;
- it->first.toString(id_string);
- fprintf(fp, "%d %s [%d]\n", (S32)it->second, id_string.c_str(), (S32)sVisuallyMuteDateMap[it->first]);
- }
- }
- fclose(fp);
- return true;
-}
-
-bool LLRenderMuteList::loadFromFile()
-{
- std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "render_mute_settings.txt");
- LLFILE* fp = LLFile::fopen(filename, "rb");
- if (!fp)
- {
- LL_WARNS() << "Couldn't open render mute list file: " << filename << LL_ENDL;
- return false;
- }
-
- char id_buffer[MAX_STRING];
- char buffer[MAX_STRING];
- while (!feof(fp) && fgets(buffer, MAX_STRING, fp))
- {
- id_buffer[0] = '\0';
- S32 setting = 0;
- S32 time = 0;
- sscanf(buffer, " %d %254s [%d]\n", &setting, id_buffer, &time);
- sVisuallyMuteSettingsMap[LLUUID(id_buffer)] = setting;
- sVisuallyMuteDateMap[LLUUID(id_buffer)] = (time == 0) ? (S32)time_corrected() : time;
- }
- fclose(fp);
- return true;
-}
-
-void LLRenderMuteList::saveVisualMuteSetting(const LLUUID& agent_id, S32 setting)
-{
- if(setting == 0)
- {
- sVisuallyMuteSettingsMap.erase(agent_id);
- sVisuallyMuteDateMap.erase(agent_id);
- }
- else
- {
- sVisuallyMuteSettingsMap[agent_id] = setting;
- if (sVisuallyMuteDateMap.find(agent_id) == sVisuallyMuteDateMap.end())
- {
- sVisuallyMuteDateMap[agent_id] = (S32)time_corrected();
- }
- }
- saveToFile();
- notifyObservers();
-}
-
-S32 LLRenderMuteList::getSavedVisualMuteSetting(const LLUUID& agent_id)
-{
- std::map<LLUUID, S32>::iterator iter = sVisuallyMuteSettingsMap.find(agent_id);
- if (iter != sVisuallyMuteSettingsMap.end())
- {
- return iter->second;
- }
-
- return 0;
-}
-
-S32 LLRenderMuteList::getVisualMuteDate(const LLUUID& agent_id)
-{
- std::map<LLUUID, S32>::iterator iter = sVisuallyMuteDateMap.find(agent_id);
- if (iter != sVisuallyMuteDateMap.end())
- {
- return iter->second;
- }
-
- return 0;
-}
-
-void LLRenderMuteList::addObserver(LLMuteListObserver* observer)
-{
- mObservers.insert(observer);
-}
-
-void LLRenderMuteList::removeObserver(LLMuteListObserver* observer)
-{
- mObservers.erase(observer);
-}
-
-void LLRenderMuteList::notifyObservers()
-{
- for (observer_set_t::iterator it = mObservers.begin();
- it != mObservers.end();
- )
- {
- LLMuteListObserver* observer = *it;
- observer->onChange();
- // In case onChange() deleted an entry.
- it = mObservers.upper_bound(observer);
- }
-}
+/** + * @file llmutelist.cpp + * @author Richard Nelson, James Cook + * @brief Management of list of muted players + * + * $LicenseInfo:firstyear=2003&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +/* + * How should muting work? + * Mute an avatar + * Mute a specific object (accidentally spamming) + * + * right-click avatar, mute + * see list of recent chatters, mute + * type a name to mute? + * + * show in list whether chatter is avatar or object + * + * need fast lookup by id + * need lookup by name, doesn't have to be fast + */ + +#include "llviewerprecompiledheaders.h" + +#include "llmutelist.h" + +#include "pipeline.h" + +#include <boost/tokenizer.hpp> +#include <boost/bind.hpp> +#include <boost/algorithm/string/replace.hpp> + +#include "lldispatcher.h" +#include "llxfermanager.h" + +#include "llagent.h" +#include "llavatarnamecache.h" +#include "llviewergenericmessage.h" // for gGenericDispatcher +#include "llworld.h" //for particle system banning +#include "llimview.h" +#include "llnotifications.h" +#include "llviewercontrol.h" +#include "llviewerobjectlist.h" +#include "lltrans.h" + +namespace +{ + // This method is used to return an object to mute given an object id. + // Its used by the LLMute constructor and LLMuteList::isMuted. + LLViewerObject* get_object_to_mute_from_id(LLUUID object_id) + { + LLViewerObject *objectp = gObjectList.findObject(object_id); + if ((objectp) && (!objectp->isAvatar())) + { + LLViewerObject *parentp = (LLViewerObject *)objectp->getParent(); + if (parentp && parentp->getID() != gAgent.getID()) + { + objectp = parentp; + } + } + return objectp; + } +} + +// "emptymutelist" +class LLDispatchEmptyMuteList : public LLDispatchHandler +{ +public: + virtual bool operator()( + const LLDispatcher* dispatcher, + const std::string& key, + const LLUUID& invoice, + const sparam_t& strings) + { + LLMuteList::getInstance()->setLoaded(); + return true; + } +}; + +static LLDispatchEmptyMuteList sDispatchEmptyMuteList; + +//----------------------------------------------------------------------------- +// LLMute() +//----------------------------------------------------------------------------- + +LLMute::LLMute(const LLUUID& id, const std::string& name, EType type, U32 flags) + : mID(id), + mName(name), + mType(type), + mFlags(flags) +{ + // muting is done by root objects only - try to find this objects root + LLViewerObject* mute_object = get_object_to_mute_from_id(id); + if(mute_object && mute_object->getID() != id) + { + mID = mute_object->getID(); + LLNameValue* firstname = mute_object->getNVPair("FirstName"); + LLNameValue* lastname = mute_object->getNVPair("LastName"); + if (firstname && lastname) + { + mName = LLCacheName::buildFullName( + firstname->getString(), lastname->getString()); + } + mType = mute_object->isAvatar() ? AGENT : OBJECT; + } + +} + + +std::string LLMute::getDisplayType() const +{ + switch (mType) + { + case BY_NAME: + default: + return LLTrans::getString("MuteByName"); + break; + case AGENT: + return LLTrans::getString("MuteAgent"); + break; + case OBJECT: + return LLTrans::getString("MuteObject"); + break; + case GROUP: + return LLTrans::getString("MuteGroup"); + break; + case EXTERNAL: + return LLTrans::getString("MuteExternal"); + break; + } +} + +//----------------------------------------------------------------------------- +// LLMuteList() +//----------------------------------------------------------------------------- +LLMuteList::LLMuteList() : + mIsLoaded(false) +{ + gGenericDispatcher.addHandler("emptymutelist", &sDispatchEmptyMuteList); + + // Register our callbacks. We may be constructed before gMessageSystem, so + // use callWhenReady() to register them as soon as gMessageSystem becomes + // available. + // When using bind(), must be explicit about default arguments such as + // that last NULL. + gMessageSystem.callWhenReady(boost::bind(&LLMessageSystem::setHandlerFuncFast, _1, + _PREHASH_MuteListUpdate, processMuteListUpdate, + static_cast<void**>(NULL))); + gMessageSystem.callWhenReady(boost::bind(&LLMessageSystem::setHandlerFuncFast, _1, + _PREHASH_UseCachedMuteList, processUseCachedMuteList, + static_cast<void**>(NULL))); + + // make sure mute list's instance gets initialized before we start any name requests + LLAvatarNameCache::getInstance()->setAccountNameChangedCallback([this](const LLUUID& id, const LLAvatarName& av_name) + { + // it would be better to just pass LLAvatarName instead of doing unnesssesary copies + // but this way is just more convinient + onAccountNameChanged(id, av_name.getUserName()); + }); +} + +//----------------------------------------------------------------------------- +// ~LLMuteList() +//----------------------------------------------------------------------------- +LLMuteList::~LLMuteList() +{ + +} + +void LLMuteList::cleanupSingleton() +{ + LLAvatarNameCache::getInstance()->setAccountNameChangedCallback(NULL); +} + +bool LLMuteList::isLinden(const std::string& name) +{ + std::string username = boost::replace_all_copy(name, ".", " "); + typedef boost::tokenizer<boost::char_separator<char> > tokenizer; + boost::char_separator<char> sep(" "); + tokenizer tokens(username, sep); + tokenizer::iterator token_iter = tokens.begin(); + + if (token_iter == tokens.end()) return false; + token_iter++; + if (token_iter == tokens.end()) return false; + + std::string last_name = *token_iter; + LLStringUtil::toLower(last_name); + return last_name == "linden"; +} + +static LLVOAvatar* find_avatar(const LLUUID& id) +{ + LLViewerObject *obj = gObjectList.findObject(id); + while (obj && obj->isAttachment()) + { + obj = (LLViewerObject *)obj->getParent(); + } + + if (obj && obj->isAvatar()) + { + return (LLVOAvatar*)obj; + } + else + { + return NULL; + } +} + +bool LLMuteList::add(const LLMute& mute, U32 flags) +{ + // Can't mute text from Lindens + if ((mute.mType == LLMute::AGENT) + && isLinden(mute.mName) && (flags & LLMute::flagTextChat || flags == 0)) + { + LL_WARNS() << "Trying to mute a Linden; ignored" << LL_ENDL; + LLNotifications::instance().add("MuteLinden", LLSD(), LLSD()); + return false; + } + + // Can't mute self. + if (mute.mType == LLMute::AGENT + && mute.mID == gAgent.getID()) + { + LL_WARNS() << "Trying to self; ignored" << LL_ENDL; + return false; + } + + static LLCachedControl<S32> mute_list_limit(gSavedSettings, "MuteListLimit", 1000); + if (getMutes().size() >= mute_list_limit) + { + LL_WARNS() << "Mute limit is reached; ignored" << LL_ENDL; + LLSD args; + args["MUTE_LIMIT"] = mute_list_limit; + LLNotifications::instance().add(LLNotification::Params("MuteLimitReached").substitutions(args)); + return false; + } + + if (mute.mType == LLMute::BY_NAME) + { + // Can't mute empty string by name + if (mute.mName.empty()) + { + LL_WARNS() << "Trying to mute empty string by-name" << LL_ENDL; + return false; + } + + // Null mutes must have uuid null + if (mute.mID.notNull()) + { + LL_WARNS() << "Trying to add by-name mute with non-null id" << LL_ENDL; + return false; + } + + std::pair<string_set_t::iterator, bool> result = mLegacyMutes.insert(mute.mName); + if (result.second) + { + LL_INFOS() << "Muting by name " << mute.mName << LL_ENDL; + updateAdd(mute); + notifyObservers(); + notifyObserversDetailed(mute); + return true; + } + else + { + LL_INFOS() << "duplicate mute ignored" << LL_ENDL; + // was duplicate + return false; + } + } + else + { + // Need a local (non-const) copy to set up flags properly. + LLMute localmute = mute; + + // If an entry for the same entity is already in the list, remove it, saving flags as necessary. + mute_set_t::iterator it = mMutes.find(localmute); + if (it != mMutes.end()) + { + // This mute is already in the list. Save the existing entry's flags if that's warranted. + localmute.mFlags = it->mFlags; + + mMutes.erase(it); + // Don't need to call notifyObservers() here, since it will happen after the entry has been re-added below. + } + else + { + // There was no entry in the list previously. Fake things up by making it look like the previous entry had all properties unmuted. + localmute.mFlags = LLMute::flagAll; + } + + if(flags) + { + // The user passed some combination of flags. Make sure those flag bits are turned off (i.e. those properties will be muted). + localmute.mFlags &= (~flags); + } + else + { + // The user passed 0. Make sure all flag bits are turned off (i.e. all properties will be muted). + localmute.mFlags = 0; + } + + // (re)add the mute entry. + { + std::pair<mute_set_t::iterator, bool> result = mMutes.insert(localmute); + if (result.second) + { + LL_INFOS() << "Muting " << localmute.mName << " id " << localmute.mID << " flags " << localmute.mFlags << LL_ENDL; + updateAdd(localmute); + notifyObservers(); + notifyObserversDetailed(localmute); + + //mute local lights that are attached to the avatar + LLVOAvatar *avatarp = find_avatar(localmute.mID); + if (avatarp) + { + LLPipeline::removeMutedAVsLights(avatarp); + } + //remove agent's notifications as well + if (localmute.mType == LLMute::AGENT) + { + LLNotifications::instance().cancelByOwner(localmute.mID); + } + return true; + } + } + } + + // If we were going to return success, we'd have done it by now. + return false; +} + +void LLMuteList::updateAdd(const LLMute& mute) +{ + // External mutes are local only, don't send them to the server. + if (mute.mType == LLMute::EXTERNAL) + { + return; + } + + // Update the database + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_UpdateMuteListEntry); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_MuteData); + msg->addUUIDFast(_PREHASH_MuteID, mute.mID); + msg->addStringFast(_PREHASH_MuteName, mute.mName); + msg->addS32("MuteType", mute.mType); + msg->addU32("MuteFlags", mute.mFlags); + gAgent.sendReliableMessage(); + + if (!mIsLoaded) + { + LL_WARNS() << "Added elements to non-initialized block list" << LL_ENDL; + } + mIsLoaded = true; // why is this here? -MG +} + + +bool LLMuteList::remove(const LLMute& mute, U32 flags) +{ + bool found = false; + + // First, remove from main list. + mute_set_t::iterator it = mMutes.find(mute); + if (it != mMutes.end()) + { + LLMute localmute = *it; + bool remove = true; + if(flags) + { + // If the user passed mute flags, we may only want to turn some flags on. + localmute.mFlags |= flags; + + if(localmute.mFlags == LLMute::flagAll) + { + // Every currently available mute property has been masked out. + // Remove the mute entry entirely. + } + else + { + // Only some of the properties are masked out. Update the entry. + remove = false; + } + } + else + { + // The caller didn't pass any flags -- just remove the mute entry entirely. + // set flags to notify observers with (flag being present means that something is allowed) + localmute.mFlags = LLMute::flagAll; + } + + // Always remove the entry from the set -- it will be re-added with new flags if necessary. + mMutes.erase(it); + + if(remove) + { + // The entry was actually removed. Notify the server. + updateRemove(localmute); + LL_INFOS() << "Unmuting " << localmute.mName << " id " << localmute.mID << " flags " << localmute.mFlags << LL_ENDL; + } + else + { + // Flags were updated, the mute entry needs to be retransmitted to the server and re-added to the list. + mMutes.insert(localmute); + updateAdd(localmute); + LL_INFOS() << "Updating mute entry " << localmute.mName << " id " << localmute.mID << " flags " << localmute.mFlags << LL_ENDL; + } + + // Must be after erase. + notifyObservers(); + notifyObserversDetailed(localmute); + } + else + { + // Clean up any legacy mutes + string_set_t::iterator legacy_it = mLegacyMutes.find(mute.mName); + if (legacy_it != mLegacyMutes.end()) + { + // Database representation of legacy mute is UUID null. + LLMute mute(LLUUID::null, *legacy_it, LLMute::BY_NAME); + updateRemove(mute); + mLegacyMutes.erase(legacy_it); + // Must be after erase. + notifyObservers(); + notifyObserversDetailed(mute); + } + } + + return found; +} + + +void LLMuteList::updateRemove(const LLMute& mute) +{ + // External mutes are not sent to the server anyway, no need to remove them. + if (mute.mType == LLMute::EXTERNAL) + { + return; + } + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_RemoveMuteListEntry); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_MuteData); + msg->addUUIDFast(_PREHASH_MuteID, mute.mID); + msg->addString("MuteName", mute.mName); + gAgent.sendReliableMessage(); +} + +void notify_automute_callback(const LLUUID& agent_id, const LLAvatarName& full_name, LLMuteList::EAutoReason reason) +{ + std::string notif_name; + switch (reason) + { + default: + case LLMuteList::AR_IM: + notif_name = "AutoUnmuteByIM"; + break; + case LLMuteList::AR_INVENTORY: + notif_name = "AutoUnmuteByInventory"; + break; + case LLMuteList::AR_MONEY: + notif_name = "AutoUnmuteByMoney"; + break; + } + + LLSD args; + args["NAME"] = full_name.getUserName(); + + LLNotificationPtr notif_ptr = LLNotifications::instance().add(notif_name, args, LLSD()); + if (notif_ptr) + { + std::string message = notif_ptr->getMessage(); + + if (reason == LLMuteList::AR_IM) + { + LLIMModel::getInstance()->addMessage(agent_id, SYSTEM_FROM, LLUUID::null, message); + } + } +} + + +bool LLMuteList::autoRemove(const LLUUID& agent_id, const EAutoReason reason) +{ + bool removed = false; + + if (isMuted(agent_id)) + { + LLMute automute(agent_id, LLStringUtil::null, LLMute::AGENT); + removed = true; + remove(automute); + + LLAvatarName av_name; + if (LLAvatarNameCache::get(agent_id, &av_name)) + { + // name in cache, call callback directly + notify_automute_callback(agent_id, av_name, reason); + } + else + { + // not in cache, lookup name from cache + LLAvatarNameCache::get(agent_id, + boost::bind(¬ify_automute_callback, _1, _2, reason)); + } + } + + return removed; +} + + +std::vector<LLMute> LLMuteList::getMutes() const +{ + std::vector<LLMute> mutes; + + for (mute_set_t::const_iterator it = mMutes.begin(); + it != mMutes.end(); + ++it) + { + mutes.push_back(*it); + } + + for (string_set_t::const_iterator it = mLegacyMutes.begin(); + it != mLegacyMutes.end(); + ++it) + { + LLMute legacy(LLUUID::null, *it); + mutes.push_back(legacy); + } + + std::sort(mutes.begin(), mutes.end(), compare_by_name()); + return mutes; +} + +//----------------------------------------------------------------------------- +// loadFromFile() +//----------------------------------------------------------------------------- +bool LLMuteList::loadFromFile(const std::string& filename) +{ + if(!filename.size()) + { + LL_WARNS() << "Mute List Filename is Empty!" << LL_ENDL; + return false; + } + + LLFILE* fp = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/ + if (!fp) + { + LL_WARNS() << "Couldn't open mute list " << filename << LL_ENDL; + return false; + } + + // *NOTE: Changing the size of these buffers will require changes + // in the scanf below. + char id_buffer[MAX_STRING]; /*Flawfinder: ignore*/ + char name_buffer[MAX_STRING]; /*Flawfinder: ignore*/ + char buffer[MAX_STRING]; /*Flawfinder: ignore*/ + while (!feof(fp) + && fgets(buffer, MAX_STRING, fp)) + { + id_buffer[0] = '\0'; + name_buffer[0] = '\0'; + S32 type = 0; + U32 flags = 0; + sscanf( /* Flawfinder: ignore */ + buffer, " %d %254s %254[^|]| %u\n", &type, id_buffer, name_buffer, + &flags); + LLUUID id = LLUUID(id_buffer); + LLMute mute(id, std::string(name_buffer), (LLMute::EType)type, flags); + if (mute.mID.isNull() + || mute.mType == LLMute::BY_NAME) + { + mLegacyMutes.insert(mute.mName); + } + else + { + mMutes.insert(mute); + } + } + fclose(fp); + setLoaded(); + + // server does not maintain up-to date account names (not display names!) + // in this list, so it falls to viewer. + pending_names_t::iterator iter = mPendingAgentNameUpdates.begin(); + pending_names_t::iterator end = mPendingAgentNameUpdates.end(); + while (iter != end) + { + // this will send updates to server, make sure mIsLoaded is set + onAccountNameChanged(iter->first, iter->second); + iter++; + } + mPendingAgentNameUpdates.clear(); + + return true; +} + +//----------------------------------------------------------------------------- +// saveToFile() +//----------------------------------------------------------------------------- +bool LLMuteList::saveToFile(const std::string& filename) +{ + if(!filename.size()) + { + LL_WARNS() << "Mute List Filename is Empty!" << LL_ENDL; + return false; + } + + LLFILE* fp = LLFile::fopen(filename, "wb"); /*Flawfinder: ignore*/ + if (!fp) + { + LL_WARNS() << "Couldn't open mute list " << filename << LL_ENDL; + return false; + } + // legacy mutes have null uuid + std::string id_string; + LLUUID::null.toString(id_string); + for (string_set_t::iterator it = mLegacyMutes.begin(); + it != mLegacyMutes.end(); + ++it) + { + fprintf(fp, "%d %s %s|\n", (S32)LLMute::BY_NAME, id_string.c_str(), it->c_str()); + } + for (mute_set_t::iterator it = mMutes.begin(); + it != mMutes.end(); + ++it) + { + // Don't save external mutes as they are not sent to the server and probably won't + //be valid next time anyway. + if (it->mType != LLMute::EXTERNAL) + { + it->mID.toString(id_string); + const std::string& name = it->mName; + fprintf(fp, "%d %s %s|%u\n", (S32)it->mType, id_string.c_str(), name.c_str(), it->mFlags); + } + } + fclose(fp); + return true; +} + + +bool LLMuteList::isMuted(const LLUUID& id, const std::string& name, U32 flags) const +{ + // for objects, check for muting on their parent prim + LLViewerObject* mute_object = get_object_to_mute_from_id(id); + LLUUID id_to_check = (mute_object) ? mute_object->getID() : id; + + // don't need name or type for lookup + LLMute mute(id_to_check); + mute_set_t::const_iterator mute_it = mMutes.find(mute); + if (mute_it != mMutes.end()) + { + // If any of the flags the caller passed are set, this item isn't considered muted for this caller. + if(flags & mute_it->mFlags) + { + return false; + } + return true; + } + + // empty names can't be legacy-muted + bool avatar = mute_object && mute_object->isAvatar(); + if (name.empty() || avatar) return false; + + // Look in legacy pile + string_set_t::const_iterator legacy_it = mLegacyMutes.find(name); + return legacy_it != mLegacyMutes.end(); +} + +bool LLMuteList::isMuted(const std::string& username, U32 flags) const +{ + mute_set_t::const_iterator mute_iter = mMutes.begin(); + while(mute_iter != mMutes.end()) + { + // can't convert "leha.test" into "LeHa TesT" so username comparison is more reliable + if (mute_iter->mType == LLMute::AGENT + && LLCacheName::buildUsername(mute_iter->mName) == username) + { + return true; + } + mute_iter++; + } + return false; +} + +//----------------------------------------------------------------------------- +// requestFromServer() +//----------------------------------------------------------------------------- +void LLMuteList::requestFromServer(const LLUUID& agent_id) +{ + std::string agent_id_string; + std::string filename; + agent_id.toString(agent_id_string); + filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string) + ".cached_mute"; + LLCRC crc; + crc.update(filename); + + LLMessageSystem* msg = gMessageSystem; + msg->newMessageFast(_PREHASH_MuteListRequest); + msg->nextBlockFast(_PREHASH_AgentData); + msg->addUUIDFast(_PREHASH_AgentID, agent_id); + msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + msg->nextBlockFast(_PREHASH_MuteData); + msg->addU32Fast(_PREHASH_MuteCRC, crc.getCRC()); + + if (gDisconnected) + { + LL_WARNS() << "Trying to request mute list when disconnected!" << LL_ENDL; + return; + } + if (!gAgent.getRegion()) + { + LL_WARNS() << "No region for agent yet, skipping mute list request!" << LL_ENDL; + return; + } + // Double amount of retries due to this request happening during busy stage + // Ideally this should be turned into a capability + gMessageSystem->sendReliable(gAgent.getRegionHost(), LL_DEFAULT_RELIABLE_RETRIES * 2, true, LL_PING_BASED_TIMEOUT_DUMMY, NULL, NULL); +} + +//----------------------------------------------------------------------------- +// cache() +//----------------------------------------------------------------------------- + +void LLMuteList::cache(const LLUUID& agent_id) +{ + // Write to disk even if empty. + if(mIsLoaded) + { + std::string agent_id_string; + std::string filename; + agent_id.toString(agent_id_string); + filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string) + ".cached_mute"; + saveToFile(filename); + } +} + +//----------------------------------------------------------------------------- +// Static message handlers +//----------------------------------------------------------------------------- + +void LLMuteList::processMuteListUpdate(LLMessageSystem* msg, void**) +{ + LL_INFOS() << "LLMuteList::processMuteListUpdate()" << LL_ENDL; + LLUUID agent_id; + msg->getUUIDFast(_PREHASH_MuteData, _PREHASH_AgentID, agent_id); + if(agent_id != gAgent.getID()) + { + LL_WARNS() << "Got an mute list update for the wrong agent." << LL_ENDL; + return; + } + std::string unclean_filename; + msg->getStringFast(_PREHASH_MuteData, _PREHASH_Filename, unclean_filename); + std::string filename = LLDir::getScrubbedFileName(unclean_filename); + + std::string *local_filename_and_path = new std::string(gDirUtilp->getExpandedFilename( LL_PATH_CACHE, filename )); + gXferManager->requestFile(*local_filename_and_path, + filename, + LL_PATH_CACHE, + msg->getSender(), + true, // make the remote file temporary. + onFileMuteList, + (void**)local_filename_and_path, + LLXferManager::HIGH_PRIORITY); +} + +void LLMuteList::processUseCachedMuteList(LLMessageSystem* msg, void**) +{ + LL_INFOS() << "LLMuteList::processUseCachedMuteList()" << LL_ENDL; + + std::string agent_id_string; + gAgent.getID().toString(agent_id_string); + std::string filename; + filename = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string) + ".cached_mute"; + LLMuteList::getInstance()->loadFromFile(filename); +} + +void LLMuteList::onFileMuteList(void** user_data, S32 error_code, LLExtStat ext_status) +{ + LL_INFOS() << "LLMuteList::processMuteListFile()" << LL_ENDL; + + std::string* local_filename_and_path = (std::string*)user_data; + if(local_filename_and_path && !local_filename_and_path->empty() && (error_code == 0)) + { + LLMuteList::getInstance()->loadFromFile(*local_filename_and_path); + LLFile::remove(*local_filename_and_path); + } + delete local_filename_and_path; +} + +void LLMuteList::onAccountNameChanged(const LLUUID& id, const std::string& username) +{ + if (mIsLoaded) + { + LLMute mute(id, username, LLMute::AGENT); + mute_set_t::iterator mute_it = mMutes.find(mute); + if (mute_it != mMutes.end() + && mute_it->mName != mute.mName + && mute_it->mType == LLMute::AGENT) // just in case, it is std::set, not map + { + // existing mute, but name changed, copy data + mute.mFlags = mute_it->mFlags; + + // erase old variant + mMutes.erase(mute_it); + + // (re)add the mute entry. + { + std::pair<mute_set_t::iterator, bool> result = mMutes.insert(mute); + if (result.second) + { + LL_INFOS() << "Muting " << mute.mName << " id " << mute.mID << " flags " << mute.mFlags << LL_ENDL; + updateAdd(mute); + // Do not notify observers here, observers do not know or need to handle name changes + // Example: block list considers notifyObserversDetailed as a selection update; + // Various chat/voice statuses care only about id and flags + // Since apropriate update time for account names is considered to be in 'hours' it is + // fine not to update UI (will be fine after restart or couple other changes) + + } + } + } + } + else + { + // Delay update until we load file + // Ex: Buddies list can arrive too early since we prerequest + // names from Buddies list before we load blocklist + mPendingAgentNameUpdates[id] = username; + } +} + +void LLMuteList::addObserver(LLMuteListObserver* observer) +{ + mObservers.insert(observer); +} + +void LLMuteList::removeObserver(LLMuteListObserver* observer) +{ + mObservers.erase(observer); +} + +void LLMuteList::setLoaded() +{ + mIsLoaded = true; + notifyObservers(); +} + +void LLMuteList::notifyObservers() +{ + for (observer_set_t::iterator it = mObservers.begin(); + it != mObservers.end(); + ) + { + LLMuteListObserver* observer = *it; + observer->onChange(); + // In case onChange() deleted an entry. + it = mObservers.upper_bound(observer); + } +} + +void LLMuteList::notifyObserversDetailed(const LLMute& mute) +{ + for (observer_set_t::iterator it = mObservers.begin(); + it != mObservers.end(); + ) + { + LLMuteListObserver* observer = *it; + observer->onChangeDetailed(mute); + // In case onChange() deleted an entry. + it = mObservers.upper_bound(observer); + } +} + +LLRenderMuteList::LLRenderMuteList() +{} + +bool LLRenderMuteList::saveToFile() +{ + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "render_mute_settings.txt"); + LLFILE* fp = LLFile::fopen(filename, "wb"); + if (!fp) + { + LL_WARNS() << "Couldn't open render mute list file: " << filename << LL_ENDL; + return false; + } + + for (std::map<LLUUID, S32>::iterator it = sVisuallyMuteSettingsMap.begin(); it != sVisuallyMuteSettingsMap.end(); ++it) + { + if (it->second != 0) + { + std::string id_string; + it->first.toString(id_string); + fprintf(fp, "%d %s [%d]\n", (S32)it->second, id_string.c_str(), (S32)sVisuallyMuteDateMap[it->first]); + } + } + fclose(fp); + return true; +} + +bool LLRenderMuteList::loadFromFile() +{ + std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "render_mute_settings.txt"); + LLFILE* fp = LLFile::fopen(filename, "rb"); + if (!fp) + { + LL_WARNS() << "Couldn't open render mute list file: " << filename << LL_ENDL; + return false; + } + + char id_buffer[MAX_STRING]; + char buffer[MAX_STRING]; + while (!feof(fp) && fgets(buffer, MAX_STRING, fp)) + { + id_buffer[0] = '\0'; + S32 setting = 0; + S32 time = 0; + sscanf(buffer, " %d %254s [%d]\n", &setting, id_buffer, &time); + sVisuallyMuteSettingsMap[LLUUID(id_buffer)] = setting; + sVisuallyMuteDateMap[LLUUID(id_buffer)] = (time == 0) ? (S32)time_corrected() : time; + } + fclose(fp); + return true; +} + +void LLRenderMuteList::saveVisualMuteSetting(const LLUUID& agent_id, S32 setting) +{ + if(setting == 0) + { + sVisuallyMuteSettingsMap.erase(agent_id); + sVisuallyMuteDateMap.erase(agent_id); + } + else + { + sVisuallyMuteSettingsMap[agent_id] = setting; + if (sVisuallyMuteDateMap.find(agent_id) == sVisuallyMuteDateMap.end()) + { + sVisuallyMuteDateMap[agent_id] = (S32)time_corrected(); + } + } + saveToFile(); + notifyObservers(); +} + +S32 LLRenderMuteList::getSavedVisualMuteSetting(const LLUUID& agent_id) +{ + std::map<LLUUID, S32>::iterator iter = sVisuallyMuteSettingsMap.find(agent_id); + if (iter != sVisuallyMuteSettingsMap.end()) + { + return iter->second; + } + + return 0; +} + +S32 LLRenderMuteList::getVisualMuteDate(const LLUUID& agent_id) +{ + std::map<LLUUID, S32>::iterator iter = sVisuallyMuteDateMap.find(agent_id); + if (iter != sVisuallyMuteDateMap.end()) + { + return iter->second; + } + + return 0; +} + +void LLRenderMuteList::addObserver(LLMuteListObserver* observer) +{ + mObservers.insert(observer); +} + +void LLRenderMuteList::removeObserver(LLMuteListObserver* observer) +{ + mObservers.erase(observer); +} + +void LLRenderMuteList::notifyObservers() +{ + for (observer_set_t::iterator it = mObservers.begin(); + it != mObservers.end(); + ) + { + LLMuteListObserver* observer = *it; + observer->onChange(); + // In case onChange() deleted an entry. + it = mObservers.upper_bound(observer); + } +} |
