diff options
| author | Andrey Lihatskiy <alihatskiy@productengine.com> | 2024-05-22 20:51:58 +0300 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-05-22 20:51:58 +0300 | 
| commit | 6cc7dd09d5e69cf57e6de7fb568a0ad2693f9c9a (patch) | |
| tree | fab23811a5cedc1ebf01479c852ee92ff62b636c /indra/llmessage/llcachename.cpp | |
| parent | ef8f4819822288e044ea719feb6af7a1f4df4c4e (diff) | |
| parent | 7bb5afc11ee5a6af78302a8d76a9a619e2baaab2 (diff) | |
Merge pull request #1545 from Ansariel/DRTVWR-600-maint-A
Merge main into DRTVWR-600-maint-a
Diffstat (limited to 'indra/llmessage/llcachename.cpp')
| -rw-r--r-- | indra/llmessage/llcachename.cpp | 2042 | 
1 files changed, 1021 insertions, 1021 deletions
diff --git a/indra/llmessage/llcachename.cpp b/indra/llmessage/llcachename.cpp index 5b4f9aded7..cb654f5a30 100644 --- a/indra/llmessage/llcachename.cpp +++ b/indra/llmessage/llcachename.cpp @@ -1,1021 +1,1021 @@ -/**  - * @file llcachename.cpp - * @brief A hierarchical cache of first and last names queried based on UUID. - * - * $LicenseInfo:firstyear=2002&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$ - */ - -#include "linden_common.h" - -#include "llcachename.h" - -// linden library includes -#include "lldbstrings.h" -#include "llframetimer.h" -#include "llhost.h" -#include "llrand.h" -#include "llsdserialize.h" -#include "lluuid.h" -#include "message.h" - -#include <boost/regex.hpp> - -// 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. -// We won't re-request a name during this time -const U32 PENDING_TIMEOUT_SECS = 5 * 60; - -// Globals -LLCacheName* gCacheName = NULL; -std::map<std::string, std::string> LLCacheName::sCacheName; - -/// --------------------------------------------------------------------------- -/// class LLCacheNameEntry -/// --------------------------------------------------------------------------- - -class LLCacheNameEntry -{ -public: -	LLCacheNameEntry(); - -public: -	bool mIsGroup; -	U32 mCreateTime;	// unix time_t -	// IDEVO 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: -	LLUUID				mID; -	LLCacheNameSignal	mSignal; -	LLHost				mHost; - -	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(); } -}; - -class ReplySender -{ -public: -	ReplySender(LLMessageSystem* msg); -	~ReplySender(); - -	void send(const LLUUID& id, -		const LLCacheNameEntry& entry, const LLHost& host); - -private: -	void flush(); - -	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) -	{ -		if (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; -	} -} - - -typedef std::set<LLUUID>					AskQueue; -typedef std::list<PendingReply*>			ReplyQueue; -typedef std::map<LLUUID,U32>				PendingQueue; -typedef std::map<LLUUID, LLCacheNameEntry*> Cache; -typedef std::map<std::string, LLUUID> 		ReverseCache; - -class LLCacheName::Impl -{ -public: -	LLMessageSystem*	mMsg; -	LLHost				mUpstreamHost; - -	Cache				mCache; -		// the map of UUIDs to names -	ReverseCache   	  	mReverseCache; -		// map of names to UUIDs -	 -	AskQueue			mAskNameQueue; -	AskQueue			mAskGroupQueue; -		// UUIDs to ask our upstream host about -	 -	PendingQueue		mPendingQueue; -		// UUIDs that have been requested but are not in cache yet. - -	ReplyQueue			mReplyQueue; -		// requests awaiting replies from us - -	LLCacheNameSignal	mSignal; - -	LLFrameTimer		mProcessTimer; - -	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 AskQueue& queue); -	bool isRequestPending(const LLUUID& id); - -	// Message system callbacks. -	void processUUIDRequest(LLMessageSystem* msg, bool isGroup); -	void processUUIDReply(LLMessageSystem* msg, bool isGroup); - -	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); -}; - - -/// -------------------------------------------------------------------------- -/// class LLCacheName -/// --------------------------------------------------------------------------- - -LLCacheName::LLCacheName(LLMessageSystem* msg) -	: impl(* new Impl(msg)) -	{ } - -LLCacheName::LLCacheName(LLMessageSystem* msg, const LLHost& upstream_host) -	: impl(* new Impl(msg)) -{ -	sCacheName["waiting"] = "(Loading...)"; -	sCacheName["nobody"] = "(nobody)"; -	sCacheName["none"] = "(none)"; -	setUpstream(upstream_host); -} - -LLCacheName::~LLCacheName() -{ -	delete &impl; -} - -LLCacheName::Impl::Impl(LLMessageSystem* msg) -	: mMsg(msg), mUpstreamHost(LLHost()) -{ -	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_each(mCache.begin(), mCache.end(), DeletePairedPointer()); -	mCache.clear(); -	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); -} - -void LLCacheName::setUpstream(const LLHost& upstream_host) -{ -	impl.mUpstreamHost = upstream_host; -} - -boost::signals2::connection LLCacheName::addObserver(const LLCacheNameCallback& callback) -{ -	return impl.mSignal.connect(callback); -} - -bool LLCacheName::importFile(std::istream& istr) -{ -	LLSD data; -	if(LLSDParser::PARSE_FAILURE == LLSDSerialize::fromXMLDocument(data, istr)) -	{ -		return false; -	} - -	// We'll expire entries more than a week old -	U32 now = (U32)time(NULL); -	const 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); -		impl.mReverseCache[fullname] = id; - -		++count; -	} -	LL_INFOS() << "LLCacheName loaded " << count << " agent names" << LL_ENDL; - -	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; -	} -	LL_INFOS() << "LLCacheName loaded " << count << " group names" << LL_ENDL; -	return true; -} - -void LLCacheName::exportFile(std::ostream& ostr) -{ -	LLSD data; -	Cache::iterator iter = impl.mCache.begin(); -	Cache::iterator end = impl.mCache.end(); -	for( ; iter != end; ++iter) -	{ -		// Only write entries for which we have valid data. -		LLCacheNameEntry* entry = iter->second; -		if(!entry -		   || (std::string::npos != entry->mFirstName.find('?')) -		   || (std::string::npos != entry->mGroupName.find('?'))) -		{ -			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::Impl::getName(const LLUUID& id, std::string& first, std::string& last) -{ -	if(id.isNull()) -	{ -		first = sCacheName["nobody"]; -		last.clear(); -		return true; -	} - -	LLCacheNameEntry* entry = get_ptr_in_map(mCache, id ); -	if (entry) -	{ -		first = entry->mFirstName; -		last =  entry->mLastName; -		return true; -	} -	else -	{ -		first = sCacheName["waiting"]; -		last.clear(); -		if (!isRequestPending(id)) -		{ -			mAskNameQueue.insert(id); -		}	 -		return false; -	} - -} - -// static -void LLCacheName::localizeCacheName(std::string key, std::string value) -{ -	if (key!="" && value!= "" ) -		sCacheName[key]=value; -	else -		LL_WARNS()<< " Error localizing cache key " << key << " To "<< value<<LL_ENDL; -} - -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 = sCacheName["none"]; -		return true; -	} - -	LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache,id); -	if (entry && entry->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() << "LLCacheName queuing HACK group request: " << id << LL_ENDL; -		entry = NULL; -	} - -	if (entry) -	{ -		group = entry->mGroupName; -		return true; -	} -	else  -	{ -		group = sCacheName["waiting"]; -		if (!impl.isRequestPending(id)) -		{ -			impl.mAskGroupQueue.insert(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) -{ -	ReverseCache::iterator iter = impl.mReverseCache.find(full_name); -	if (iter != impl.mReverseCache.end()) -	{ -		id = iter->second; -		return true; -	} -	else -	{ -		return false; -	} -} - -//static -std::string LLCacheName::buildFullName(const std::string& first, const std::string& last) -{ -	std::string fullname = first; -	if (!last.empty() -		&& 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  -// Transform hard-coded name provided by server to a more legible username -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 wasn't a correctly formatted legacy name, just return it   -	// cleaned up from a potential terminal "Resident" -    std::string clean_name = cleanFullName(full_name); -    LLStringUtil::toLower(clean_name); -	return clean_name; -} - -//static  -std::string LLCacheName::buildLegacyName(const std::string& complete_name) -{ -	//boost::regexp was showing up in the crashreporter, so doing   -	//painfully manual parsing using substr. LF -	S32 open_paren = complete_name.rfind(" ("); -	S32 close_paren = complete_name.rfind(')'); - -	if (open_paren != std::string::npos && -		close_paren == complete_name.length()-1) -	{ -		S32 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); -	 -			S32 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; -} - -// 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 isn't a boost::function is so that trackable behavior -//  doesn't 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, sCacheName["nobody"], is_group); -		return res; -	} - -	LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache, id ); -	if (entry) -	{ -		LLCacheNameSignal signal; -		signal.connect(callback); -		// id found in map therefore we can call the callback immediately. -		if (entry->mIsGroup) -		{ -			signal(id, entry->mGroupName, entry->mIsGroup); -		} -		else -		{ -			std::string fullname = -				buildFullName(entry->mFirstName, entry->mLastName); -			signal(id, fullname, entry->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.insert(id); -			} -			else -			{ -				impl.mAskNameQueue.insert(id); -			} -		} -		res = impl.addPending(id, callback); -	} -	return res; -} - -boost::signals2::connection LLCacheName::getGroup(const LLUUID& group_id, -												  const LLCacheNameCallback& callback) -{ -	return get(group_id, true, callback); -} - -boost::signals2::connection LLCacheName::get(const LLUUID& id, bool is_group, old_callback_t callback, void* user_data) -{ -	return get(id, is_group, boost::bind(callback, _1, _2, _3, user_data)); -} - -void LLCacheName::processPending() -{ -	const F32 SECS_BETWEEN_PROCESS = 0.1f; -	if(!impl.mProcessTimer.checkExpirationAndReset(SECS_BETWEEN_PROCESS)) -	{ -		return; -	} - -	if(!impl.mUpstreamHost.isOk()) -	{ -		LL_DEBUGS() << "LLCacheName::processPending() - 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::iterator iter = impl.mCache.begin(); iter != impl.mCache.end(); ) -	{ -		Cache::iterator curiter = iter++; -		LLCacheNameEntry* entry = curiter->second; -		if (entry->mCreateTime < expire_time) -		{ -			delete entry; -			impl.mCache.erase(curiter); -		} -	} - -	// These are pending requests that we never heard back from. -	U32 pending_expire_time = now - PENDING_TIMEOUT_SECS; -	for(PendingQueue::iterator p_iter = impl.mPendingQueue.begin(); -		p_iter != impl.mPendingQueue.end(); ) -	{ -		PendingQueue::iterator p_curitor = p_iter++; -  -		if (p_curitor->second < pending_expire_time) -		{ -			impl.mPendingQueue.erase(p_curitor); -		} -	} -} - - -void LLCacheName::dump() -{ -	for (Cache::iterator iter = impl.mCache.begin(), -			 end = impl.mCache.end(); -		 iter != end; iter++) -	{ -		LLCacheNameEntry* entry = iter->second; -		if (entry->mIsGroup) -		{ -			LL_INFOS() -				<< iter->first << " = (group) " -				<< entry->mGroupName -				<< " @ " << entry->mCreateTime -				<< LL_ENDL; -		} -		else -		{ -			LL_INFOS() -				<< iter->first << " = " -				<< buildFullName(entry->mFirstName, entry->mLastName) -				<< " @ " << entry->mCreateTime -				<< LL_ENDL; -		} -	} -} - -void LLCacheName::dumpStats() -{ -	LL_INFOS() << "Queue sizes: " -			<< " Cache=" << impl.mCache.size() -			<< " AskName=" << impl.mAskNameQueue.size() -			<< " AskGroup=" << impl.mAskGroupQueue.size() -			<< " Pending=" << impl.mPendingQueue.size() -			<< " Reply=" << impl.mReplyQueue.size() -// 			<< " Observers=" << impl.mSignal.size() -			<< LL_ENDL; -} - -void LLCacheName::clear() -{ -	for_each(impl.mCache.begin(), impl.mCache.end(), DeletePairedPointer()); -	impl.mCache.clear(); -} - -//static  -std::string LLCacheName::getDefaultName() -{ -	return sCacheName["waiting"]; -} - -//static  -std::string LLCacheName::getDefaultLastName() -{ -	return "Resident"; -} - -void LLCacheName::Impl::processPendingAsks() -{ -	sendRequest(_PREHASH_UUIDNameRequest, mAskNameQueue); -	sendRequest(_PREHASH_UUIDGroupNameRequest, mAskGroupQueue); -	mAskNameQueue.clear(); -	mAskGroupQueue.clear(); -} - -void LLCacheName::Impl::processPendingReplies() -{ -	// First call all the callbacks, because they might send messages. -	for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); ++it) -	{ -		PendingReply* reply = *it; -		LLCacheNameEntry* entry = get_ptr_in_map(mCache, reply->mID); -		if(!entry) continue; - -		if (!entry->mIsGroup) -		{ -			std::string fullname = -				LLCacheName::buildFullName(entry->mFirstName, entry->mLastName); -			(reply->mSignal)(reply->mID, fullname, false); -		} -		else -		{ -			(reply->mSignal)(reply->mID, entry->mGroupName, true); -		} -	} - -	// Forward on all replies, if needed. -	ReplySender sender(mMsg); -	for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); ++it) -	{ -		PendingReply* reply = *it; -		LLCacheNameEntry* entry = get_ptr_in_map(mCache, reply->mID); -		if(!entry) continue; - -		if (reply->mHost.isOk()) -		{ -			sender.send(reply->mID, *entry, reply->mHost); -		} - -		reply->done(); -	} -	 -	for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); ) -	{ -		ReplyQueue::iterator curit = it++; -		PendingReply* reply = *curit; -		if (reply->isDone()) -		{ -			delete reply; -			mReplyQueue.erase(curit); -		} -	} -} - - -void LLCacheName::Impl::sendRequest( -	const char* msg_name, -	const AskQueue& queue) -{ -	if(queue.empty()) -	{ -		return;		 -	} - -	bool start_new_message = true; -	AskQueue::const_iterator it = queue.begin(); -	AskQueue::const_iterator end = queue.end(); -	for(; 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; - -	PendingQueue::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 isGroup) -{ -	// You should only get this message if the cache is at the simulator -	// level, hence having an upstream provider. -	if (!mUpstreamHost.isOk()) -	{ -		LL_WARNS() << "LLCacheName - got UUID name/group request, but no upstream provider!" << LL_ENDL; -		return; -	} - -	LLHost fromHost = 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* entry = get_ptr_in_map(mCache, id); -		if(entry) -		{ -			if (isGroup != entry->mIsGroup) -			{ -				LL_WARNS() << "LLCacheName - Asked for " -						<< (isGroup ? "group" : "user") << " name, " -						<< "but found " -						<< (entry->mIsGroup ? "group" : "user") -						<< ": " << id << LL_ENDL; -			} -			else -			{ -				// ...it's in the cache, so send it as the reply -				sender.send(id, *entry, fromHost); -			} -		} -		else -		{ -			if (!isRequestPending(id)) -			{ -				if (isGroup) -				{ -					mAskGroupQueue.insert(id); -				} -				else -				{ -					mAskNameQueue.insert(id); -				} -			} -			 -			addPending(id, fromHost); -		} -	} -} - - - -void LLCacheName::Impl::processUUIDReply(LLMessageSystem* msg, bool isGroup) -{ -	S32 count = msg->getNumberOfBlocksFast(_PREHASH_UUIDNameBlock); -	for(S32 i = 0; i < count; ++i) -	{ -		LLUUID id; -		msg->getUUIDFast(_PREHASH_UUIDNameBlock, _PREHASH_ID, id, i); -		LLCacheNameEntry* entry = get_ptr_in_map(mCache, id); -		if (!entry) -		{ -			entry = new LLCacheNameEntry; -			mCache[id] = entry; -		} - -		mPendingQueue.erase(id); - -		entry->mIsGroup = isGroup; -		entry->mCreateTime = (U32)time(NULL); -		if (!isGroup) -		{ -			msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_FirstName, entry->mFirstName, i); -			msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_LastName,  entry->mLastName, i); -		} -		else -		{	// is group -			msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_GroupName, entry->mGroupName, i); -			LLStringFn::replace_ascii_controlchars(entry->mGroupName, LL_UNKNOWN_CHAR); -		} - -		if (!isGroup) -		{ -			// 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 (entry->mLastName.empty()) -			{ -				full_name = cleanFullName(entry->mFirstName); - -				//fix what we are putting in the cache -				entry->mFirstName = full_name; -				entry->mLastName = "Resident"; -			} -			else -			{ -				full_name = LLCacheName::buildFullName(entry->mFirstName, entry->mLastName); -			} -			mSignal(id, full_name, false); -			mReverseCache[full_name] = id; -		} -		else -		{ -			mSignal(id, entry->mGroupName, true); -			mReverseCache[entry->mGroupName] = id; -		} -	} -} - - - -// static call back functions - -void LLCacheName::Impl::handleUUIDNameReply(LLMessageSystem* msg, void** userData) -{ -	((LLCacheName::Impl*)userData)->processUUIDReply(msg, false); -} - -void LLCacheName::Impl::handleUUIDNameRequest(LLMessageSystem* msg, void** userData) -{ -	((LLCacheName::Impl*)userData)->processUUIDRequest(msg, false); -} - -void LLCacheName::Impl::handleUUIDGroupNameRequest(LLMessageSystem* msg, void** userData) -{ -	((LLCacheName::Impl*)userData)->processUUIDRequest(msg, true); -} - -void LLCacheName::Impl::handleUUIDGroupNameReply(LLMessageSystem* msg, void** userData) -{ -	((LLCacheName::Impl*)userData)->processUUIDReply(msg, true); -} +/**
 + * @file llcachename.cpp
 + * @brief A hierarchical cache of first and last names queried based on UUID.
 + *
 + * $LicenseInfo:firstyear=2002&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$
 + */
 +
 +#include "linden_common.h"
 +
 +#include "llcachename.h"
 +
 +// linden library includes
 +#include "lldbstrings.h"
 +#include "llframetimer.h"
 +#include "llhost.h"
 +#include "llrand.h"
 +#include "llsdserialize.h"
 +#include "lluuid.h"
 +#include "message.h"
 +
 +#include <boost/regex.hpp>
 +
 +// 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.
 +// We won't re-request a name during this time
 +const U32 PENDING_TIMEOUT_SECS = 5 * 60;
 +
 +// Globals
 +LLCacheName* gCacheName = NULL;
 +std::map<std::string, std::string> LLCacheName::sCacheName;
 +
 +/// ---------------------------------------------------------------------------
 +/// class LLCacheNameEntry
 +/// ---------------------------------------------------------------------------
 +
 +class LLCacheNameEntry
 +{
 +public:
 +    LLCacheNameEntry();
 +
 +public:
 +    bool mIsGroup;
 +    U32 mCreateTime;    // unix time_t
 +    // IDEVO 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:
 +    LLUUID              mID;
 +    LLCacheNameSignal   mSignal;
 +    LLHost              mHost;
 +
 +    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(); }
 +};
 +
 +class ReplySender
 +{
 +public:
 +    ReplySender(LLMessageSystem* msg);
 +    ~ReplySender();
 +
 +    void send(const LLUUID& id,
 +        const LLCacheNameEntry& entry, const LLHost& host);
 +
 +private:
 +    void flush();
 +
 +    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)
 +    {
 +        if (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;
 +    }
 +}
 +
 +
 +typedef std::set<LLUUID>                    AskQueue;
 +typedef std::list<PendingReply*>            ReplyQueue;
 +typedef std::map<LLUUID,U32>                PendingQueue;
 +typedef std::map<LLUUID, LLCacheNameEntry*> Cache;
 +typedef std::map<std::string, LLUUID>       ReverseCache;
 +
 +class LLCacheName::Impl
 +{
 +public:
 +    LLMessageSystem*    mMsg;
 +    LLHost              mUpstreamHost;
 +
 +    Cache               mCache;
 +        // the map of UUIDs to names
 +    ReverseCache        mReverseCache;
 +        // map of names to UUIDs
 +
 +    AskQueue            mAskNameQueue;
 +    AskQueue            mAskGroupQueue;
 +        // UUIDs to ask our upstream host about
 +
 +    PendingQueue        mPendingQueue;
 +        // UUIDs that have been requested but are not in cache yet.
 +
 +    ReplyQueue          mReplyQueue;
 +        // requests awaiting replies from us
 +
 +    LLCacheNameSignal   mSignal;
 +
 +    LLFrameTimer        mProcessTimer;
 +
 +    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 AskQueue& queue);
 +    bool isRequestPending(const LLUUID& id);
 +
 +    // Message system callbacks.
 +    void processUUIDRequest(LLMessageSystem* msg, bool isGroup);
 +    void processUUIDReply(LLMessageSystem* msg, bool isGroup);
 +
 +    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);
 +};
 +
 +
 +/// --------------------------------------------------------------------------
 +/// class LLCacheName
 +/// ---------------------------------------------------------------------------
 +
 +LLCacheName::LLCacheName(LLMessageSystem* msg)
 +    : impl(* new Impl(msg))
 +    { }
 +
 +LLCacheName::LLCacheName(LLMessageSystem* msg, const LLHost& upstream_host)
 +    : impl(* new Impl(msg))
 +{
 +    sCacheName["waiting"] = "(Loading...)";
 +    sCacheName["nobody"] = "(nobody)";
 +    sCacheName["none"] = "(none)";
 +    setUpstream(upstream_host);
 +}
 +
 +LLCacheName::~LLCacheName()
 +{
 +    delete &impl;
 +}
 +
 +LLCacheName::Impl::Impl(LLMessageSystem* msg)
 +    : mMsg(msg), mUpstreamHost(LLHost())
 +{
 +    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_each(mCache.begin(), mCache.end(), DeletePairedPointer());
 +    mCache.clear();
 +    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);
 +}
 +
 +void LLCacheName::setUpstream(const LLHost& upstream_host)
 +{
 +    impl.mUpstreamHost = upstream_host;
 +}
 +
 +boost::signals2::connection LLCacheName::addObserver(const LLCacheNameCallback& callback)
 +{
 +    return impl.mSignal.connect(callback);
 +}
 +
 +bool LLCacheName::importFile(std::istream& istr)
 +{
 +    LLSD data;
 +    if(LLSDParser::PARSE_FAILURE == LLSDSerialize::fromXMLDocument(data, istr))
 +    {
 +        return false;
 +    }
 +
 +    // We'll expire entries more than a week old
 +    U32 now = (U32)time(NULL);
 +    const 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);
 +        impl.mReverseCache[fullname] = id;
 +
 +        ++count;
 +    }
 +    LL_INFOS() << "LLCacheName loaded " << count << " agent names" << LL_ENDL;
 +
 +    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;
 +    }
 +    LL_INFOS() << "LLCacheName loaded " << count << " group names" << LL_ENDL;
 +    return true;
 +}
 +
 +void LLCacheName::exportFile(std::ostream& ostr)
 +{
 +    LLSD data;
 +    Cache::iterator iter = impl.mCache.begin();
 +    Cache::iterator end = impl.mCache.end();
 +    for( ; iter != end; ++iter)
 +    {
 +        // Only write entries for which we have valid data.
 +        LLCacheNameEntry* entry = iter->second;
 +        if(!entry
 +           || (std::string::npos != entry->mFirstName.find('?'))
 +           || (std::string::npos != entry->mGroupName.find('?')))
 +        {
 +            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::Impl::getName(const LLUUID& id, std::string& first, std::string& last)
 +{
 +    if(id.isNull())
 +    {
 +        first = sCacheName["nobody"];
 +        last.clear();
 +        return true;
 +    }
 +
 +    LLCacheNameEntry* entry = get_ptr_in_map(mCache, id );
 +    if (entry)
 +    {
 +        first = entry->mFirstName;
 +        last =  entry->mLastName;
 +        return true;
 +    }
 +    else
 +    {
 +        first = sCacheName["waiting"];
 +        last.clear();
 +        if (!isRequestPending(id))
 +        {
 +            mAskNameQueue.insert(id);
 +        }
 +        return false;
 +    }
 +
 +}
 +
 +// static
 +void LLCacheName::localizeCacheName(std::string key, std::string value)
 +{
 +    if (key!="" && value!= "" )
 +        sCacheName[key]=value;
 +    else
 +        LL_WARNS()<< " Error localizing cache key " << key << " To "<< value<<LL_ENDL;
 +}
 +
 +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 = sCacheName["none"];
 +        return true;
 +    }
 +
 +    LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache,id);
 +    if (entry && entry->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() << "LLCacheName queuing HACK group request: " << id << LL_ENDL;
 +        entry = NULL;
 +    }
 +
 +    if (entry)
 +    {
 +        group = entry->mGroupName;
 +        return true;
 +    }
 +    else
 +    {
 +        group = sCacheName["waiting"];
 +        if (!impl.isRequestPending(id))
 +        {
 +            impl.mAskGroupQueue.insert(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)
 +{
 +    ReverseCache::iterator iter = impl.mReverseCache.find(full_name);
 +    if (iter != impl.mReverseCache.end())
 +    {
 +        id = iter->second;
 +        return true;
 +    }
 +    else
 +    {
 +        return false;
 +    }
 +}
 +
 +//static
 +std::string LLCacheName::buildFullName(const std::string& first, const std::string& last)
 +{
 +    std::string fullname = first;
 +    if (!last.empty()
 +        && 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
 +// Transform hard-coded name provided by server to a more legible username
 +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 wasn't a correctly formatted legacy name, just return it
 +    // cleaned up from a potential terminal "Resident"
 +    std::string clean_name = cleanFullName(full_name);
 +    LLStringUtil::toLower(clean_name);
 +    return clean_name;
 +}
 +
 +//static
 +std::string LLCacheName::buildLegacyName(const std::string& complete_name)
 +{
 +    //boost::regexp was showing up in the crashreporter, so doing
 +    //painfully manual parsing using substr. LF
 +    S32 open_paren = complete_name.rfind(" (");
 +    S32 close_paren = complete_name.rfind(')');
 +
 +    if (open_paren != std::string::npos &&
 +        close_paren == complete_name.length()-1)
 +    {
 +        S32 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);
 +
 +            S32 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;
 +}
 +
 +// 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 isn't a boost::function is so that trackable behavior
 +//  doesn't 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, sCacheName["nobody"], is_group);
 +        return res;
 +    }
 +
 +    LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache, id );
 +    if (entry)
 +    {
 +        LLCacheNameSignal signal;
 +        signal.connect(callback);
 +        // id found in map therefore we can call the callback immediately.
 +        if (entry->mIsGroup)
 +        {
 +            signal(id, entry->mGroupName, entry->mIsGroup);
 +        }
 +        else
 +        {
 +            std::string fullname =
 +                buildFullName(entry->mFirstName, entry->mLastName);
 +            signal(id, fullname, entry->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.insert(id);
 +            }
 +            else
 +            {
 +                impl.mAskNameQueue.insert(id);
 +            }
 +        }
 +        res = impl.addPending(id, callback);
 +    }
 +    return res;
 +}
 +
 +boost::signals2::connection LLCacheName::getGroup(const LLUUID& group_id,
 +                                                  const LLCacheNameCallback& callback)
 +{
 +    return get(group_id, true, callback);
 +}
 +
 +boost::signals2::connection LLCacheName::get(const LLUUID& id, bool is_group, old_callback_t callback, void* user_data)
 +{
 +    return get(id, is_group, boost::bind(callback, _1, _2, _3, user_data));
 +}
 +
 +void LLCacheName::processPending()
 +{
 +    const F32 SECS_BETWEEN_PROCESS = 0.1f;
 +    if(!impl.mProcessTimer.checkExpirationAndReset(SECS_BETWEEN_PROCESS))
 +    {
 +        return;
 +    }
 +
 +    if(!impl.mUpstreamHost.isOk())
 +    {
 +        LL_DEBUGS() << "LLCacheName::processPending() - 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::iterator iter = impl.mCache.begin(); iter != impl.mCache.end(); )
 +    {
 +        Cache::iterator curiter = iter++;
 +        LLCacheNameEntry* entry = curiter->second;
 +        if (entry->mCreateTime < expire_time)
 +        {
 +            delete entry;
 +            impl.mCache.erase(curiter);
 +        }
 +    }
 +
 +    // These are pending requests that we never heard back from.
 +    U32 pending_expire_time = now - PENDING_TIMEOUT_SECS;
 +    for(PendingQueue::iterator p_iter = impl.mPendingQueue.begin();
 +        p_iter != impl.mPendingQueue.end(); )
 +    {
 +        PendingQueue::iterator p_curitor = p_iter++;
 +
 +        if (p_curitor->second < pending_expire_time)
 +        {
 +            impl.mPendingQueue.erase(p_curitor);
 +        }
 +    }
 +}
 +
 +
 +void LLCacheName::dump()
 +{
 +    for (Cache::iterator iter = impl.mCache.begin(),
 +             end = impl.mCache.end();
 +         iter != end; iter++)
 +    {
 +        LLCacheNameEntry* entry = iter->second;
 +        if (entry->mIsGroup)
 +        {
 +            LL_INFOS()
 +                << iter->first << " = (group) "
 +                << entry->mGroupName
 +                << " @ " << entry->mCreateTime
 +                << LL_ENDL;
 +        }
 +        else
 +        {
 +            LL_INFOS()
 +                << iter->first << " = "
 +                << buildFullName(entry->mFirstName, entry->mLastName)
 +                << " @ " << entry->mCreateTime
 +                << LL_ENDL;
 +        }
 +    }
 +}
 +
 +void LLCacheName::dumpStats()
 +{
 +    LL_INFOS() << "Queue sizes: "
 +            << " Cache=" << impl.mCache.size()
 +            << " AskName=" << impl.mAskNameQueue.size()
 +            << " AskGroup=" << impl.mAskGroupQueue.size()
 +            << " Pending=" << impl.mPendingQueue.size()
 +            << " Reply=" << impl.mReplyQueue.size()
 +//          << " Observers=" << impl.mSignal.size()
 +            << LL_ENDL;
 +}
 +
 +void LLCacheName::clear()
 +{
 +    for_each(impl.mCache.begin(), impl.mCache.end(), DeletePairedPointer());
 +    impl.mCache.clear();
 +}
 +
 +//static
 +std::string LLCacheName::getDefaultName()
 +{
 +    return sCacheName["waiting"];
 +}
 +
 +//static
 +std::string LLCacheName::getDefaultLastName()
 +{
 +    return "Resident";
 +}
 +
 +void LLCacheName::Impl::processPendingAsks()
 +{
 +    sendRequest(_PREHASH_UUIDNameRequest, mAskNameQueue);
 +    sendRequest(_PREHASH_UUIDGroupNameRequest, mAskGroupQueue);
 +    mAskNameQueue.clear();
 +    mAskGroupQueue.clear();
 +}
 +
 +void LLCacheName::Impl::processPendingReplies()
 +{
 +    // First call all the callbacks, because they might send messages.
 +    for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); ++it)
 +    {
 +        PendingReply* reply = *it;
 +        LLCacheNameEntry* entry = get_ptr_in_map(mCache, reply->mID);
 +        if(!entry) continue;
 +
 +        if (!entry->mIsGroup)
 +        {
 +            std::string fullname =
 +                LLCacheName::buildFullName(entry->mFirstName, entry->mLastName);
 +            (reply->mSignal)(reply->mID, fullname, false);
 +        }
 +        else
 +        {
 +            (reply->mSignal)(reply->mID, entry->mGroupName, true);
 +        }
 +    }
 +
 +    // Forward on all replies, if needed.
 +    ReplySender sender(mMsg);
 +    for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); ++it)
 +    {
 +        PendingReply* reply = *it;
 +        LLCacheNameEntry* entry = get_ptr_in_map(mCache, reply->mID);
 +        if(!entry) continue;
 +
 +        if (reply->mHost.isOk())
 +        {
 +            sender.send(reply->mID, *entry, reply->mHost);
 +        }
 +
 +        reply->done();
 +    }
 +
 +    for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); )
 +    {
 +        ReplyQueue::iterator curit = it++;
 +        PendingReply* reply = *curit;
 +        if (reply->isDone())
 +        {
 +            delete reply;
 +            mReplyQueue.erase(curit);
 +        }
 +    }
 +}
 +
 +
 +void LLCacheName::Impl::sendRequest(
 +    const char* msg_name,
 +    const AskQueue& queue)
 +{
 +    if(queue.empty())
 +    {
 +        return;
 +    }
 +
 +    bool start_new_message = true;
 +    AskQueue::const_iterator it = queue.begin();
 +    AskQueue::const_iterator end = queue.end();
 +    for(; 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;
 +
 +    PendingQueue::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 isGroup)
 +{
 +    // You should only get this message if the cache is at the simulator
 +    // level, hence having an upstream provider.
 +    if (!mUpstreamHost.isOk())
 +    {
 +        LL_WARNS() << "LLCacheName - got UUID name/group request, but no upstream provider!" << LL_ENDL;
 +        return;
 +    }
 +
 +    LLHost fromHost = 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* entry = get_ptr_in_map(mCache, id);
 +        if(entry)
 +        {
 +            if (isGroup != entry->mIsGroup)
 +            {
 +                LL_WARNS() << "LLCacheName - Asked for "
 +                        << (isGroup ? "group" : "user") << " name, "
 +                        << "but found "
 +                        << (entry->mIsGroup ? "group" : "user")
 +                        << ": " << id << LL_ENDL;
 +            }
 +            else
 +            {
 +                // ...it's in the cache, so send it as the reply
 +                sender.send(id, *entry, fromHost);
 +            }
 +        }
 +        else
 +        {
 +            if (!isRequestPending(id))
 +            {
 +                if (isGroup)
 +                {
 +                    mAskGroupQueue.insert(id);
 +                }
 +                else
 +                {
 +                    mAskNameQueue.insert(id);
 +                }
 +            }
 +
 +            addPending(id, fromHost);
 +        }
 +    }
 +}
 +
 +
 +
 +void LLCacheName::Impl::processUUIDReply(LLMessageSystem* msg, bool isGroup)
 +{
 +    S32 count = msg->getNumberOfBlocksFast(_PREHASH_UUIDNameBlock);
 +    for(S32 i = 0; i < count; ++i)
 +    {
 +        LLUUID id;
 +        msg->getUUIDFast(_PREHASH_UUIDNameBlock, _PREHASH_ID, id, i);
 +        LLCacheNameEntry* entry = get_ptr_in_map(mCache, id);
 +        if (!entry)
 +        {
 +            entry = new LLCacheNameEntry;
 +            mCache[id] = entry;
 +        }
 +
 +        mPendingQueue.erase(id);
 +
 +        entry->mIsGroup = isGroup;
 +        entry->mCreateTime = (U32)time(NULL);
 +        if (!isGroup)
 +        {
 +            msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_FirstName, entry->mFirstName, i);
 +            msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_LastName,  entry->mLastName, i);
 +        }
 +        else
 +        {   // is group
 +            msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_GroupName, entry->mGroupName, i);
 +            LLStringFn::replace_ascii_controlchars(entry->mGroupName, LL_UNKNOWN_CHAR);
 +        }
 +
 +        if (!isGroup)
 +        {
 +            // 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 (entry->mLastName.empty())
 +            {
 +                full_name = cleanFullName(entry->mFirstName);
 +
 +                //fix what we are putting in the cache
 +                entry->mFirstName = full_name;
 +                entry->mLastName = "Resident";
 +            }
 +            else
 +            {
 +                full_name = LLCacheName::buildFullName(entry->mFirstName, entry->mLastName);
 +            }
 +            mSignal(id, full_name, false);
 +            mReverseCache[full_name] = id;
 +        }
 +        else
 +        {
 +            mSignal(id, entry->mGroupName, true);
 +            mReverseCache[entry->mGroupName] = id;
 +        }
 +    }
 +}
 +
 +
 +
 +// static call back functions
 +
 +void LLCacheName::Impl::handleUUIDNameReply(LLMessageSystem* msg, void** userData)
 +{
 +    ((LLCacheName::Impl*)userData)->processUUIDReply(msg, false);
 +}
 +
 +void LLCacheName::Impl::handleUUIDNameRequest(LLMessageSystem* msg, void** userData)
 +{
 +    ((LLCacheName::Impl*)userData)->processUUIDRequest(msg, false);
 +}
 +
 +void LLCacheName::Impl::handleUUIDGroupNameRequest(LLMessageSystem* msg, void** userData)
 +{
 +    ((LLCacheName::Impl*)userData)->processUUIDRequest(msg, true);
 +}
 +
 +void LLCacheName::Impl::handleUUIDGroupNameReply(LLMessageSystem* msg, void** userData)
 +{
 +    ((LLCacheName::Impl*)userData)->processUUIDReply(msg, true);
 +}
  | 
