123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032 |
- /**
- * @file llcachename.cpp
- * @brief A hierarchical cache of first and last names queried based on UUID.
- *
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "linden_common.h"
- #include <algorithm>
- #include "llcachename.h"
- #include "lldbstrings.h"
- #include "hbfastmap.h"
- #include "llframetimer.h"
- #include "llmessage.h"
- #include "llhost.h"
- #include "llrand.h"
- #include "llsdserialize.h"
- // LLSD serialization constants
- static const std::string AGENTS("agents");
- static const std::string GROUPS("groups");
- static const std::string CTIME("ctime");
- static const std::string FIRST("first");
- static const std::string LAST("last");
- static const std::string NAME("name");
- // We track name requests in flight for up to this long and hold on requesting
- // them again during this time.
- constexpr U32 PENDING_TIMEOUT_SECS = 300;
- // Globals
- LLCacheName* gCacheNamep = NULL;
- // ----------------------------------------------------------------------------
- // LLCacheNameEntry class
- // ----------------------------------------------------------------------------
- class LLCacheNameEntry
- {
- public:
- LLCacheNameEntry();
- public:
- bool mIsGroup;
- U32 mCreateTime; // unix time_t
- // *TODO: collapse names to one field, which will eliminate many string
- // compares on "Resident"
- std::string mFirstName;
- std::string mLastName;
- std::string mGroupName;
- };
- LLCacheNameEntry::LLCacheNameEntry()
- : mIsGroup(false),
- mCreateTime(0)
- {
- }
- class PendingReply
- {
- public:
- PendingReply(const LLUUID& id, const LLHost& host)
- : mID(id),
- mHost(host)
- {
- }
- boost::signals2::connection setCallback(const LLCacheNameCallback& cb)
- {
- return mSignal.connect(cb);
- }
- void done() { mID.setNull(); }
- bool isDone() const { return mID.isNull(); }
- public:
- LLUUID mID;
- LLCacheNameSignal mSignal;
- LLHost mHost;
- };
- class ReplySender
- {
- public:
- ReplySender(LLMessageSystem* msg);
- ~ReplySender();
- void send(const LLUUID& id, const LLCacheNameEntry& entry,
- const LLHost& host);
- private:
- void flush();
- private:
- LLMessageSystem* mMsg;
- bool mPending;
- bool mCurrIsGroup;
- LLHost mCurrHost;
- };
- ReplySender::ReplySender(LLMessageSystem* msg)
- : mMsg(msg),
- mPending(false),
- mCurrIsGroup(false)
- {
- }
- ReplySender::~ReplySender()
- {
- flush();
- }
- void ReplySender::send(const LLUUID& id, const LLCacheNameEntry& entry,
- const LLHost& host)
- {
- if (mPending && (mCurrIsGroup != entry.mIsGroup || mCurrHost != host))
- {
- flush();
- }
- if (!mPending)
- {
- mPending = true;
- mCurrIsGroup = entry.mIsGroup;
- mCurrHost = host;
- if (mCurrIsGroup)
- {
- mMsg->newMessageFast(_PREHASH_UUIDGroupNameReply);
- }
- else
- {
- mMsg->newMessageFast(_PREHASH_UUIDNameReply);
- }
- }
- mMsg->nextBlockFast(_PREHASH_UUIDNameBlock);
- mMsg->addUUIDFast(_PREHASH_ID, id);
- if (mCurrIsGroup)
- {
- mMsg->addStringFast(_PREHASH_GroupName, entry.mGroupName);
- }
- else
- {
- mMsg->addStringFast(_PREHASH_FirstName, entry.mFirstName);
- mMsg->addStringFast(_PREHASH_LastName, entry.mLastName);
- }
- if (mMsg->isSendFullFast(_PREHASH_UUIDNameBlock))
- {
- flush();
- }
- }
- void ReplySender::flush()
- {
- if (mPending)
- {
- mMsg->sendReliable(mCurrHost);
- mPending = false;
- }
- }
- // ----------------------------------------------------------------------------
- // LLCacheName::Impl class: hides gory details from main API
- // ----------------------------------------------------------------------------
- typedef std::list<PendingReply*> reply_map_t;
- typedef fast_hmap<LLUUID, U32> pending_map_t;
- typedef fast_hmap<LLUUID, LLCacheNameEntry*> cache_map_t;
- typedef std::map<std::string, LLUUID> revcache_map_t;
- class LLCacheName::Impl
- {
- protected:
- LOG_CLASS(LLCacheName::Impl);
- public:
- Impl(LLMessageSystem* msg);
- ~Impl();
- bool getName(const LLUUID& id, std::string& first, std::string& last);
- boost::signals2::connection addPending(const LLUUID& id,
- const LLCacheNameCallback& callback);
- void addPending(const LLUUID& id, const LLHost& host);
- void processPendingAsks();
- void processPendingReplies();
- void sendRequest(const char* msg_name, const uuid_list_t& queue);
- bool isRequestPending(const LLUUID& id);
- // Message system callbacks.
- void processUUIDRequest(LLMessageSystem* msg, bool is_group);
- void processUUIDReply(LLMessageSystem* msg, bool is_group);
- static void handleUUIDNameRequest(LLMessageSystem* msg, void** userdata);
- static void handleUUIDNameReply(LLMessageSystem* msg, void** userdata);
- static void handleUUIDGroupNameRequest(LLMessageSystem* msg,
- void** userdata);
- static void handleUUIDGroupNameReply(LLMessageSystem* msg,
- void** userdata);
- public:
- LLMessageSystem* mMsg;
- LLHost mUpstreamHost;
- cache_map_t mCache; // The map of UUIDs to names
- revcache_map_t mReverseCache; // Map of names to UUIDs
- uuid_list_t mAskNameQueue;
- uuid_list_t mAskGroupQueue; // UUIDs to ask our upstream host about
- // UUIDs that have been requested but are not in cache yet.
- pending_map_t mPendingQueue;
- reply_map_t mReplyQueue; // Requests awaiting replies from us
- LLCacheNameSignal mSignal;
- LLFrameTimer mProcessTimer;
- std::string mLoadingString;
- std::string mNobodyString;
- std::string mNoneString;
- };
- LLCacheName::Impl::Impl(LLMessageSystem* msg)
- : mMsg(msg),
- mLoadingString("(loading...)"),
- mNobodyString("(nobody)"),
- mNoneString("(none)")
- {
- mMsg->setHandlerFuncFast(_PREHASH_UUIDNameRequest,
- handleUUIDNameRequest, (void**)this);
- mMsg->setHandlerFuncFast(_PREHASH_UUIDNameReply,
- handleUUIDNameReply, (void**)this);
- mMsg->setHandlerFuncFast(_PREHASH_UUIDGroupNameRequest,
- handleUUIDGroupNameRequest, (void**)this);
- mMsg->setHandlerFuncFast(_PREHASH_UUIDGroupNameReply,
- handleUUIDGroupNameReply, (void**)this);
- }
- LLCacheName::Impl::~Impl()
- {
- for (auto it = mCache.begin(), end = mCache.end(); it != end; ++it)
- {
- delete it->second;
- }
- mCache.clear();
- std::for_each(mReplyQueue.begin(), mReplyQueue.end(), DeletePointer());
- mReplyQueue.clear();
- }
- boost::signals2::connection LLCacheName::Impl::addPending(const LLUUID& id,
- const LLCacheNameCallback& callback)
- {
- PendingReply* reply = new PendingReply(id, LLHost());
- boost::signals2::connection res = reply->setCallback(callback);
- mReplyQueue.push_back(reply);
- return res;
- }
- void LLCacheName::Impl::addPending(const LLUUID& id, const LLHost& host)
- {
- PendingReply* reply = new PendingReply(id, host);
- mReplyQueue.push_back(reply);
- }
- bool LLCacheName::Impl::getName(const LLUUID& id, std::string& first,
- std::string& last)
- {
- if (id.isNull())
- {
- first = mNobodyString;
- last.clear();
- return true;
- }
- LLCacheNameEntry* entryp = get_ptr_in_map(mCache, id);
- if (entryp)
- {
- first = entryp->mFirstName;
- last = entryp->mLastName;
- return true;
- }
- first = mLoadingString;
- last.clear();
- if (!isRequestPending(id))
- {
- mAskNameQueue.emplace(id);
- }
- return false;
- }
- void LLCacheName::Impl::processPendingAsks()
- {
- sendRequest(_PREHASH_UUIDNameRequest, mAskNameQueue);
- sendRequest(_PREHASH_UUIDGroupNameRequest, mAskGroupQueue);
- mAskNameQueue.clear();
- mAskGroupQueue.clear();
- }
- void LLCacheName::Impl::processPendingReplies()
- {
- reply_map_t::iterator reply_queue_begin = mReplyQueue.begin();
- reply_map_t::iterator reply_queue_end = mReplyQueue.end();
- reply_map_t::iterator it;
- // First call all the callbacks, because they might send messages.
- std::string fullname;
- for (it = reply_queue_begin; it != reply_queue_end; ++it)
- {
- PendingReply* replyp = *it;
- LLCacheNameEntry* entryp = get_ptr_in_map(mCache, replyp->mID);
- if (!entryp) continue;
- if (entryp->mIsGroup)
- {
- (replyp->mSignal)(replyp->mID, entryp->mGroupName, true);
- }
- else
- {
- fullname = LLCacheName::buildFullName(entryp->mFirstName,
- entryp->mLastName);
- (replyp->mSignal)(replyp->mID, fullname, false);
- }
- }
- // Forward on all replies, if needed.
- ReplySender sender(mMsg);
- for (it = reply_queue_begin; it != reply_queue_end; ++it)
- {
- PendingReply* replyp = *it;
- LLCacheNameEntry* entryp = get_ptr_in_map(mCache, replyp->mID);
- if (!entryp) continue;
- if (replyp->mHost.isOk())
- {
- sender.send(replyp->mID, *entryp, replyp->mHost);
- }
- replyp->done();
- }
- for (it = reply_queue_begin; it != reply_queue_end; )
- {
- reply_map_t::iterator curit = it++;
- PendingReply* replyp = *curit;
- if (replyp->isDone())
- {
- delete replyp;
- mReplyQueue.erase(curit);
- }
- }
- }
- void LLCacheName::Impl::sendRequest(const char* msg_name,
- const uuid_list_t& queue)
- {
- if (queue.empty())
- {
- return;
- }
- bool start_new_message = true;
- for (uuid_list_t::const_iterator it = queue.begin(), end = queue.end();
- it != end; ++it)
- {
- if (start_new_message)
- {
- start_new_message = false;
- mMsg->newMessageFast(msg_name);
- }
- mMsg->nextBlockFast(_PREHASH_UUIDNameBlock);
- mMsg->addUUIDFast(_PREHASH_ID, *it);
- if (mMsg->isSendFullFast(_PREHASH_UUIDNameBlock))
- {
- start_new_message = true;
- mMsg->sendReliable(mUpstreamHost);
- }
- }
- if (!start_new_message)
- {
- mMsg->sendReliable(mUpstreamHost);
- }
- }
- bool LLCacheName::Impl::isRequestPending(const LLUUID& id)
- {
- U32 now = (U32)time(NULL);
- U32 expire_time = now - PENDING_TIMEOUT_SECS;
- pending_map_t::iterator iter = mPendingQueue.find(id);
- if (iter == mPendingQueue.end() || iter->second < expire_time)
- {
- mPendingQueue[id] = now;
- return false;
- }
- return true;
- }
- void LLCacheName::Impl::processUUIDRequest(LLMessageSystem* msg, bool is_group)
- {
- // You should only get this message if the cache is at the simulator level,
- // hence having an upstream provider.
- if (!mUpstreamHost.isOk())
- {
- llwarns << "Got UUID name/group request, but no upstream provider !"
- << llendl;
- return;
- }
- LLHost from_host = msg->getSender();
- ReplySender sender(msg);
- S32 count = msg->getNumberOfBlocksFast(_PREHASH_UUIDNameBlock);
- for (S32 i = 0; i < count; ++i)
- {
- LLUUID id;
- msg->getUUIDFast(_PREHASH_UUIDNameBlock, _PREHASH_ID, id, i);
- LLCacheNameEntry* entryp = get_ptr_in_map(mCache, id);
- if (entryp)
- {
- if (is_group != entryp->mIsGroup)
- {
- llwarns << "Asked for " << (is_group ? "group" : "user")
- << " name, but found "
- << (entryp->mIsGroup ? "group" : "user") << ": " << id
- << llendl;
- }
- else // It is in the cache, so send it as the reply
- {
- sender.send(id, *entryp, from_host);
- }
- }
- else
- {
- if (!isRequestPending(id))
- {
- if (is_group)
- {
- mAskGroupQueue.emplace(id);
- }
- else
- {
- mAskNameQueue.emplace(id);
- }
- }
- addPending(id, from_host);
- }
- }
- }
- void LLCacheName::Impl::processUUIDReply(LLMessageSystem* msg, bool is_group)
- {
- S32 count = msg->getNumberOfBlocksFast(_PREHASH_UUIDNameBlock);
- for (S32 i = 0; i < count; ++i)
- {
- LLUUID id;
- msg->getUUIDFast(_PREHASH_UUIDNameBlock, _PREHASH_ID, id, i);
- LLCacheNameEntry* entryp = get_ptr_in_map(mCache, id);
- if (!entryp)
- {
- entryp = new LLCacheNameEntry;
- mCache[id] = entryp;
- }
- mPendingQueue.erase(id);
- entryp->mIsGroup = is_group;
- entryp->mCreateTime = (U32)time(NULL);
- if (is_group)
- {
- msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_GroupName,
- entryp->mGroupName, i);
- LLStringFn::replace_ascii_controlchars(entryp->mGroupName,
- LL_UNKNOWN_CHAR);
- }
- else
- {
- msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_FirstName,
- entryp->mFirstName, i);
- msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_LastName,
- entryp->mLastName, i);
- }
- if (!is_group)
- {
- // NOTE: Very occasionally the server sends down a full name in the
- // first name field with an empty last name, for example,
- // first = "Ladanie1 Resident", last = "". I cannot reproduce this,
- // nor can I find a bug in the server code. Ensure "Resident" does
- // not appear via cleanFullName, because buildFullName() only
- // checks last name. JC
- std::string full_name;
- if (entryp->mLastName.empty())
- {
- full_name = cleanFullName(entryp->mFirstName);
- // Fix what we are putting in the cache
- entryp->mFirstName = full_name;
- entryp->mLastName = "Resident";
- }
- else
- {
- full_name = LLCacheName::buildFullName(entryp->mFirstName,
- entryp->mLastName);
- }
- mSignal(id, full_name, false);
- // Do not keep "Resident" for reverse lookups
- full_name = cleanFullName(full_name);
- mReverseCache[full_name] = id;
- }
- else
- {
- mSignal(id, entryp->mGroupName, true);
- mReverseCache[entryp->mGroupName] = id;
- }
- }
- }
- //static
- void LLCacheName::Impl::handleUUIDNameReply(LLMessageSystem* msg,
- void** datap)
- {
- ((LLCacheName::Impl*)datap)->processUUIDReply(msg, false);
- }
- //static
- void LLCacheName::Impl::handleUUIDNameRequest(LLMessageSystem* msg,
- void** datap)
- {
- ((LLCacheName::Impl*)datap)->processUUIDRequest(msg, false);
- }
- //static
- void LLCacheName::Impl::handleUUIDGroupNameRequest(LLMessageSystem* msg,
- void** datap)
- {
- ((LLCacheName::Impl*)datap)->processUUIDRequest(msg, true);
- }
- //static
- void LLCacheName::Impl::handleUUIDGroupNameReply(LLMessageSystem* msg,
- void** datap)
- {
- ((LLCacheName::Impl*)datap)->processUUIDReply(msg, true);
- }
- // ----------------------------------------------------------------------------
- // LLCacheName class proper
- // ----------------------------------------------------------------------------
- LLCacheName::LLCacheName(LLMessageSystem* msgp, const std::string& loading,
- const std::string& nobody, const std::string& none)
- : impl(*new Impl(msgp))
- {
- if (!loading.empty())
- {
- impl.mLoadingString = loading;
- }
- if (!nobody.empty())
- {
- impl.mNobodyString = nobody;
- }
- if (!none.empty())
- {
- impl.mNoneString = none;
- }
- }
- LLCacheName::~LLCacheName()
- {
- delete &impl;
- }
- void LLCacheName::setUpstream(const LLHost& upstream_host)
- {
- impl.mUpstreamHost = upstream_host;
- }
- boost::signals2::connection LLCacheName::addObserver(const LLCacheNameCallback& cb)
- {
- return impl.mSignal.connect(cb);
- }
- bool LLCacheName::importFile(std::istream& istr)
- {
- LLSD data;
- if (LLSDSerialize::fromXMLDocument(data, istr) ==
- LLSDParser::PARSE_FAILURE)
- {
- return false;
- }
- // We will expire entries more than a week old
- U32 now = (U32)time(NULL);
- constexpr U32 SECS_PER_DAY = 60 * 60 * 24;
- U32 delete_before_time = now - (7 * SECS_PER_DAY);
- // Iterate over the agents
- S32 count = 0;
- LLSD agents = data[AGENTS];
- LLSD::map_iterator iter = agents.beginMap();
- LLSD::map_iterator end = agents.endMap();
- for ( ; iter != end; ++iter)
- {
- LLUUID id(iter->first);
- LLSD agent = iter->second;
- U32 ctime = (U32)agent[CTIME].asInteger();
- if (ctime < delete_before_time) continue;
- LLCacheNameEntry* entry = new LLCacheNameEntry();
- entry->mIsGroup = false;
- entry->mCreateTime = ctime;
- entry->mFirstName = agent[FIRST].asString();
- entry->mLastName = agent[LAST].asString();
- impl.mCache[id] = entry;
- std::string fullname = buildFullName(entry->mFirstName,
- entry->mLastName);
- // Do not keep "Resident" for reverse lookups:
- fullname = cleanFullName(fullname);
- impl.mReverseCache[fullname] = id;
- ++count;
- }
- llinfos << "Loaded " << count << " avatar names" << llendl;
- count = 0;
- LLSD groups = data[GROUPS];
- iter = groups.beginMap();
- end = groups.endMap();
- for ( ; iter != end; ++iter)
- {
- LLUUID id(iter->first);
- LLSD group = iter->second;
- U32 ctime = (U32)group[CTIME].asInteger();
- if (ctime < delete_before_time) continue;
- LLCacheNameEntry* entry = new LLCacheNameEntry();
- entry->mIsGroup = true;
- entry->mCreateTime = ctime;
- entry->mGroupName = group[NAME].asString();
- impl.mCache[id] = entry;
- impl.mReverseCache[entry->mGroupName] = id;
- ++count;
- }
- llinfos << "Loaded " << count << " group names." << llendl;
- return true;
- }
- void LLCacheName::exportFile(std::ostream& ostr)
- {
- LLSD data;
- for (cache_map_t::iterator iter = impl.mCache.begin(),
- end = impl.mCache.end();
- iter != end; ++iter)
- {
- // Only write entries for which we have valid data.
- LLCacheNameEntry* entry = iter->second;
- if (!entry || entry->mFirstName.find('?') != std::string::npos ||
- entry->mGroupName.find('?') != std::string::npos)
- {
- continue;
- }
- // Store it
- LLUUID id = iter->first;
- std::string id_str = id.asString();
- // IDEVO TODO: Should we store SLIDs with last name "Resident" or not?
- if (!entry->mFirstName.empty() && !entry->mLastName.empty())
- {
- data[AGENTS][id_str][FIRST] = entry->mFirstName;
- data[AGENTS][id_str][LAST] = entry->mLastName;
- data[AGENTS][id_str][CTIME] = (S32)entry->mCreateTime;
- }
- else if (entry->mIsGroup && !entry->mGroupName.empty())
- {
- data[GROUPS][id_str][NAME] = entry->mGroupName;
- data[GROUPS][id_str][CTIME] = (S32)entry->mCreateTime;
- }
- }
- LLSDSerialize::toPrettyXML(data, ostr);
- }
- bool LLCacheName::getName(const LLUUID& id, std::string& first,
- std::string& last)
- {
- return impl.getName(id, first, last);
- }
- bool LLCacheName::getFullName(const LLUUID& id, std::string& fullname)
- {
- std::string first_name, last_name;
- bool res = impl.getName(id, first_name, last_name);
- fullname = buildFullName(first_name, last_name);
- return res;
- }
- bool LLCacheName::getGroupName(const LLUUID& id, std::string& group)
- {
- if (id.isNull())
- {
- group = impl.mNoneString;
- return true;
- }
- LLCacheNameEntry* entryp = get_ptr_in_map(impl.mCache,id);
- if (entryp && entryp->mGroupName.empty())
- {
- // COUNTER-HACK to combat James' HACK in exportFile()... This group
- // name was loaded from a name cache that did not bother to save the
- // group name ==> we must ask for it.
- LL_DEBUGS("NameCache") << "Queuing HACK group request: " << id
- << LL_ENDL;
- entryp = NULL;
- }
- if (entryp)
- {
- group = entryp->mGroupName;
- return true;
- }
- group = impl.mLoadingString;
- if (!impl.isRequestPending(id))
- {
- impl.mAskGroupQueue.emplace(id);
- }
- return false;
- }
- bool LLCacheName::getUUID(const std::string& first, const std::string& last,
- LLUUID& id)
- {
- std::string full_name = buildFullName(first, last);
- return getUUID(full_name, id);
- }
- bool LLCacheName::getUUID(const std::string& full_name, LLUUID& id)
- {
- revcache_map_t::iterator iter =
- impl.mReverseCache.find(cleanFullName(full_name));
- if (iter == impl.mReverseCache.end())
- {
- return false;
- }
- id = iter->second;
- return true;
- }
- // This is a little bit kludgy. LLCacheNameCallback is a slot instead of a
- // function pointer. The reason it is a slot is so that the legacy get()
- // function below can bind an old callback and pass it as a slot. The reason it
- // is not a boost::function is so that trackable behavior does not get lost. As
- // a result, we have to bind the slot to a signal to call it, even when we call
- // it immediately. -Steve
- // NOTE: Even though passing first and last name is a bit of extra overhead, it
- // eliminates the potential need for any parsing should any code need to handle
- // first and last name independently.
- boost::signals2::connection LLCacheName::get(const LLUUID& id, bool is_group,
- const LLCacheNameCallback& callback)
- {
- boost::signals2::connection res;
- if (id.isNull())
- {
- LLCacheNameSignal signal;
- signal.connect(callback);
- signal(id, is_group ? impl.mNoneString : impl.mNobodyString, is_group);
- return res;
- }
- LLCacheNameEntry* entryp = get_ptr_in_map(impl.mCache, id);
- if (entryp)
- {
- LLCacheNameSignal signal;
- signal.connect(callback);
- // Id found in map therefore we can call the callback immediately.
- if (entryp->mIsGroup)
- {
- signal(id, entryp->mGroupName, entryp->mIsGroup);
- }
- else
- {
- std::string fullname = buildFullName(entryp->mFirstName,
- entryp->mLastName);
- signal(id, fullname, entryp->mIsGroup);
- }
- }
- else
- {
- // Id not found in map so we must queue the callback call until
- // available.
- if (!impl.isRequestPending(id))
- {
- if (is_group)
- {
- impl.mAskGroupQueue.emplace(id);
- }
- else
- {
- impl.mAskNameQueue.emplace(id);
- }
- }
- res = impl.addPending(id, callback);
- }
- return res;
- }
- void LLCacheName::processPending()
- {
- constexpr F32 SECS_BETWEEN_PROCESS = 0.1f;
- if (!impl.mProcessTimer.checkExpirationAndReset(SECS_BETWEEN_PROCESS))
- {
- return;
- }
- if (!impl.mUpstreamHost.isOk())
- {
- LL_DEBUGS("NameCache") << "Bad upstream host." << LL_ENDL;
- return;
- }
- impl.processPendingAsks();
- impl.processPendingReplies();
- }
- void LLCacheName::deleteEntriesOlderThan(S32 secs)
- {
- U32 now = (U32)time(NULL);
- U32 expire_time = now - secs;
- for (cache_map_t::iterator iter = impl.mCache.begin(),
- end = impl.mCache.end();
- iter != end; )
- {
- cache_map_t::iterator curiter = iter++;
- LLCacheNameEntry* entryp = curiter->second;
- if (entryp->mCreateTime < expire_time)
- {
- delete entryp;
- impl.mCache.erase(curiter);
- }
- }
- // These are pending requests that we never heard back from.
- U32 pending_expire_time = now - PENDING_TIMEOUT_SECS;
- for (pending_map_t::iterator it = impl.mPendingQueue.begin(),
- end = impl.mPendingQueue.end();
- it != end; )
- {
- pending_map_t::iterator curit = it++;
- if (curit->second < pending_expire_time)
- {
- impl.mPendingQueue.erase(curit);
- }
- }
- }
- void LLCacheName::dump()
- {
- for (cache_map_t::iterator iter = impl.mCache.begin(),
- end = impl.mCache.end();
- iter != end; iter++)
- {
- LLCacheNameEntry* entryp = iter->second;
- if (entryp->mIsGroup)
- {
- llinfos << iter->first << " = (group) " << entryp->mGroupName
- << " @ " << entryp->mCreateTime << llendl;
- }
- else
- {
- llinfos << iter->first << " = "
- << buildFullName(entryp->mFirstName, entryp->mLastName)
- << " @ " << entryp->mCreateTime << llendl;
- }
- }
- }
- void LLCacheName::dumpStats()
- {
- llinfos << "Queue sizes: "
- << " Cache=" << impl.mCache.size()
- << " AskName=" << impl.mAskNameQueue.size()
- << " AskGroup=" << impl.mAskGroupQueue.size()
- << " Pending=" << impl.mPendingQueue.size()
- << " Reply=" << impl.mReplyQueue.size()
- << llendl;
- }
- void LLCacheName::clear()
- {
- for (auto it = impl.mCache.begin(), end = impl.mCache.end();
- it != end; ++it)
- {
- delete it->second;
- }
- impl.mCache.clear();
- }
- const std::string& LLCacheName::getDefaultName() const
- {
- return impl.mLoadingString;
- }
- //static
- std::string LLCacheName::buildFullName(const std::string& first,
- const std::string& last)
- {
- std::string fullname = first;
- if (!last.empty() &&
- !(LLAvatarName::sOmitResidentAsLastName && last == "Resident"))
- {
- fullname += ' ';
- fullname += last;
- }
- return fullname;
- }
- //static
- std::string LLCacheName::cleanFullName(const std::string& full_name)
- {
- return full_name.substr(0, full_name.find(" Resident"));
- }
- //static
- std::string LLCacheName::buildUsername(const std::string& full_name)
- {
- // Rare, but handle hard-coded error names returned from server
- if (full_name == "(\?\?\?) (\?\?\?)")
- {
- return "(\?\?\?)";
- }
- std::string::size_type index = full_name.find(' ');
- if (index != std::string::npos)
- {
- std::string username;
- username = full_name.substr(0, index);
- std::string lastname = full_name.substr(index+1);
- if (lastname != "Resident")
- {
- username = username + "." + lastname;
- }
- LLStringUtil::toLower(username);
- return username;
- }
- // If the input was not a correctly formatted legacy name just return it
- // unchanged
- return full_name;
- }
- //static
- std::string LLCacheName::buildLegacyName(const std::string& complete_name)
- {
- // boost::regex was showing up in the crashreporter, so doing painfully
- // manual parsing using substr. LF
- // *TODO: convert to std::regex ?
- size_t open_paren = complete_name.rfind(" (");
- size_t close_paren = complete_name.rfind(')');
- if (open_paren != std::string::npos &&
- close_paren == complete_name.length() - 1)
- {
- size_t length = close_paren - open_paren - 2;
- std::string legacy_name = complete_name.substr(open_paren + 2, length);
- if (legacy_name.length() > 0)
- {
- std::string cap_letter = legacy_name.substr(0, 1);
- LLStringUtil::toUpper(cap_letter);
- legacy_name = cap_letter + legacy_name.substr(1);
- size_t separator = legacy_name.find('.');
- if (separator != std::string::npos)
- {
- std::string last_name = legacy_name.substr(separator + 1);
- legacy_name = legacy_name.substr(0, separator);
- if (last_name.length() > 0)
- {
- cap_letter = last_name.substr(0, 1);
- LLStringUtil::toUpper(cap_letter);
- legacy_name = legacy_name + " " + cap_letter +
- last_name.substr(1);
- }
- }
- return legacy_name;
- }
- }
- return complete_name;
- }
|