PageRenderTime 102ms CodeModel.GetById 19ms app.highlight 75ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/llmessage/llcachename.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1020 lines | 787 code | 145 blank | 88 comment | 107 complexity | f91a83bc69d6cbcdc78cddad70ab5864 MD5 | raw file
   1/** 
   2 * @file llcachename.cpp
   3 * @brief A hierarchical cache of first and last names queried based on UUID.
   4 *
   5 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
   6 * Second Life Viewer Source Code
   7 * Copyright (C) 2010, Linden Research, Inc.
   8 * 
   9 * This library is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU Lesser General Public
  11 * License as published by the Free Software Foundation;
  12 * version 2.1 of the License only.
  13 * 
  14 * This library is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  17 * Lesser General Public License for more details.
  18 * 
  19 * You should have received a copy of the GNU Lesser General Public
  20 * License along with this library; if not, write to the Free Software
  21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  22 * 
  23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  24 * $/LicenseInfo$
  25 */
  26
  27#include "linden_common.h"
  28
  29#include "llcachename.h"
  30
  31// linden library includes
  32#include "lldbstrings.h"
  33#include "llframetimer.h"
  34#include "llhost.h"
  35#include "llrand.h"
  36#include "llsdserialize.h"
  37#include "lluuid.h"
  38#include "message.h"
  39#include "llmemtype.h"
  40
  41#include <boost/regex.hpp>
  42
  43// llsd serialization constants
  44static const std::string AGENTS("agents");
  45static const std::string GROUPS("groups");
  46static const std::string CTIME("ctime");
  47static const std::string FIRST("first");
  48static const std::string LAST("last");
  49static const std::string NAME("name");
  50
  51// We track name requests in flight for up to this long.
  52// We won't re-request a name during this time
  53const U32 PENDING_TIMEOUT_SECS = 5 * 60;
  54
  55// File version number
  56const S32 CN_FILE_VERSION = 2;
  57
  58// Globals
  59LLCacheName* gCacheName = NULL;
  60std::map<std::string, std::string> LLCacheName::sCacheName;
  61
  62/// ---------------------------------------------------------------------------
  63/// class LLCacheNameEntry
  64/// ---------------------------------------------------------------------------
  65
  66class LLCacheNameEntry
  67{
  68public:
  69	LLCacheNameEntry();
  70
  71public:
  72	bool mIsGroup;
  73	U32 mCreateTime;	// unix time_t
  74	// IDEVO TODO collapse names to one field, which will eliminate
  75	// many string compares on "Resident"
  76	std::string mFirstName;
  77	std::string mLastName;
  78	std::string mGroupName;
  79};
  80
  81LLCacheNameEntry::LLCacheNameEntry()
  82	: mIsGroup(false),
  83	  mCreateTime(0)
  84{
  85}
  86
  87
  88class PendingReply
  89{
  90public:
  91	LLUUID				mID;
  92	LLCacheNameSignal	mSignal;
  93	LLHost				mHost;
  94	
  95	PendingReply(const LLUUID& id, const LLHost& host)
  96		: mID(id), mHost(host)
  97	{
  98	}
  99	
 100	boost::signals2::connection setCallback(const LLCacheNameCallback& cb)
 101	{
 102		return mSignal.connect(cb);
 103	}
 104	
 105	void done()			{ mID.setNull(); }
 106	bool isDone() const	{ return mID.isNull() != FALSE; }
 107};
 108
 109class ReplySender
 110{
 111public:
 112	ReplySender(LLMessageSystem* msg);
 113	~ReplySender();
 114
 115	void send(const LLUUID& id,
 116		const LLCacheNameEntry& entry, const LLHost& host);
 117
 118private:
 119	void flush();
 120
 121	LLMessageSystem*	mMsg;
 122	bool				mPending;
 123	bool				mCurrIsGroup;
 124	LLHost				mCurrHost;
 125};
 126
 127ReplySender::ReplySender(LLMessageSystem* msg)
 128	: mMsg(msg), mPending(false), mCurrIsGroup(false)
 129{ }
 130
 131ReplySender::~ReplySender()
 132{
 133	flush();
 134}
 135
 136void ReplySender::send(const LLUUID& id,
 137	const LLCacheNameEntry& entry, const LLHost& host)
 138{
 139	if (mPending)
 140	{
 141		if (mCurrIsGroup != entry.mIsGroup
 142		||  mCurrHost != host)
 143		{
 144			flush();
 145		}
 146	}
 147
 148	if (!mPending)
 149	{
 150		mPending = true;
 151		mCurrIsGroup = entry.mIsGroup;
 152		mCurrHost = host;
 153
 154		if(mCurrIsGroup)
 155			mMsg->newMessageFast(_PREHASH_UUIDGroupNameReply);
 156		else
 157			mMsg->newMessageFast(_PREHASH_UUIDNameReply);
 158	}
 159
 160	mMsg->nextBlockFast(_PREHASH_UUIDNameBlock);
 161	mMsg->addUUIDFast(_PREHASH_ID, id);
 162	if(mCurrIsGroup)
 163	{
 164		mMsg->addStringFast(_PREHASH_GroupName, entry.mGroupName);
 165	}
 166	else
 167	{
 168		mMsg->addStringFast(_PREHASH_FirstName,	entry.mFirstName);
 169		mMsg->addStringFast(_PREHASH_LastName, entry.mLastName);
 170	}
 171
 172	if(mMsg->isSendFullFast(_PREHASH_UUIDNameBlock))
 173	{
 174		flush();
 175	}
 176}
 177
 178void ReplySender::flush()
 179{
 180	if (mPending)
 181	{
 182		mMsg->sendReliable(mCurrHost);
 183		mPending = false;
 184	}
 185}
 186
 187
 188typedef std::set<LLUUID>					AskQueue;
 189typedef std::list<PendingReply*>			ReplyQueue;
 190typedef std::map<LLUUID,U32>				PendingQueue;
 191typedef std::map<LLUUID, LLCacheNameEntry*> Cache;
 192typedef std::map<std::string, LLUUID> 		ReverseCache;
 193
 194class LLCacheName::Impl
 195{
 196public:
 197	LLMessageSystem*	mMsg;
 198	LLHost				mUpstreamHost;
 199
 200	Cache				mCache;
 201		// the map of UUIDs to names
 202	ReverseCache   	  	mReverseCache;
 203		// map of names to UUIDs
 204	
 205	AskQueue			mAskNameQueue;
 206	AskQueue			mAskGroupQueue;
 207		// UUIDs to ask our upstream host about
 208	
 209	PendingQueue		mPendingQueue;
 210		// UUIDs that have been requested but are not in cache yet.
 211
 212	ReplyQueue			mReplyQueue;
 213		// requests awaiting replies from us
 214
 215	LLCacheNameSignal	mSignal;
 216
 217	LLFrameTimer		mProcessTimer;
 218
 219	Impl(LLMessageSystem* msg);
 220	~Impl();
 221
 222	BOOL getName(const LLUUID& id, std::string& first, std::string& last);
 223
 224	boost::signals2::connection addPending(const LLUUID& id, const LLCacheNameCallback& callback);
 225	void addPending(const LLUUID& id, const LLHost& host);
 226	
 227	void processPendingAsks();
 228	void processPendingReplies();
 229	void sendRequest(const char* msg_name, const AskQueue& queue);
 230	bool isRequestPending(const LLUUID& id);
 231
 232	// Message system callbacks.
 233	void processUUIDRequest(LLMessageSystem* msg, bool isGroup);
 234	void processUUIDReply(LLMessageSystem* msg, bool isGroup);
 235
 236	static void handleUUIDNameRequest(LLMessageSystem* msg, void** userdata);
 237	static void handleUUIDNameReply(LLMessageSystem* msg, void** userdata);
 238	static void handleUUIDGroupNameRequest(LLMessageSystem* msg, void** userdata);
 239	static void handleUUIDGroupNameReply(LLMessageSystem* msg, void** userdata);
 240};
 241
 242
 243/// --------------------------------------------------------------------------
 244/// class LLCacheName
 245/// ---------------------------------------------------------------------------
 246
 247LLCacheName::LLCacheName(LLMessageSystem* msg)
 248	: impl(* new Impl(msg))
 249	{ }
 250
 251LLCacheName::LLCacheName(LLMessageSystem* msg, const LLHost& upstream_host)
 252	: impl(* new Impl(msg))
 253{
 254	sCacheName["waiting"] = "(Loading...)";
 255	sCacheName["nobody"] = "(nobody)";
 256	sCacheName["none"] = "(none)";
 257	setUpstream(upstream_host);
 258}
 259
 260LLCacheName::~LLCacheName()
 261{
 262	delete &impl;
 263}
 264
 265LLCacheName::Impl::Impl(LLMessageSystem* msg)
 266	: mMsg(msg), mUpstreamHost(LLHost::invalid)
 267{
 268	mMsg->setHandlerFuncFast(
 269		_PREHASH_UUIDNameRequest, handleUUIDNameRequest, (void**)this);
 270	mMsg->setHandlerFuncFast(
 271		_PREHASH_UUIDNameReply, handleUUIDNameReply, (void**)this);
 272	mMsg->setHandlerFuncFast(
 273		_PREHASH_UUIDGroupNameRequest, handleUUIDGroupNameRequest, (void**)this);
 274	mMsg->setHandlerFuncFast(
 275		_PREHASH_UUIDGroupNameReply, handleUUIDGroupNameReply, (void**)this);
 276}
 277
 278
 279LLCacheName::Impl::~Impl()
 280{
 281	for_each(mCache.begin(), mCache.end(), DeletePairedPointer());
 282	for_each(mReplyQueue.begin(), mReplyQueue.end(), DeletePointer());
 283}
 284
 285boost::signals2::connection LLCacheName::Impl::addPending(const LLUUID& id, const LLCacheNameCallback& callback)
 286{
 287	PendingReply* reply = new PendingReply(id, LLHost());
 288	boost::signals2::connection res = reply->setCallback(callback);
 289	mReplyQueue.push_back(reply);
 290	return res;
 291}
 292
 293void LLCacheName::Impl::addPending(const LLUUID& id, const LLHost& host)
 294{
 295	PendingReply* reply = new PendingReply(id, host);
 296	mReplyQueue.push_back(reply);
 297}
 298
 299void LLCacheName::setUpstream(const LLHost& upstream_host)
 300{
 301	impl.mUpstreamHost = upstream_host;
 302}
 303
 304boost::signals2::connection LLCacheName::addObserver(const LLCacheNameCallback& callback)
 305{
 306	return impl.mSignal.connect(callback);
 307}
 308
 309bool LLCacheName::importFile(std::istream& istr)
 310{
 311	LLSD data;
 312	if(LLSDSerialize::fromXMLDocument(data, istr) < 1)
 313		return false;
 314
 315	// We'll expire entries more than a week old
 316	U32 now = (U32)time(NULL);
 317	const U32 SECS_PER_DAY = 60 * 60 * 24;
 318	U32 delete_before_time = now - (7 * SECS_PER_DAY);
 319
 320	// iterate over the agents
 321	S32 count = 0;
 322	LLSD agents = data[AGENTS];
 323	LLSD::map_iterator iter = agents.beginMap();
 324	LLSD::map_iterator end = agents.endMap();
 325	for( ; iter != end; ++iter)
 326	{
 327		LLUUID id((*iter).first);
 328		LLSD agent = (*iter).second;
 329		U32 ctime = (U32)agent[CTIME].asInteger();
 330		if(ctime < delete_before_time) continue;
 331
 332		LLCacheNameEntry* entry = new LLCacheNameEntry();
 333		entry->mIsGroup = false;
 334		entry->mCreateTime = ctime;
 335		entry->mFirstName = agent[FIRST].asString();
 336		entry->mLastName = agent[LAST].asString();
 337		impl.mCache[id] = entry;
 338		std::string fullname = buildFullName(entry->mFirstName, entry->mLastName);
 339		impl.mReverseCache[fullname] = id;
 340
 341		++count;
 342	}
 343	llinfos << "LLCacheName loaded " << count << " agent names" << llendl;
 344
 345	count = 0;
 346	LLSD groups = data[GROUPS];
 347	iter = groups.beginMap();
 348	end = groups.endMap();
 349	for( ; iter != end; ++iter)
 350	{
 351		LLUUID id((*iter).first);
 352		LLSD group = (*iter).second;
 353		U32 ctime = (U32)group[CTIME].asInteger();
 354		if(ctime < delete_before_time) continue;
 355
 356		LLCacheNameEntry* entry = new LLCacheNameEntry();
 357		entry->mIsGroup = true;
 358		entry->mCreateTime = ctime;
 359		entry->mGroupName = group[NAME].asString();
 360		impl.mCache[id] = entry;
 361		impl.mReverseCache[entry->mGroupName] = id;
 362		++count;
 363	}
 364	llinfos << "LLCacheName loaded " << count << " group names" << llendl;
 365	return true;
 366}
 367
 368void LLCacheName::exportFile(std::ostream& ostr)
 369{
 370	LLSD data;
 371	Cache::iterator iter = impl.mCache.begin();
 372	Cache::iterator end = impl.mCache.end();
 373	for( ; iter != end; ++iter)
 374	{
 375		// Only write entries for which we have valid data.
 376		LLCacheNameEntry* entry = iter->second;
 377		if(!entry
 378		   || (std::string::npos != entry->mFirstName.find('?'))
 379		   || (std::string::npos != entry->mGroupName.find('?')))
 380		{
 381			continue;
 382		}
 383
 384		// store it
 385		LLUUID id = iter->first;
 386		std::string id_str = id.asString();
 387		// IDEVO TODO: Should we store SLIDs with last name "Resident" or not?
 388		if(!entry->mFirstName.empty() && !entry->mLastName.empty())
 389		{
 390			data[AGENTS][id_str][FIRST] = entry->mFirstName;
 391			data[AGENTS][id_str][LAST] = entry->mLastName;
 392			data[AGENTS][id_str][CTIME] = (S32)entry->mCreateTime;
 393		}
 394		else if(entry->mIsGroup && !entry->mGroupName.empty())
 395		{
 396			data[GROUPS][id_str][NAME] = entry->mGroupName;
 397			data[GROUPS][id_str][CTIME] = (S32)entry->mCreateTime;
 398		}
 399	}
 400
 401	LLSDSerialize::toPrettyXML(data, ostr);
 402}
 403
 404
 405BOOL LLCacheName::Impl::getName(const LLUUID& id, std::string& first, std::string& last)
 406{
 407	if(id.isNull())
 408	{
 409		first = sCacheName["nobody"];
 410		last.clear();
 411		return TRUE;
 412	}
 413
 414	LLCacheNameEntry* entry = get_ptr_in_map(mCache, id );
 415	if (entry)
 416	{
 417		first = entry->mFirstName;
 418		last =  entry->mLastName;
 419		return TRUE;
 420	}
 421	else
 422	{
 423		first = sCacheName["waiting"];
 424		last.clear();
 425		if (!isRequestPending(id))
 426		{
 427			mAskNameQueue.insert(id);
 428		}	
 429		return FALSE;
 430	}
 431
 432}
 433
 434// static
 435void LLCacheName::localizeCacheName(std::string key, std::string value)
 436{
 437	if (key!="" && value!= "" )
 438		sCacheName[key]=value;
 439	else
 440		llwarns<< " Error localizing cache key " << key << " To "<< value<<llendl;
 441}
 442
 443BOOL LLCacheName::getFullName(const LLUUID& id, std::string& fullname)
 444{
 445	std::string first_name, last_name;
 446	BOOL res = impl.getName(id, first_name, last_name);
 447	fullname = buildFullName(first_name, last_name);
 448	return res;
 449}
 450
 451
 452
 453BOOL LLCacheName::getGroupName(const LLUUID& id, std::string& group)
 454{
 455	if(id.isNull())
 456	{
 457		group = sCacheName["none"];
 458		return TRUE;
 459	}
 460
 461	LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache,id);
 462	if (entry && entry->mGroupName.empty())
 463	{
 464		// COUNTER-HACK to combat James' HACK in exportFile()...
 465		// this group name was loaded from a name cache that did not
 466		// bother to save the group name ==> we must ask for it
 467		lldebugs << "LLCacheName queuing HACK group request: " << id << llendl;
 468		entry = NULL;
 469	}
 470
 471	if (entry)
 472	{
 473		group = entry->mGroupName;
 474		return TRUE;
 475	}
 476	else 
 477	{
 478		group = sCacheName["waiting"];
 479		if (!impl.isRequestPending(id))
 480		{
 481			impl.mAskGroupQueue.insert(id);
 482		}
 483		return FALSE;
 484	}
 485}
 486
 487BOOL LLCacheName::getUUID(const std::string& first, const std::string& last, LLUUID& id)
 488{
 489	std::string full_name = buildFullName(first, last);
 490	return getUUID(full_name, id);
 491}
 492
 493BOOL LLCacheName::getUUID(const std::string& full_name, LLUUID& id)
 494{
 495	ReverseCache::iterator iter = impl.mReverseCache.find(full_name);
 496	if (iter != impl.mReverseCache.end())
 497	{
 498		id = iter->second;
 499		return TRUE;
 500	}
 501	else
 502	{
 503		return FALSE;
 504	}
 505}
 506
 507//static
 508std::string LLCacheName::buildFullName(const std::string& first, const std::string& last)
 509{
 510	std::string fullname = first;
 511	if (!last.empty()
 512		&& last != "Resident")
 513	{
 514		fullname += ' ';
 515		fullname += last;
 516	}
 517	return fullname;
 518}
 519
 520//static
 521std::string LLCacheName::cleanFullName(const std::string& full_name)
 522{
 523	return full_name.substr(0, full_name.find(" Resident"));
 524}
 525
 526//static 
 527std::string LLCacheName::buildUsername(const std::string& full_name)
 528{
 529	// rare, but handle hard-coded error names returned from server
 530	if (full_name == "(\?\?\?) (\?\?\?)")
 531	{
 532		return "(\?\?\?)";
 533	}
 534	
 535	std::string::size_type index = full_name.find(' ');
 536
 537	if (index != std::string::npos)
 538	{
 539		std::string username;
 540		username = full_name.substr(0, index);
 541		std::string lastname = full_name.substr(index+1);
 542
 543		if (lastname != "Resident")
 544		{
 545			username = username + "." + lastname;
 546		}
 547		
 548		LLStringUtil::toLower(username);
 549		return username;
 550	}
 551
 552	// if the input wasn't a correctly formatted legacy name just return it unchanged
 553	return full_name;
 554}
 555
 556//static 
 557std::string LLCacheName::buildLegacyName(const std::string& complete_name)
 558{
 559	//boost::regexp was showing up in the crashreporter, so doing  
 560	//painfully manual parsing using substr. LF
 561	S32 open_paren = complete_name.rfind(" (");
 562	S32 close_paren = complete_name.rfind(')');
 563
 564	if (open_paren != std::string::npos &&
 565		close_paren == complete_name.length()-1)
 566	{
 567		S32 length = close_paren - open_paren - 2;
 568		std::string legacy_name = complete_name.substr(open_paren+2, length);
 569		
 570		if (legacy_name.length() > 0)
 571		{			
 572			std::string cap_letter = legacy_name.substr(0, 1);
 573			LLStringUtil::toUpper(cap_letter);
 574			legacy_name = cap_letter + legacy_name.substr(1);
 575	
 576			S32 separator = legacy_name.find('.');
 577
 578			if (separator != std::string::npos)
 579			{
 580				std::string last_name = legacy_name.substr(separator+1);
 581				legacy_name = legacy_name.substr(0, separator);
 582
 583				if (last_name.length() > 0)
 584				{
 585					cap_letter = last_name.substr(0, 1);
 586					LLStringUtil::toUpper(cap_letter);
 587					legacy_name = legacy_name + " " + cap_letter + last_name.substr(1);
 588				}
 589			}
 590
 591			return legacy_name;
 592		}
 593	}
 594
 595	return complete_name;
 596}
 597
 598// This is a little bit kludgy. LLCacheNameCallback is a slot instead of a function pointer.
 599//  The reason it is a slot is so that the legacy get() function below can bind an old callback
 600//  and pass it as a slot. The reason it isn't a boost::function is so that trackable behavior
 601//  doesn't get lost. As a result, we have to bind the slot to a signal to call it, even when
 602//  we call it immediately. -Steve
 603// NOTE: Even though passing first and last name is a bit of extra overhead, it eliminates the
 604//  potential need for any parsing should any code need to handle first and last name independently.
 605boost::signals2::connection LLCacheName::get(const LLUUID& id, bool is_group, const LLCacheNameCallback& callback)
 606{
 607	boost::signals2::connection res;
 608	
 609	if(id.isNull())
 610	{
 611		LLCacheNameSignal signal;
 612		signal.connect(callback);
 613		signal(id, sCacheName["nobody"], is_group);
 614		return res;
 615	}
 616
 617	LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache, id );
 618	if (entry)
 619	{
 620		LLCacheNameSignal signal;
 621		signal.connect(callback);
 622		// id found in map therefore we can call the callback immediately.
 623		if (entry->mIsGroup)
 624		{
 625			signal(id, entry->mGroupName, entry->mIsGroup);
 626		}
 627		else
 628		{
 629			std::string fullname =
 630				buildFullName(entry->mFirstName, entry->mLastName);
 631			signal(id, fullname, entry->mIsGroup);
 632		}
 633	}
 634	else
 635	{
 636		// id not found in map so we must queue the callback call until available.
 637		if (!impl.isRequestPending(id))
 638		{
 639			if (is_group)
 640			{
 641				impl.mAskGroupQueue.insert(id);
 642			}
 643			else
 644			{
 645				impl.mAskNameQueue.insert(id);
 646			}
 647		}
 648		res = impl.addPending(id, callback);
 649	}
 650	return res;
 651}
 652
 653boost::signals2::connection LLCacheName::getGroup(const LLUUID& group_id,
 654												  const LLCacheNameCallback& callback)
 655{
 656	return get(group_id, true, callback);
 657}
 658
 659boost::signals2::connection LLCacheName::get(const LLUUID& id, bool is_group, old_callback_t callback, void* user_data)
 660{
 661	return get(id, is_group, boost::bind(callback, _1, _2, _3, user_data));
 662}
 663
 664void LLCacheName::processPending()
 665{
 666	LLMemType mt_pp(LLMemType::MTYPE_CACHE_PROCESS_PENDING);
 667	const F32 SECS_BETWEEN_PROCESS = 0.1f;
 668	if(!impl.mProcessTimer.checkExpirationAndReset(SECS_BETWEEN_PROCESS))
 669	{
 670		return;
 671	}
 672
 673	if(!impl.mUpstreamHost.isOk())
 674	{
 675		lldebugs << "LLCacheName::processPending() - bad upstream host."
 676				 << llendl;
 677		return;
 678	}
 679
 680	impl.processPendingAsks();
 681	impl.processPendingReplies();
 682}
 683
 684void LLCacheName::deleteEntriesOlderThan(S32 secs)
 685{
 686	U32 now = (U32)time(NULL);
 687	U32 expire_time = now - secs;
 688	for(Cache::iterator iter = impl.mCache.begin(); iter != impl.mCache.end(); )
 689	{
 690		Cache::iterator curiter = iter++;
 691		LLCacheNameEntry* entry = curiter->second;
 692		if (entry->mCreateTime < expire_time)
 693		{
 694			delete entry;
 695			impl.mCache.erase(curiter);
 696		}
 697	}
 698
 699	// These are pending requests that we never heard back from.
 700	U32 pending_expire_time = now - PENDING_TIMEOUT_SECS;
 701	for(PendingQueue::iterator p_iter = impl.mPendingQueue.begin();
 702		p_iter != impl.mPendingQueue.end(); )
 703	{
 704		PendingQueue::iterator p_curitor = p_iter++;
 705 
 706		if (p_curitor->second < pending_expire_time)
 707		{
 708			impl.mPendingQueue.erase(p_curitor);
 709		}
 710	}
 711}
 712
 713
 714void LLCacheName::dump()
 715{
 716	for (Cache::iterator iter = impl.mCache.begin(),
 717			 end = impl.mCache.end();
 718		 iter != end; iter++)
 719	{
 720		LLCacheNameEntry* entry = iter->second;
 721		if (entry->mIsGroup)
 722		{
 723			llinfos
 724				<< iter->first << " = (group) "
 725				<< entry->mGroupName
 726				<< " @ " << entry->mCreateTime
 727				<< llendl;
 728		}
 729		else
 730		{
 731			llinfos
 732				<< iter->first << " = "
 733				<< buildFullName(entry->mFirstName, entry->mLastName)
 734				<< " @ " << entry->mCreateTime
 735				<< llendl;
 736		}
 737	}
 738}
 739
 740void LLCacheName::dumpStats()
 741{
 742	llinfos << "Queue sizes: "
 743			<< " Cache=" << impl.mCache.size()
 744			<< " AskName=" << impl.mAskNameQueue.size()
 745			<< " AskGroup=" << impl.mAskGroupQueue.size()
 746			<< " Pending=" << impl.mPendingQueue.size()
 747			<< " Reply=" << impl.mReplyQueue.size()
 748// 			<< " Observers=" << impl.mSignal.size()
 749			<< llendl;
 750}
 751
 752void LLCacheName::clear()
 753{
 754	for_each(impl.mCache.begin(), impl.mCache.end(), DeletePairedPointer());
 755	impl.mCache.clear();
 756}
 757
 758//static 
 759std::string LLCacheName::getDefaultName()
 760{
 761	return sCacheName["waiting"];
 762}
 763
 764//static 
 765std::string LLCacheName::getDefaultLastName()
 766{
 767	return "Resident";
 768}
 769
 770void LLCacheName::Impl::processPendingAsks()
 771{
 772	LLMemType mt_ppa(LLMemType::MTYPE_CACHE_PROCESS_PENDING_ASKS);
 773	sendRequest(_PREHASH_UUIDNameRequest, mAskNameQueue);
 774	sendRequest(_PREHASH_UUIDGroupNameRequest, mAskGroupQueue);
 775	mAskNameQueue.clear();
 776	mAskGroupQueue.clear();
 777}
 778
 779void LLCacheName::Impl::processPendingReplies()
 780{
 781	LLMemType mt_ppr(LLMemType::MTYPE_CACHE_PROCESS_PENDING_REPLIES);
 782	// First call all the callbacks, because they might send messages.
 783	for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); ++it)
 784	{
 785		PendingReply* reply = *it;
 786		LLCacheNameEntry* entry = get_ptr_in_map(mCache, reply->mID);
 787		if(!entry) continue;
 788
 789		if (!entry->mIsGroup)
 790		{
 791			std::string fullname =
 792				LLCacheName::buildFullName(entry->mFirstName, entry->mLastName);
 793			(reply->mSignal)(reply->mID, fullname, false);
 794		}
 795		else
 796		{
 797			(reply->mSignal)(reply->mID, entry->mGroupName, true);
 798		}
 799	}
 800
 801	// Forward on all replies, if needed.
 802	ReplySender sender(mMsg);
 803	for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); ++it)
 804	{
 805		PendingReply* reply = *it;
 806		LLCacheNameEntry* entry = get_ptr_in_map(mCache, reply->mID);
 807		if(!entry) continue;
 808
 809		if (reply->mHost.isOk())
 810		{
 811			sender.send(reply->mID, *entry, reply->mHost);
 812		}
 813
 814		reply->done();
 815	}
 816	
 817	for(ReplyQueue::iterator it = mReplyQueue.begin(); it != mReplyQueue.end(); )
 818	{
 819		ReplyQueue::iterator curit = it++;
 820		PendingReply* reply = *curit;
 821		if (reply->isDone())
 822		{
 823			delete reply;
 824			mReplyQueue.erase(curit);
 825		}
 826	}
 827}
 828
 829
 830void LLCacheName::Impl::sendRequest(
 831	const char* msg_name,
 832	const AskQueue& queue)
 833{
 834	if(queue.empty())
 835	{
 836		return;		
 837	}
 838
 839	bool start_new_message = true;
 840	AskQueue::const_iterator it = queue.begin();
 841	AskQueue::const_iterator end = queue.end();
 842	for(; it != end; ++it)
 843	{
 844		if(start_new_message)
 845		{
 846			start_new_message = false;
 847			mMsg->newMessageFast(msg_name);
 848		}
 849		mMsg->nextBlockFast(_PREHASH_UUIDNameBlock);
 850		mMsg->addUUIDFast(_PREHASH_ID, (*it));
 851
 852		if(mMsg->isSendFullFast(_PREHASH_UUIDNameBlock))
 853		{
 854			start_new_message = true;
 855			mMsg->sendReliable(mUpstreamHost);
 856		}
 857	}
 858	if(!start_new_message)
 859	{
 860		mMsg->sendReliable(mUpstreamHost);
 861	}
 862}
 863
 864bool LLCacheName::Impl::isRequestPending(const LLUUID& id)
 865{
 866	U32 now = (U32)time(NULL);
 867	U32 expire_time = now - PENDING_TIMEOUT_SECS;
 868
 869	PendingQueue::iterator iter = mPendingQueue.find(id);
 870
 871	if (iter == mPendingQueue.end()
 872		|| (iter->second < expire_time) )
 873	{
 874		mPendingQueue[id] = now;
 875		return false;
 876	}
 877
 878	return true;
 879}
 880	
 881void LLCacheName::Impl::processUUIDRequest(LLMessageSystem* msg, bool isGroup)
 882{
 883	// You should only get this message if the cache is at the simulator
 884	// level, hence having an upstream provider.
 885	if (!mUpstreamHost.isOk())
 886	{
 887		llwarns << "LLCacheName - got UUID name/group request, but no upstream provider!" << llendl;
 888		return;
 889	}
 890
 891	LLHost fromHost = msg->getSender();
 892	ReplySender sender(msg);
 893
 894	S32 count = msg->getNumberOfBlocksFast(_PREHASH_UUIDNameBlock);
 895	for(S32 i = 0; i < count; ++i)
 896	{
 897		LLUUID id;
 898		msg->getUUIDFast(_PREHASH_UUIDNameBlock, _PREHASH_ID, id, i);
 899		LLCacheNameEntry* entry = get_ptr_in_map(mCache, id);
 900		if(entry)
 901		{
 902			if (isGroup != entry->mIsGroup)
 903			{
 904				llwarns << "LLCacheName - Asked for "
 905						<< (isGroup ? "group" : "user") << " name, "
 906						<< "but found "
 907						<< (entry->mIsGroup ? "group" : "user")
 908						<< ": " << id << llendl;
 909			}
 910			else
 911			{
 912				// ...it's in the cache, so send it as the reply
 913				sender.send(id, *entry, fromHost);
 914			}
 915		}
 916		else
 917		{
 918			if (!isRequestPending(id))
 919			{
 920				if (isGroup)
 921				{
 922					mAskGroupQueue.insert(id);
 923				}
 924				else
 925				{
 926					mAskNameQueue.insert(id);
 927				}
 928			}
 929			
 930			addPending(id, fromHost);
 931		}
 932	}
 933}
 934
 935
 936
 937void LLCacheName::Impl::processUUIDReply(LLMessageSystem* msg, bool isGroup)
 938{
 939	S32 count = msg->getNumberOfBlocksFast(_PREHASH_UUIDNameBlock);
 940	for(S32 i = 0; i < count; ++i)
 941	{
 942		LLUUID id;
 943		msg->getUUIDFast(_PREHASH_UUIDNameBlock, _PREHASH_ID, id, i);
 944		LLCacheNameEntry* entry = get_ptr_in_map(mCache, id);
 945		if (!entry)
 946		{
 947			entry = new LLCacheNameEntry;
 948			mCache[id] = entry;
 949		}
 950
 951		mPendingQueue.erase(id);
 952
 953		entry->mIsGroup = isGroup;
 954		entry->mCreateTime = (U32)time(NULL);
 955		if (!isGroup)
 956		{
 957			msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_FirstName, entry->mFirstName, i);
 958			msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_LastName,  entry->mLastName, i);
 959		}
 960		else
 961		{	// is group
 962			msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_GroupName, entry->mGroupName, i);
 963			LLStringFn::replace_ascii_controlchars(entry->mGroupName, LL_UNKNOWN_CHAR);
 964		}
 965
 966		if (!isGroup)
 967		{
 968			// NOTE: Very occasionally the server sends down a full name
 969			// in the first name field with an empty last name, for example,
 970			// first = "Ladanie1 Resident", last = "".
 971			// I cannot reproduce this, nor can I find a bug in the server code.
 972			// Ensure "Resident" does not appear via cleanFullName, because
 973			// buildFullName only checks last name. JC
 974			std::string full_name;
 975			if (entry->mLastName.empty())
 976			{
 977				full_name = cleanFullName(entry->mFirstName);
 978
 979				//fix what we are putting in the cache
 980				entry->mFirstName = full_name;
 981				entry->mLastName = "Resident";
 982			}
 983			else
 984			{
 985				full_name = LLCacheName::buildFullName(entry->mFirstName, entry->mLastName);
 986			}
 987			mSignal(id, full_name, false);
 988			mReverseCache[full_name] = id;
 989		}
 990		else
 991		{
 992			mSignal(id, entry->mGroupName, true);
 993			mReverseCache[entry->mGroupName] = id;
 994		}
 995	}
 996}
 997
 998
 999
1000// static call back functions
1001
1002void LLCacheName::Impl::handleUUIDNameReply(LLMessageSystem* msg, void** userData)
1003{
1004	((LLCacheName::Impl*)userData)->processUUIDReply(msg, false);
1005}
1006
1007void LLCacheName::Impl::handleUUIDNameRequest(LLMessageSystem* msg, void** userData)
1008{
1009	((LLCacheName::Impl*)userData)->processUUIDRequest(msg, false);
1010}
1011
1012void LLCacheName::Impl::handleUUIDGroupNameRequest(LLMessageSystem* msg, void** userData)
1013{
1014	((LLCacheName::Impl*)userData)->processUUIDRequest(msg, true);
1015}
1016
1017void LLCacheName::Impl::handleUUIDGroupNameReply(LLMessageSystem* msg, void** userData)
1018{
1019	((LLCacheName::Impl*)userData)->processUUIDReply(msg, true);
1020}