llmutelist.cpp

Go to the documentation of this file.
00001 
00033 /*
00034  * How should muting work?
00035  * Mute an avatar
00036  * Mute a specific object (accidentally spamming)
00037  *
00038  * right-click avatar, mute
00039  * see list of recent chatters, mute
00040  * type a name to mute?
00041  *
00042  * show in list whether chatter is avatar or object
00043  *
00044  * need fast lookup by id
00045  * need lookup by name, doesn't have to be fast
00046  */
00047 
00048 #include "llviewerprecompiledheaders.h"
00049 
00050 #include "llmutelist.h"
00051 
00052 #include <boost/tokenizer.hpp>
00053 
00054 #include "llcrc.h"
00055 #include "lldispatcher.h"
00056 #include "llxfermanager.h"
00057 #include "message.h"
00058 #include "lldir.h"
00059 
00060 #include "llagent.h"
00061 #include "llfloatermute.h"
00062 #include "llviewergenericmessage.h"     // for gGenericDispatcher
00063 #include "llviewerwindow.h"
00064 #include "viewer.h"
00065 #include "llworld.h" //for particle system banning
00066 
00067 LLMuteList* gMuteListp = NULL;
00068 
00069 // "emptymutelist"
00070 class LLDispatchEmptyMuteList : public LLDispatchHandler
00071 {
00072 public:
00073         virtual bool operator()(
00074                 const LLDispatcher* dispatcher,
00075                 const std::string& key,
00076                 const LLUUID& invoice,
00077                 const sparam_t& strings)
00078         {
00079                 gMuteListp->setLoaded();
00080                 return true;
00081         }
00082 };
00083 
00084 static LLDispatchEmptyMuteList sDispatchEmptyMuteList;
00085 
00086 //-----------------------------------------------------------------------------
00087 // LLMute()
00088 //-----------------------------------------------------------------------------
00089 const char BY_NAME_SUFFIX[] = " (by name)";
00090 const char AGENT_SUFFIX[] = " (resident)";
00091 const char OBJECT_SUFFIX[] = " (object)";
00092 const char GROUP_SUFFIX[] = " (group)";
00093 
00094 LLString LLMute::getDisplayName() const
00095 {
00096         LLString name_with_suffix = mName;
00097         switch (mType)
00098         {
00099                 case BY_NAME:
00100                 default:
00101                         name_with_suffix += BY_NAME_SUFFIX;
00102                         break;
00103                 case AGENT:
00104                         name_with_suffix += AGENT_SUFFIX;
00105                         break;
00106                 case OBJECT:
00107                         name_with_suffix += OBJECT_SUFFIX;
00108                         break;
00109                 case GROUP:
00110                         name_with_suffix += GROUP_SUFFIX;
00111                         break;
00112         }
00113         return name_with_suffix;
00114 }
00115 
00116 void LLMute::setFromDisplayName(const LLString& display_name)
00117 {
00118         size_t pos = 0;
00119         mName = display_name;
00120         
00121         pos = mName.rfind(GROUP_SUFFIX);
00122         if (pos != std::string::npos)
00123         {
00124                 mName.erase(pos);
00125                 mType = GROUP;
00126                 return;
00127         }
00128         
00129         pos = mName.rfind(OBJECT_SUFFIX);
00130         if (pos != std::string::npos)
00131         {
00132                 mName.erase(pos);
00133                 mType = OBJECT;
00134                 return;
00135         }
00136         
00137         pos = mName.rfind(AGENT_SUFFIX);
00138         if (pos != std::string::npos)
00139         {
00140                 mName.erase(pos);
00141                 mType = AGENT;
00142                 return;
00143         }
00144         
00145         pos = mName.rfind(BY_NAME_SUFFIX);
00146         if (pos != std::string::npos)
00147         {
00148                 mName.erase(pos);
00149                 mType = BY_NAME;
00150                 return;
00151         }
00152         
00153         llwarns << "Unable to set mute from display name " << display_name << llendl;
00154         return;
00155 }
00156 
00157 //-----------------------------------------------------------------------------
00158 // LLMuteList()
00159 //-----------------------------------------------------------------------------
00160 LLMuteList::LLMuteList() :
00161         mIsLoaded(FALSE)
00162 {
00163         LLMessageSystem* msg = gMessageSystem;
00164 
00165         // Register our various callbacks
00166         msg->setHandlerFuncFast(_PREHASH_MuteListUpdate, processMuteListUpdate);
00167         msg->setHandlerFuncFast(_PREHASH_UseCachedMuteList, processUseCachedMuteList);
00168 
00169         gGenericDispatcher.addHandler("emptymutelist", &sDispatchEmptyMuteList);
00170 }
00171 
00172 //-----------------------------------------------------------------------------
00173 // ~LLMuteList()
00174 //-----------------------------------------------------------------------------
00175 LLMuteList::~LLMuteList()
00176 {
00177 }
00178 
00179 BOOL LLMuteList::isLinden(const LLString& name) const
00180 {
00181         typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
00182         boost::char_separator<char> sep(" ");
00183         tokenizer tokens(name, sep);
00184         tokenizer::iterator token_iter = tokens.begin();
00185         
00186         if (token_iter == tokens.end()) return FALSE;
00187         token_iter++;
00188         if (token_iter == tokens.end()) return FALSE;
00189         
00190         LLString last_name = *token_iter;
00191         return last_name == "Linden";
00192 }
00193 
00194 
00195 BOOL LLMuteList::add(const LLMute& mute, U32 flags)
00196 {
00197         // Can't mute text from Lindens
00198         if ((mute.mType == LLMute::AGENT)
00199                 && isLinden(mute.mName) && (flags & LLMute::flagTextChat || flags == 0))
00200         {
00201                 gViewerWindow->alertXml("MuteLinden");
00202                 return FALSE;
00203         }
00204         
00205         // Can't mute self.
00206         if (mute.mType == LLMute::AGENT
00207                 && mute.mID == gAgent.getID())
00208         {
00209                 return FALSE;
00210         }
00211         
00212         if (mute.mType == LLMute::BY_NAME)
00213         {               
00214                 // Can't mute empty string by name
00215                 if (mute.mName.empty()) 
00216                 {
00217                         llwarns << "Trying to mute empty string by-name" << llendl;
00218                         return FALSE;
00219                 }
00220 
00221                 // Null mutes must have uuid null
00222                 if (mute.mID.notNull())
00223                 {
00224                         llwarns << "Trying to add by-name mute with non-null id" << llendl;
00225                         return FALSE;
00226                 }
00227 
00228                 std::pair<string_set_t::iterator, bool> result = mLegacyMutes.insert(mute.mName);
00229                 if (result.second)
00230                 {
00231                         llinfos << "Muting by name " << mute.mName << llendl;
00232                         updateAdd(mute);
00233                         notifyObservers();
00234                         return TRUE;
00235                 }
00236                 else
00237                 {
00238                         // was duplicate
00239                         return FALSE;
00240                 }
00241         }
00242         else
00243         {
00244                 // Need a local (non-const) copy to set up flags properly.
00245                 LLMute localmute = mute;
00246                 
00247                 // If an entry for the same entity is already in the list, remove it, saving flags as necessary.
00248                 mute_set_t::iterator it = mMutes.find(localmute);
00249                 if (it != mMutes.end())
00250                 {
00251                         // This mute is already in the list.  Save the existing entry's flags if that's warranted.
00252                         localmute.mFlags = it->mFlags;
00253                         
00254                         mMutes.erase(it);
00255                         // Don't need to call notifyObservers() here, since it will happen after the entry has been re-added below.
00256                 }
00257                 else
00258                 {
00259                         // There was no entry in the list previously.  Fake things up by making it look like the previous entry had all properties unmuted.
00260                         localmute.mFlags = LLMute::flagAll;
00261                 }
00262 
00263                 if(flags)
00264                 {
00265                         // The user passed some combination of flags.  Make sure those flag bits are turned off (i.e. those properties will be muted).
00266                         localmute.mFlags &= (~flags);
00267                 }
00268                 else
00269                 {
00270                         // The user passed 0.  Make sure all flag bits are turned off (i.e. all properties will be muted).
00271                         localmute.mFlags = 0;
00272                 }
00273                 
00274                 // (re)add the mute entry.
00275                 {                       
00276                         std::pair<mute_set_t::iterator, bool> result = mMutes.insert(localmute);
00277                         if (result.second)
00278                         {
00279                                 llinfos << "Muting " << localmute.mName << " id " << localmute.mID << " flags " << localmute.mFlags << llendl;
00280                                 updateAdd(localmute);
00281                                 notifyObservers();
00282                                 if(!(localmute.mFlags & LLMute::flagParticles))
00283                                 {
00284                                         //Kill all particle systems owned by muted task
00285                                         if(localmute.mType == LLMute::AGENT || localmute.mType == LLMute::OBJECT)
00286                                         {
00287                                                 gWorldPointer->mPartSim.clearParticlesByOwnerID(localmute.mID);
00288                                         }
00289                                 }
00290                                 return TRUE;
00291                         }
00292                 }
00293         }
00294         
00295         // If we were going to return success, we'd have done it by now.
00296         return FALSE;
00297 }
00298 
00299 void LLMuteList::updateAdd(const LLMute& mute)
00300 {
00301         // Update the database
00302         LLMessageSystem* msg = gMessageSystem;
00303         msg->newMessageFast(_PREHASH_UpdateMuteListEntry);
00304         msg->nextBlockFast(_PREHASH_AgentData);
00305         msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
00306         msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
00307         msg->nextBlockFast(_PREHASH_MuteData);
00308         msg->addUUIDFast(_PREHASH_MuteID, mute.mID);
00309         msg->addStringFast(_PREHASH_MuteName, mute.mName);
00310         msg->addS32("MuteType", mute.mType);
00311         msg->addU32("MuteFlags", mute.mFlags);
00312         gAgent.sendReliableMessage();
00313 
00314         mIsLoaded = TRUE;
00315 }
00316 
00317 
00318 BOOL LLMuteList::remove(const LLMute& mute, U32 flags)
00319 {
00320         BOOL found = FALSE;
00321         
00322         // First, remove from main list.
00323         mute_set_t::iterator it = mMutes.find(mute);
00324         if (it != mMutes.end())
00325         {
00326                 LLMute localmute = *it;
00327                 bool remove = true;
00328                 if(flags)
00329                 {
00330                         // If the user passed mute flags, we may only want to turn some flags on.
00331                         localmute.mFlags |= flags;
00332                         
00333                         if(localmute.mFlags == LLMute::flagAll)
00334                         {
00335                                 // Every currently available mute property has been masked out.
00336                                 // Remove the mute entry entirely.
00337                         }
00338                         else
00339                         {
00340                                 // Only some of the properties are masked out.  Update the entry.
00341                                 remove = false;
00342                         }
00343                 }
00344                 else
00345                 {
00346                         // The caller didn't pass any flags -- just remove the mute entry entirely.
00347                 }
00348                 
00349                 // Always remove the entry from the set -- it will be re-added with new flags if necessary.
00350                 mMutes.erase(it);
00351 
00352                 if(remove)
00353                 {
00354                         // The entry was actually removed.  Notify the server.
00355                         updateRemove(localmute);
00356                         llinfos << "Unmuting " << localmute.mName << " id " << localmute.mID << " flags " << localmute.mFlags << llendl;
00357                 }
00358                 else
00359                 {
00360                         // Flags were updated, the mute entry needs to be retransmitted to the server and re-added to the list.
00361                         mMutes.insert(localmute);
00362                         updateAdd(localmute);
00363                         llinfos << "Updating mute entry " << localmute.mName << " id " << localmute.mID << " flags " << localmute.mFlags << llendl;
00364                 }
00365                 
00366                 // Must be after erase.
00367                 notifyObservers();
00368                 found = TRUE;
00369         }
00370 
00371         // Clean up any legacy mutes
00372         string_set_t::iterator legacy_it = mLegacyMutes.find(mute.mName);
00373         if (legacy_it != mLegacyMutes.end())
00374         {
00375                 // Database representation of legacy mute is UUID null.
00376                 LLMute mute(LLUUID::null, *legacy_it, LLMute::BY_NAME);
00377                 updateRemove(mute);
00378                 mLegacyMutes.erase(legacy_it);
00379                 // Must be after erase.
00380                 notifyObservers();
00381                 found = TRUE;
00382         }
00383         
00384         return found;
00385 }
00386 
00387 
00388 void LLMuteList::updateRemove(const LLMute& mute)
00389 {
00390         LLMessageSystem* msg = gMessageSystem;
00391         msg->newMessageFast(_PREHASH_RemoveMuteListEntry);
00392         msg->nextBlockFast(_PREHASH_AgentData);
00393         msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
00394         msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
00395         msg->nextBlockFast(_PREHASH_MuteData);
00396         msg->addUUIDFast(_PREHASH_MuteID, mute.mID);
00397         msg->addString("MuteName", mute.mName);
00398         gAgent.sendReliableMessage();
00399 }
00400 
00401 
00402 std::vector<LLMute> LLMuteList::getMutes() const
00403 {
00404         std::vector<LLMute> mutes;
00405         
00406         for (mute_set_t::const_iterator it = mMutes.begin();
00407                  it != mMutes.end();
00408                  ++it)
00409         {
00410                 mutes.push_back(*it);
00411         }
00412         
00413         for (string_set_t::const_iterator it = mLegacyMutes.begin();
00414                  it != mLegacyMutes.end();
00415                  ++it)
00416         {
00417                 LLMute legacy(LLUUID::null, *it);
00418                 mutes.push_back(legacy);
00419         }
00420         
00421         std::sort(mutes.begin(), mutes.end(), compare_by_name());
00422         return mutes;
00423 }
00424 
00425 //-----------------------------------------------------------------------------
00426 // loadFromFile()
00427 //-----------------------------------------------------------------------------
00428 BOOL LLMuteList::loadFromFile(const LLString& filename)
00429 {
00430         if(!filename.size())
00431         {
00432                 llwarns << "Mute List Filename is Empty!" << llendl;
00433                 return FALSE;
00434         }
00435 
00436         FILE* fp = LLFile::fopen(filename.c_str(), "rb");               /*Flawfinder: ignore*/
00437         if (!fp)
00438         {
00439                 llwarns << "Couldn't open mute list " << filename << llendl;
00440                 return FALSE;
00441         }
00442 
00443         // *NOTE: Changing the size of these buffers will require changes
00444         // in the scanf below.
00445         char id_buffer[MAX_STRING];             /*Flawfinder: ignore*/
00446         char name_buffer[MAX_STRING];           /*Flawfinder: ignore*/
00447         char buffer[MAX_STRING];                /*Flawfinder: ignore*/
00448         while (!feof(fp) 
00449                    && fgets(buffer, MAX_STRING, fp))
00450         {
00451                 id_buffer[0] = '\0';
00452                 name_buffer[0] = '\0';
00453                 S32 type = 0;
00454                 U32 flags = 0;
00455                 sscanf( /* Flawfinder: ignore */
00456                         buffer, " %d %254s %254[^|]| %u\n", &type, id_buffer, name_buffer,
00457                         &flags);
00458                 LLUUID id = LLUUID(id_buffer);
00459                 LLMute mute(id, name_buffer, (LLMute::EType)type, flags);
00460                 if (mute.mID.isNull()
00461                         || mute.mType == LLMute::BY_NAME)
00462                 {
00463                         mLegacyMutes.insert(mute.mName);
00464                 }
00465                 else
00466                 {
00467                         mMutes.insert(mute);
00468                 }
00469         }
00470         fclose(fp);
00471         mIsLoaded = TRUE;
00472         notifyObservers();      
00473         return TRUE;
00474 }
00475 
00476 //-----------------------------------------------------------------------------
00477 // saveToFile()
00478 //-----------------------------------------------------------------------------
00479 BOOL LLMuteList::saveToFile(const LLString& filename)
00480 {
00481         if(!filename.size())
00482         {
00483                 llwarns << "Mute List Filename is Empty!" << llendl;
00484                 return FALSE;
00485         }
00486 
00487         FILE* fp = LLFile::fopen(filename.c_str(), "wb");               /*Flawfinder: ignore*/
00488         if (!fp)
00489         {
00490                 llwarns << "Couldn't open mute list " << filename << llendl;
00491                 return FALSE;
00492         }
00493         // legacy mutes have null uuid
00494         char id_string[UUID_STR_LENGTH];                /*Flawfinder: ignore*/
00495         LLUUID::null.toString(id_string);
00496         for (string_set_t::iterator it = mLegacyMutes.begin();
00497                  it != mLegacyMutes.end();
00498                  ++it)
00499         {
00500                 fprintf(fp, "%d %s %s|\n", (S32)LLMute::BY_NAME, id_string, it->c_str());
00501         }
00502         for (mute_set_t::iterator it = mMutes.begin();
00503                  it != mMutes.end();
00504                  ++it)
00505         {
00506                 it->mID.toString(id_string);
00507                 const LLString& name = it->mName;
00508                 fprintf(fp, "%d %s %s|%u\n", (S32)it->mType, id_string, name.c_str(), it->mFlags);
00509         }
00510         fclose(fp);
00511         return TRUE;
00512 }
00513 
00514 
00515 BOOL LLMuteList::isMuted(const LLUUID& id, const LLString& name, U32 flags) const
00516 {
00517         // don't need name or type for lookup
00518         LLMute mute(id);
00519         mute_set_t::const_iterator mute_it = mMutes.find(mute);
00520         if (mute_it != mMutes.end())
00521         {
00522                 // If any of the flags the caller passed are set, this item isn't considered muted for this caller.
00523                 if(flags & mute_it->mFlags)
00524                 {
00525                         return FALSE;
00526                 }
00527                 return TRUE;
00528         }
00529 
00530         // empty names can't be legacy-muted
00531         if (name.empty()) return FALSE;
00532 
00533         // Look in legacy pile
00534         string_set_t::const_iterator legacy_it = mLegacyMutes.find(name);
00535         return legacy_it != mLegacyMutes.end();
00536 }
00537 
00538 //-----------------------------------------------------------------------------
00539 // requestFromServer()
00540 //-----------------------------------------------------------------------------
00541 void LLMuteList::requestFromServer(const LLUUID& agent_id)
00542 {
00543         char agent_id_string[UUID_STR_LENGTH];          /*Flawfinder: ignore*/
00544         char filename[LL_MAX_PATH];             /*Flawfinder: ignore*/
00545         agent_id.toString(agent_id_string);
00546         snprintf(filename, sizeof(filename), "%s.cached_mute", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string).c_str());                  /* Flawfinder: ignore */
00547         LLCRC crc;
00548         crc.update(filename);
00549 
00550         LLMessageSystem* msg = gMessageSystem;
00551         msg->newMessageFast(_PREHASH_MuteListRequest);
00552         msg->nextBlockFast(_PREHASH_AgentData);
00553         msg->addUUIDFast(_PREHASH_AgentID, agent_id);
00554         msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
00555         msg->nextBlockFast(_PREHASH_MuteData);
00556         msg->addU32Fast(_PREHASH_MuteCRC, crc.getCRC());
00557         gAgent.sendReliableMessage();
00558 }
00559 
00560 //-----------------------------------------------------------------------------
00561 // cache()
00562 //-----------------------------------------------------------------------------
00563 
00564 void LLMuteList::cache(const LLUUID& agent_id)
00565 {
00566         // Write to disk even if empty.
00567         if(mIsLoaded)
00568         {
00569                 char agent_id_string[UUID_STR_LENGTH];          /*Flawfinder: ignore*/
00570                 char filename[LL_MAX_PATH];             /*Flawfinder: ignore*/
00571                 agent_id.toString(agent_id_string);
00572                 snprintf(filename, sizeof(filename), "%s.cached_mute", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string).c_str());                  /* Flawfinder: ignore */
00573                 saveToFile(filename);
00574         }
00575 }
00576 
00577 
00578 //-----------------------------------------------------------------------------
00579 // Static message handlers
00580 //-----------------------------------------------------------------------------
00581 
00582 void LLMuteList::processMuteListUpdate(LLMessageSystem* msg, void**)
00583 {
00584         llinfos << "LLMuteList::processMuteListUpdate()" << llendl;
00585         LLUUID agent_id;
00586         msg->getUUIDFast(_PREHASH_MuteData, _PREHASH_AgentID, agent_id);
00587         if(agent_id != gAgent.getID())
00588         {
00589                 llwarns << "Got an mute list update for the wrong agent." << llendl;
00590                 return;
00591         }
00592         char filename[MAX_STRING];              /*Flawfinder: ignore*/
00593         filename[0] = '\0';
00594         msg->getStringFast(_PREHASH_MuteData, _PREHASH_Filename, MAX_STRING, filename);
00595         
00596         std::string *local_filename_and_path = new std::string(gDirUtilp->getExpandedFilename( LL_PATH_CACHE, filename ));
00597         gXferManager->requestFile(local_filename_and_path->c_str(),
00598                                                           filename,
00599                                                           LL_PATH_CACHE,
00600                                                           msg->getSender(),
00601                                                           TRUE, // make the remote file temporary.
00602                                                           onFileMuteList,
00603                                                           (void**)local_filename_and_path,
00604                                                           LLXferManager::HIGH_PRIORITY);
00605 }
00606 
00607 void LLMuteList::processUseCachedMuteList(LLMessageSystem* msg, void**)
00608 {
00609         llinfos << "LLMuteList::processUseCachedMuteList()" << llendl;
00610         if (!gMuteListp) return;
00611 
00612         char agent_id_string[UUID_STR_LENGTH];          /*Flawfinder: ignore*/
00613         gAgent.getID().toString(agent_id_string);
00614         char filename[LL_MAX_PATH];             /*Flawfinder: ignore*/
00615         snprintf(filename, sizeof(filename), "%s.cached_mute", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string).c_str());                  /* Flawfinder: ignore */
00616         gMuteListp->loadFromFile(filename);
00617 }
00618 
00619 void LLMuteList::onFileMuteList(void** user_data, S32 error_code, LLExtStat ext_status)
00620 {
00621         llinfos << "LLMuteList::processMuteListFile()" << llendl;
00622         if (!gMuteListp) return;
00623 
00624         std::string *local_filename_and_path = (std::string*)user_data;
00625         if(local_filename_and_path && !local_filename_and_path->empty() && (error_code == 0))
00626         {
00627                 gMuteListp->loadFromFile(local_filename_and_path->c_str());
00628                 LLFile::remove(local_filename_and_path->c_str());
00629         }
00630         delete local_filename_and_path;
00631 }
00632 
00633 void LLMuteList::addObserver(LLMuteListObserver* observer)
00634 {
00635         mObservers.insert(observer);
00636 }
00637 
00638 void LLMuteList::removeObserver(LLMuteListObserver* observer)
00639 {
00640         mObservers.erase(observer);
00641 }
00642 
00643 void LLMuteList::setLoaded()
00644 {
00645         mIsLoaded = TRUE;
00646         notifyObservers();
00647 }
00648 
00649 void LLMuteList::notifyObservers()
00650 {
00651         // HACK: LLFloaterMute is constructed before LLMuteList,
00652         // so it can't easily observe it.  Changing this requires
00653         // much reshuffling of the startup process. JC
00654         if (gFloaterMute)
00655         {
00656                 gFloaterMute->refreshMuteList();
00657         }
00658 
00659         for (observer_set_t::iterator it = mObservers.begin();
00660                 it != mObservers.end();
00661                 )
00662         {
00663                 LLMuteListObserver* observer = *it;
00664                 observer->onChange();
00665                 // In case onChange() deleted an entry.
00666                 it = mObservers.upper_bound(observer);
00667         }
00668 }

Generated on Thu Jul 1 06:08:54 2010 for Second Life Viewer by  doxygen 1.4.7