diff options
author | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
---|---|---|
committer | Ansariel <ansariel.hiller@phoenixviewer.com> | 2024-05-22 19:04:52 +0200 |
commit | 1b67dd855c41f5a0cda7ec2a68d98071986ca703 (patch) | |
tree | ab243607f74f78200787bba5b9b88f07ef1b966f /indra/newview/llmutelist.cpp | |
parent | 6d6eabca44d08d5b97bfe3e941d2b9687c2246ea (diff) | |
parent | e1623bb276f83a43ce7a197e388720c05bdefe61 (diff) |
Merge remote-tracking branch 'origin/main' into DRTVWR-600-maint-A
# Conflicts:
# autobuild.xml
# indra/cmake/CMakeLists.txt
# indra/cmake/GoogleMock.cmake
# indra/llaudio/llaudioengine_fmodstudio.cpp
# indra/llaudio/llaudioengine_fmodstudio.h
# indra/llaudio/lllistener_fmodstudio.cpp
# indra/llaudio/lllistener_fmodstudio.h
# indra/llaudio/llstreamingaudio_fmodstudio.cpp
# indra/llaudio/llstreamingaudio_fmodstudio.h
# indra/llcharacter/llmultigesture.cpp
# indra/llcharacter/llmultigesture.h
# indra/llimage/llimage.cpp
# indra/llimage/llimagepng.cpp
# indra/llimage/llimageworker.cpp
# indra/llimage/tests/llimageworker_test.cpp
# indra/llmessage/tests/llmockhttpclient.h
# indra/llprimitive/llgltfmaterial.h
# indra/llrender/llfontfreetype.cpp
# indra/llui/llcombobox.cpp
# indra/llui/llfolderview.cpp
# indra/llui/llfolderviewmodel.h
# indra/llui/lllineeditor.cpp
# indra/llui/lllineeditor.h
# indra/llui/lltextbase.cpp
# indra/llui/lltextbase.h
# indra/llui/lltexteditor.cpp
# indra/llui/lltextvalidate.cpp
# indra/llui/lltextvalidate.h
# indra/llui/lluictrl.h
# indra/llui/llview.cpp
# indra/llwindow/llwindowmacosx.cpp
# indra/newview/app_settings/settings.xml
# indra/newview/llappearancemgr.cpp
# indra/newview/llappearancemgr.h
# indra/newview/llavatarpropertiesprocessor.cpp
# indra/newview/llavatarpropertiesprocessor.h
# indra/newview/llbreadcrumbview.cpp
# indra/newview/llbreadcrumbview.h
# indra/newview/llbreastmotion.cpp
# indra/newview/llbreastmotion.h
# indra/newview/llconversationmodel.h
# indra/newview/lldensityctrl.cpp
# indra/newview/lldensityctrl.h
# indra/newview/llface.inl
# indra/newview/llfloatereditsky.cpp
# indra/newview/llfloatereditwater.cpp
# indra/newview/llfloateremojipicker.h
# indra/newview/llfloaterimsessiontab.cpp
# indra/newview/llfloaterprofiletexture.cpp
# indra/newview/llfloaterprofiletexture.h
# indra/newview/llgesturemgr.cpp
# indra/newview/llgesturemgr.h
# indra/newview/llimpanel.cpp
# indra/newview/llimpanel.h
# indra/newview/llinventorybridge.cpp
# indra/newview/llinventorybridge.h
# indra/newview/llinventoryclipboard.cpp
# indra/newview/llinventoryclipboard.h
# indra/newview/llinventoryfunctions.cpp
# indra/newview/llinventoryfunctions.h
# indra/newview/llinventorygallery.cpp
# indra/newview/lllistbrowser.cpp
# indra/newview/lllistbrowser.h
# indra/newview/llpanelobjectinventory.cpp
# indra/newview/llpanelprofile.cpp
# indra/newview/llpanelprofile.h
# indra/newview/llpreviewgesture.cpp
# indra/newview/llsavedsettingsglue.cpp
# indra/newview/llsavedsettingsglue.h
# indra/newview/lltooldraganddrop.cpp
# indra/newview/llurllineeditorctrl.cpp
# indra/newview/llvectorperfoptions.cpp
# indra/newview/llvectorperfoptions.h
# indra/newview/llviewerparceloverlay.cpp
# indra/newview/llviewertexlayer.cpp
# indra/newview/llviewertexturelist.cpp
# indra/newview/macmain.h
# indra/test/test.cpp
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 f5646b09c5..abb7a50b0e 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);
+ }
+}
|