Scott Lawrence avatar Scott Lawrence committed 88065ad Merge

pull fix for STORM-211

Comments (0)

Files changed (782)

 7076e22f9f43f479a4ea75eac447a36364bead5a 2.2.0-beta1
 9822eb3e25f7fe0c28ffd8aba45c507caa383cbc 2.2.0-beta2
 b0cd7e150009809a0b5b0a9d5785cd4bb230413a 2.2.0-beta3
+00a831292231faad7e44c69f76cb96f175b8dfad 2.2.0-beta4
 gooey.build_viewer_update_version_manager = false
 
 # ========================================
+# Display Names project
+# ========================================
+
+#viewer-identity-evolution.email = leyla@lindenlab.com
+viewer-identity.build_Debug = false
+viewer-identity.build_RelWithDebInfo = false
+viewer-identity.build_viewer = true
+viewer-identity.build_server = false
+viewer-identity.build_server_tests = false
+viewer-identity.build_Linux = true
+viewer-identity.build_hg_bundle = true
+viewer-identity.bulld_docs = true
+viewer-identity.viewer_channel = "Second Life Project Viewer"
+viewer-identity.login_channel = "Second Life Project Viewer"
+viewer-identity.viewer_grid = aditi
+viewer-identity.build_viewer_update_version_manager = false
+
+
+
+# ========================================
 # palange
 # ========================================
 
     -t $variant \
     -G "$cmake_generator" \
    configure \
+	-DGRID:STRING="$viewer_grid" \
     -DVIEWER_CHANNEL:STRING="$viewer_channel" \
     -DVIEWER_LOGIN_CHANNEL:STRING="$login_channel" \
     -DINSTALL_PROPRIETARY:BOOL=ON \
 				</map>
 
 				<!-- Server to client -->
+				<key>DisplayNameUpdate</key>
+				<map>
+					<key>flavor</key>
+					<string>llsd</string>
+					<key>trusted-sender</key>
+					<boolean>true</boolean>
+				</map>
+                
 				<key>ParcelVoiceInfo</key>
 				<map>
 					<key>flavor</key>
           <boolean>true</boolean>
         </map>
 
+        <key>SetDisplayNameReply</key>
+        <map>
+          <key>flavor</key>
+          <string>llsd</string>
+          <key>trusted-sender</key>
+          <boolean>true</boolean>
+        </map>
+
         <key>DirLandReply</key>
         <map>
           <key>flavor</key>

indra/cmake/CMakeLists.txt

     FindBerkeleyDB.cmake
     FindCARes.cmake
     FindELFIO.cmake
+    FindFMOD.cmake
     FindGooglePerfTools.cmake
     FindMono.cmake
     FindMT.cmake

indra/cmake/Copy3rdPartyLibs.cmake

       set(release_files ${release_files} libtcmalloc_minimal.dll)
     endif(USE_GOOGLE_PERFTOOLS)
 
-    if (FMOD_SDK_DIR)
-        set(fmod_files fmod.dll)
-    endif (FMOD_SDK_DIR)
+    if (FMOD)
+      set(debug_files ${debug_files} fmod.dll)
+      set(release_files ${release_files} fmod.dll)
+    endif (FMOD)
 
     #*******************************
     # LLKDU
         libssl.so.0.9.7
        )
 
-    if (FMOD_SDK_DIR)
-        set(fmod_files "libfmod-3.75.so")
-    endif (FMOD_SDK_DIR)
+    if (FMOD)
+      set(release_files ${release_files} "libfmod-3.75.so")
+    endif (FMOD)
 
     #*******************************
     # LLKDU
     )
 set(third_party_targets ${third_party_targets} ${out_targets})
 
-if (FMOD_SDK_DIR)
-    copy_if_different(
-        ${FMOD_SDK_DIR} 
-        "${CMAKE_CURRENT_BINARY_DIR}/Debug"
-        out_targets 
-        ${fmod_files}
-        )
-    set(all_targets ${all_targets} ${out_targets})
-    copy_if_different(
-        ${FMOD_SDK_DIR} 
-        "${CMAKE_CURRENT_BINARY_DIR}/Release"
-        out_targets 
-        ${fmod_files}
-        )
-    set(all_targets ${all_targets} ${out_targets})
-    copy_if_different(
-        ${FMOD_SDK_DIR} 
-        "${CMAKE_CURRENT_BINARY_DIR}/RelWithDbgInfo"
-        out_targets 
-        ${fmod_files}
-        )
-    set(all_targets ${all_targets} ${out_targets})
-endif (FMOD_SDK_DIR)
-
 #*******************************
 # LLKDU
 set(internal_llkdu_path "${CMAKE_SOURCE_DIR}/llkdu")

indra/cmake/FMOD.cmake

 # -*- cmake -*-
 
-include(Linking)
-
-if(INSTALL_PROPRIETARY)
-  include(Prebuilt)
-  use_prebuilt_binary(fmod)
-endif(INSTALL_PROPRIETARY)
-
-find_library(FMOD_LIBRARY_RELEASE
-             NAMES fmod fmodvc fmod-3.75
-             PATHS
-             ${ARCH_PREBUILT_DIRS_RELEASE}
-             )
-
-find_library(FMOD_LIBRARY_DEBUG
-             NAMES fmod fmodvc fmod-3.75
-             PATHS
-             ${ARCH_PREBUILT_DIRS_DEBUG}
-             )
-
-if (FMOD_LIBRARY_RELEASE AND FMOD_LIBRARY_DEBUG)
-  set(FMOD_LIBRARY
-      debug ${FMOD_LIBRARY_DEBUG}
-      optimized ${FMOD_LIBRARY_RELEASE})
-elseif (FMOD_LIBRARY_RELEASE)
-  set(FMOD_LIBRARY ${FMOD_LIBRARY_RELEASE})
-endif (FMOD_LIBRARY_RELEASE AND FMOD_LIBRARY_DEBUG)
-
-if (NOT FMOD_LIBRARY)
-  set(FMOD_SDK_DIR CACHE PATH "Path to the FMOD SDK.")
-  if (FMOD_SDK_DIR)
-    find_library(FMOD_LIBRARY
-                 NAMES fmodvc fmod-3.75 fmod
-                 PATHS
-                 ${FMOD_SDK_DIR}/api/lib
-                 ${FMOD_SDK_DIR}/api
-                 ${FMOD_SDK_DIR}/lib
-                 ${FMOD_SDK_DIR}
-                 )
-  endif (FMOD_SDK_DIR)
-endif (NOT FMOD_LIBRARY)
-
-find_path(FMOD_INCLUDE_DIR fmod.h
-          ${LIBS_PREBUILT_DIR}/include
-          ${FMOD_SDK_DIR}/api/inc
-          ${FMOD_SDK_DIR}/inc
-          ${FMOD_SDK_DIR}
-          )
-
-if (FMOD_LIBRARY AND FMOD_INCLUDE_DIR)
-  set(FMOD ON CACHE BOOL "Use closed source FMOD sound library.")
-else (FMOD_LIBRARY AND FMOD_INCLUDE_DIR)
-  set(FMOD_LIBRARY "")
-  set(FMOD_INCLUDE_DIR "")
-  if (FMOD)
-    message(STATUS "No support for FMOD audio (need to set FMOD_SDK_DIR?)")
-  endif (FMOD)
-  set(FMOD OFF CACHE BOOL "Use closed source FMOD sound library.")
-endif (FMOD_LIBRARY AND FMOD_INCLUDE_DIR)
+set(FMOD ON CACHE BOOL "Use FMOD sound library.")
 
 if (FMOD)
-  message(STATUS "Building with FMOD audio support")
+  if (STANDALONE)
+    set(FMOD_FIND_REQUIRED ON)
+    include(FindFMOD)
+  else (STANDALONE)
+    include(Prebuilt)
+    use_prebuilt_binary(fmod)
+    
+    if (WINDOWS)
+      set(FMOD_LIBRARY fmod)
+    elseif (DARWIN)
+      set(FMOD_LIBRARY fmod)
+    elseif (LINUX)
+      set(FMOD_LIBRARY fmod-3.75)
+    endif (WINDOWS)
+
+    SET(FMOD_LIBRARIES ${FMOD_LIBRARY})
+    set(FMOD_INCLUDE_DIR ${LIBS_PREBUILT_DIR}/include)
+  endif (STANDALONE)
 endif (FMOD)

indra/cmake/FindFMOD.cmake

+# -*- cmake -*-
+
+# - Find FMOD
+# Find the FMOD includes and library
+# This module defines
+#  FMOD_INCLUDE_DIR, where to find fmod.h and fmod_errors.h
+#  FMOD_LIBRARIES, the libraries needed to use FMOD.
+#  FMOD, If false, do not try to use FMOD.
+# also defined, but not for general use are
+#  FMOD_LIBRARY, where to find the FMOD library.
+
+FIND_PATH(FMOD_INCLUDE_DIR fmod.h PATH_SUFFIXES fmod)
+
+SET(FMOD_NAMES ${FMOD_NAMES} fmod fmodvc fmod-3.75)
+FIND_LIBRARY(FMOD_LIBRARY
+  NAMES ${FMOD_NAMES}
+  PATH_SUFFIXES fmod
+  )
+
+IF (FMOD_LIBRARY AND FMOD_INCLUDE_DIR)
+  SET(FMOD_LIBRARIES ${FMOD_LIBRARY})
+  SET(FMOD_FOUND "YES")
+ELSE (FMOD_LIBRARY AND FMOD_INCLUDE_DIR)
+  SET(FMOD_FOUND "NO")
+ENDIF (FMOD_LIBRARY AND FMOD_INCLUDE_DIR)
+
+IF (FMOD_FOUND)
+  IF (NOT FMOD_FIND_QUIETLY)
+    MESSAGE(STATUS "Found FMOD: ${FMOD_LIBRARIES}")
+  ENDIF (NOT FMOD_FIND_QUIETLY)
+ELSE (FMOD_FOUND)
+  IF (FMOD_FIND_REQUIRED)
+    MESSAGE(FATAL_ERROR "Could not find FMOD library")
+  ENDIF (FMOD_FIND_REQUIRED)
+ENDIF (FMOD_FOUND)
+
+# Deprecated declarations.
+SET (NATIVE_FMOD_INCLUDE_PATH ${FMOD_INCLUDE_DIR} )
+GET_FILENAME_COMPONENT (NATIVE_FMOD_LIB_PATH ${FMOD_LIBRARY} PATH)
+
+MARK_AS_ADVANCED(
+  FMOD_LIBRARY
+  FMOD_INCLUDE_DIR
+  )

indra/cmake/run_build_test.py

 
 $LicenseInfo:firstyear=2009&license=viewerlgpl$
 Second Life Viewer Source Code
-Copyright (C) 2010, Linden Research, Inc.
+Copyright (C) 2009-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

indra/lib/python/indra/__init__.py

 
 $LicenseInfo:firstyear=2006&license=viewerlgpl$
 Second Life Viewer Source Code
-Copyright (C) 2010, Linden Research, Inc.
+Copyright (C) 2006-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

indra/llcommon/CMakeLists.txt

     llapp.cpp
     llapr.cpp
     llassettype.cpp
+    llavatarname.cpp
     llbase32.cpp
     llbase64.cpp
     llcommon.cpp
     llallocator.h
     llallocator_heap_profile.h
     llagentconstants.h
+    llavatarname.h
     llapp.h
     llapr.h
     llassettype.h

indra/llcommon/llavatarname.cpp

+/** 
+ * @file llavatarname.cpp
+ * @brief Represents name-related data for an avatar, such as the
+ * username/SLID ("bobsmith123" or "james.linden") and the display
+ * name ("James Cook")
+ *
+ * $LicenseInfo:firstyear=2010&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 "llavatarname.h"
+
+#include "lldate.h"
+#include "llsd.h"
+
+// Store these in pre-built std::strings to avoid memory allocations in
+// LLSD map lookups
+static const std::string USERNAME("username");
+static const std::string DISPLAY_NAME("display_name");
+static const std::string LEGACY_FIRST_NAME("legacy_first_name");
+static const std::string LEGACY_LAST_NAME("legacy_last_name");
+static const std::string IS_DISPLAY_NAME_DEFAULT("is_display_name_default");
+static const std::string DISPLAY_NAME_EXPIRES("display_name_expires");
+static const std::string DISPLAY_NAME_NEXT_UPDATE("display_name_next_update");
+
+LLAvatarName::LLAvatarName()
+:	mUsername(),
+	mDisplayName(),
+	mLegacyFirstName(),
+	mLegacyLastName(),
+	mIsDisplayNameDefault(false),
+	mIsDummy(false),
+	mExpires(F64_MAX),
+	mNextUpdate(0.0)
+{ }
+
+bool LLAvatarName::operator<(const LLAvatarName& rhs) const
+{
+	if (mUsername == rhs.mUsername)
+		return mDisplayName < rhs.mDisplayName;
+	else
+		return mUsername < rhs.mUsername;
+}
+
+LLSD LLAvatarName::asLLSD() const
+{
+	LLSD sd;
+	sd[USERNAME] = mUsername;
+	sd[DISPLAY_NAME] = mDisplayName;
+	sd[LEGACY_FIRST_NAME] = mLegacyFirstName;
+	sd[LEGACY_LAST_NAME] = mLegacyLastName;
+	sd[IS_DISPLAY_NAME_DEFAULT] = mIsDisplayNameDefault;
+	sd[DISPLAY_NAME_EXPIRES] = LLDate(mExpires);
+	sd[DISPLAY_NAME_NEXT_UPDATE] = LLDate(mNextUpdate);
+	return sd;
+}
+
+void LLAvatarName::fromLLSD(const LLSD& sd)
+{
+	mUsername = sd[USERNAME].asString();
+	mDisplayName = sd[DISPLAY_NAME].asString();
+	mLegacyFirstName = sd[LEGACY_FIRST_NAME].asString();
+	mLegacyLastName = sd[LEGACY_LAST_NAME].asString();
+	mIsDisplayNameDefault = sd[IS_DISPLAY_NAME_DEFAULT].asBoolean();
+	LLDate expires = sd[DISPLAY_NAME_EXPIRES];
+	mExpires = expires.secondsSinceEpoch();
+	LLDate next_update = sd[DISPLAY_NAME_NEXT_UPDATE];
+	mNextUpdate = next_update.secondsSinceEpoch();
+}
+
+std::string LLAvatarName::getCompleteName() const
+{
+	std::string name;
+	if (!mUsername.empty())
+	{
+		name = mDisplayName + " (" + mUsername + ")";
+	}
+	else
+	{
+		// ...display names are off, legacy name is in mDisplayName
+		name = mDisplayName;
+	}
+	return name;
+}
+
+std::string LLAvatarName::getLegacyName() const
+{
+	std::string name;
+	name.reserve( mLegacyFirstName.size() + 1 + mLegacyLastName.size() );
+	name = mLegacyFirstName;
+	name += " ";
+	name += mLegacyLastName;
+	return name;
+}

indra/llcommon/llavatarname.h

+/** 
+ * @file llavatarname.h
+ * @brief Represents name-related data for an avatar, such as the
+ * username/SLID ("bobsmith123" or "james.linden") and the display
+ * name ("James Cook")
+ *
+ * $LicenseInfo:firstyear=2010&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$
+ */
+#ifndef LLAVATARNAME_H
+#define LLAVATARNAME_H
+
+#include <string>
+
+class LLSD;
+
+class LL_COMMON_API LLAvatarName
+{
+public:
+	LLAvatarName();
+	
+	bool operator<(const LLAvatarName& rhs) const;
+
+	LLSD asLLSD() const;
+
+	void fromLLSD(const LLSD& sd);
+
+	// For normal names, returns "James Linden (james.linden)"
+	// When display names are disabled returns just "James Linden"
+	std::string getCompleteName() const;
+
+	// Returns "James Linden" or "bobsmith123 Resident" for backwards
+	// compatibility with systems like voice and muting
+	// *TODO: Eliminate this in favor of username only
+	std::string getLegacyName() const;
+
+	// "bobsmith123" or "james.linden", US-ASCII only
+	std::string mUsername;
+
+	// "Jose' Sanchez" or "James Linden", UTF-8 encoded Unicode
+	// Contains data whether or not user has explicitly set
+	// a display name; may duplicate their username.
+	std::string mDisplayName;
+
+	// For "James Linden", "James"
+	// For "bobsmith123", "bobsmith123"
+	// Used to communicate with legacy systems like voice and muting which
+	// rely on old-style names.
+	// *TODO: Eliminate this in favor of username only
+	std::string mLegacyFirstName;
+
+	// For "James Linden", "Linden"
+	// For "bobsmith123", "Resident"
+	// see above for rationale
+	std::string mLegacyLastName;
+
+	// If true, both display name and SLID were generated from
+	// a legacy first and last name, like "James Linden (james.linden)"
+	bool mIsDisplayNameDefault;
+
+	// Under error conditions, we may insert "dummy" records with
+	// names like "???" into caches as placeholders.  These can be
+	// shown in UI, but are not serialized.
+	bool mIsDummy;
+
+	// Names can change, so need to keep track of when name was
+	// last checked.
+	// Unix time-from-epoch seconds for efficiency
+	F64 mExpires;
+	
+	// You can only change your name every N hours, so record
+	// when the next update is allowed
+	// Unix time-from-epoch seconds
+	F64 mNextUpdate;
+};
+
+#endif

indra/llcommon/llchat.h

 #ifndef LL_LLCHAT_H
 #define LL_LLCHAT_H
 
-#include "llstring.h"
 #include "lluuid.h"
 #include "v3math.h"
 
 class LLChat
 {
 public:
-	LLChat(const std::string& text = LLStringUtil::null)
+	LLChat(const std::string& text = std::string())
 	:	mText(text),
 		mFromName(),
 		mFromID(),

indra/llcommon/llstring.cpp

 
 	nowT = time (NULL);
 
-	tmpT = localtime (&nowT);
-	localT = mktime (tmpT);
-
 	tmpT = gmtime (&nowT);
 	gmtT = mktime (tmpT);
 
+	tmpT = localtime (&nowT);
+	localT = mktime (tmpT);
+	
 	sLocalTimeOffset = (long) (gmtT - localT);
-
+	if (tmpT->tm_isdst)
+	{
+		sLocalTimeOffset -= 60 * 60;	// 1 hour
+	}
 
 	sPacificDaylightTime = daylight;
 	sPacificTimeOffset = (sPacificDaylightTime? 7 : 8 ) * 60 * 60;
Add a comment to this file

indra/llcommon/llversionviewer.h

File contents unchanged.

indra/llinventory/lltransactionflags.cpp

 	std::ostringstream ostr;
 	if(dest_id.isNull())
 	{
+		// *NOTE: Do not change these strings!  The viewer matches
+		// them in llviewermessage.cpp to perform localization.
+		// If you need to make changes, add a new, localizable message. JC
 		ostr << "You paid L$" << amount;
 		switch(transaction_type)
 		{
 		return description;
 	}
 	std::ostringstream ostr;
+	// *NOTE: Do not change these strings!  The viewer matches
+	// them in llviewermessage.cpp to perform localization.
+	// If you need to make changes, add a new, localizable message. JC
 	ostr << source_name << " paid you L$" << amount;
 	append_reason(ostr, transaction_type, description);
 	ostr << ".";

indra/llmessage/CMakeLists.txt

     llares.cpp
     llareslistener.cpp
     llassetstorage.cpp
+    llavatarnamecache.cpp
     llblowfishcipher.cpp
     llbuffer.cpp
     llbufferstream.cpp
     llares.h
     llareslistener.h
     llassetstorage.h
+    llavatarnamecache.h
     llblowfishcipher.h
     llbuffer.h
     llbufferstream.h
     "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llsdmessage_peer.py"
     )
 
+  LL_ADD_INTEGRATION_TEST(llavatarnamecache "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(llhost "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(llpartdata "" "${test_libs}")
   LL_ADD_INTEGRATION_TEST(llxfer_file "" "${test_libs}")

indra/llmessage/llavatarnamecache.cpp

+/** 
+ * @file llavatarnamecache.cpp
+ * @brief Provides lookup of avatar SLIDs ("bobsmith123") and display names
+ * ("James Cook") from avatar UUIDs.
+ *
+ * $LicenseInfo:firstyear=2010&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 "llavatarnamecache.h"
+
+#include "llcachename.h"		// we wrap this system
+#include "llframetimer.h"
+#include "llhttpclient.h"
+#include "llsd.h"
+#include "llsdserialize.h"
+
+#include <boost/tokenizer.hpp>
+
+#include <map>
+#include <set>
+
+namespace LLAvatarNameCache
+{
+	use_display_name_signal_t mUseDisplayNamesSignal;
+
+	// Manual override for display names - can disable even if the region
+	// supports it.
+	bool sUseDisplayNames = true;
+
+	// Cache starts in a paused state until we can determine if the
+	// current region supports display names.
+	bool sRunning = false;
+	
+	// Base lookup URL for name service.
+	// On simulator, loaded from indra.xml
+	// On viewer, usually a simulator capability (at People API team's request)
+	// Includes the trailing slash, like "http://pdp60.lindenlab.com:8000/agents/"
+	std::string sNameLookupURL;
+
+	// accumulated agent IDs for next query against service
+	typedef std::set<LLUUID> ask_queue_t;
+	ask_queue_t sAskQueue;
+
+	// agent IDs that have been requested, but with no reply
+	// maps agent ID to frame time request was made
+	typedef std::map<LLUUID, F64> pending_queue_t;
+	pending_queue_t sPendingQueue;
+
+	// Callbacks to fire when we received a name.
+	// May have multiple callbacks for a single ID, which are
+	// represented as multiple slots bound to the signal.
+	// Avoid copying signals via pointers.
+	typedef std::map<LLUUID, callback_signal_t*> signal_map_t;
+	signal_map_t sSignalMap;
+
+	// names we know about
+	typedef std::map<LLUUID, LLAvatarName> cache_t;
+	cache_t sCache;
+
+	// Send bulk lookup requests a few times a second at most
+	// only need per-frame timing resolution
+	LLFrameTimer sRequestTimer;
+
+	// Periodically clean out expired entries from the cache
+	//LLFrameTimer sEraseExpiredTimer;
+
+	//-----------------------------------------------------------------------
+	// Internal methods
+	//-----------------------------------------------------------------------
+
+	// Handle name response off network.
+	// Optionally skip adding to cache, used when this is a fallback to the
+	// legacy name system.
+	void processName(const LLUUID& agent_id,
+					 const LLAvatarName& av_name,
+					 bool add_to_cache);
+
+	void requestNamesViaCapability();
+
+	// Legacy name system callback
+	void legacyNameCallback(const LLUUID& agent_id,
+		const std::string& full_name,
+		bool is_group);
+
+	void requestNamesViaLegacy();
+
+	// Fill in an LLAvatarName with the legacy name data
+	void buildLegacyName(const std::string& full_name,
+						 LLAvatarName* av_name);
+
+	// Do a single callback to a given slot
+	void fireSignal(const LLUUID& agent_id,
+					const callback_slot_t& slot,
+					const LLAvatarName& av_name);
+	
+	// Is a request in-flight over the network?
+	bool isRequestPending(const LLUUID& agent_id);
+
+	// Erase expired names from cache
+	void eraseExpired();
+
+	bool expirationFromCacheControl(LLSD headers, F64 *expires);
+}
+
+/* Sample response:
+<?xml version="1.0"?>
+<llsd>
+  <map>
+    <key>agents</key>
+    <array>
+      <map>
+        <key>display_name_next_update</key>
+        <date>2010-04-16T21:34:02+00:00Z</date>
+        <key>display_name_expires</key>
+        <date>2010-04-16T21:32:26.142178+00:00Z</date>
+        <key>display_name</key>
+        <string>MickBot390 LLQABot</string>
+        <key>sl_id</key>
+        <string>mickbot390.llqabot</string>
+        <key>id</key>
+        <string>0012809d-7d2d-4c24-9609-af1230a37715</string>
+        <key>is_display_name_default</key>
+        <boolean>false</boolean>
+      </map>
+      <map>
+        <key>display_name_next_update</key>
+        <date>2010-04-16T21:34:02+00:00Z</date>
+        <key>display_name_expires</key>
+        <date>2010-04-16T21:32:26.142178+00:00Z</date>
+        <key>display_name</key>
+        <string>Bjork Gudmundsdottir</string>
+        <key>sl_id</key>
+        <string>sardonyx.linden</string>
+        <key>id</key>
+        <string>3941037e-78ab-45f0-b421-bd6e77c1804d</string>
+        <key>is_display_name_default</key>
+        <boolean>true</boolean>
+      </map>
+    </array>
+  </map>
+</llsd>
+*/
+
+class LLAvatarNameResponder : public LLHTTPClient::Responder
+{
+private:
+	// need to store agent ids that are part of this request in case of
+	// an error, so we can flag them as unavailable
+	std::vector<LLUUID> mAgentIDs;
+
+	// Need the headers to look up Expires: and Retry-After:
+	LLSD mHeaders;
+	
+public:
+	LLAvatarNameResponder(const std::vector<LLUUID>& agent_ids)
+	:	mAgentIDs(agent_ids),
+		mHeaders()
+	{ }
+	
+	/*virtual*/ void completedHeader(U32 status, const std::string& reason, 
+		const LLSD& headers)
+	{
+		mHeaders = headers;
+	}
+
+	/*virtual*/ void result(const LLSD& content)
+	{
+		// Pull expiration out of headers if available
+		F64 expires = LLAvatarNameCache::nameExpirationFromHeaders(mHeaders);
+
+		LLSD agents = content["agents"];
+		LLSD::array_const_iterator it = agents.beginArray();
+		for ( ; it != agents.endArray(); ++it)
+		{
+			const LLSD& row = *it;
+			LLUUID agent_id = row["id"].asUUID();
+
+			LLAvatarName av_name;
+			av_name.fromLLSD(row);
+
+			// Use expiration time from header
+			av_name.mExpires = expires;
+
+			// Some avatars don't have explicit display names set
+			if (av_name.mDisplayName.empty())
+			{
+				av_name.mDisplayName = av_name.mUsername;
+			}
+
+			// cache it and fire signals
+			LLAvatarNameCache::processName(agent_id, av_name, true);
+		}
+
+		// Same logic as error response case
+		LLSD unresolved_agents = content["bad_ids"];
+		if (unresolved_agents.size() > 0)
+		{
+			const std::string DUMMY_NAME("\?\?\?");
+			LLAvatarName av_name;
+			av_name.mUsername = DUMMY_NAME;
+			av_name.mDisplayName = DUMMY_NAME;
+			av_name.mIsDisplayNameDefault = false;
+			av_name.mIsDummy = true;
+			av_name.mExpires = expires;
+
+			it = unresolved_agents.beginArray();
+			for ( ; it != unresolved_agents.endArray(); ++it)
+			{
+				const LLUUID& agent_id = *it;
+				// cache it and fire signals
+				LLAvatarNameCache::processName(agent_id, av_name, true);
+			}
+		}
+	}
+
+	/*virtual*/ void error(U32 status, const std::string& reason)
+	{
+		// We're going to construct a dummy record and cache it for a while,
+		// either briefly for a 503 Service Unavailable, or longer for other
+		// errors.
+		F64 retry_timestamp = errorRetryTimestamp(status);
+
+		// *NOTE: "??" starts trigraphs in C/C++, escape the question marks.
+		const std::string DUMMY_NAME("\?\?\?");
+		LLAvatarName av_name;
+		av_name.mUsername = DUMMY_NAME;
+		av_name.mDisplayName = DUMMY_NAME;
+		av_name.mIsDisplayNameDefault = false;
+		av_name.mIsDummy = true;
+		av_name.mExpires = retry_timestamp;
+
+		// Add dummy records for all agent IDs in this request
+		std::vector<LLUUID>::const_iterator it = mAgentIDs.begin();
+		for ( ; it != mAgentIDs.end(); ++it)
+		{
+			const LLUUID& agent_id = *it;
+			// cache it and fire signals
+			LLAvatarNameCache::processName(agent_id, av_name, true);
+		}
+	}
+
+	// Return time to retry a request that generated an error, based on
+	// error type and headers.  Return value is seconds-since-epoch.
+	F64 errorRetryTimestamp(S32 status)
+	{
+		F64 now = LLFrameTimer::getTotalSeconds();
+
+		// Retry-After takes priority
+		LLSD retry_after = mHeaders["retry-after"];
+		if (retry_after.isDefined())
+		{
+			// We only support the delta-seconds type
+			S32 delta_seconds = retry_after.asInteger();
+			if (delta_seconds > 0)
+			{
+				// ...valid delta-seconds
+				return now + F64(delta_seconds);
+			}
+		}
+
+		// If no Retry-After, look for Cache-Control max-age
+		F64 expires = 0.0;
+		if (LLAvatarNameCache::expirationFromCacheControl(mHeaders, &expires))
+		{
+			return expires;
+		}
+
+		// No information in header, make a guess
+		if (status == 503)
+		{
+			// ...service unavailable, retry soon
+			const F64 SERVICE_UNAVAILABLE_DELAY = 600.0; // 10 min
+			return now + SERVICE_UNAVAILABLE_DELAY;
+		}
+		else
+		{
+			// ...other unexpected error
+			const F64 DEFAULT_DELAY = 3600.0; // 1 hour
+			return now + DEFAULT_DELAY;
+		}
+	}
+};
+
+void LLAvatarNameCache::processName(const LLUUID& agent_id,
+									const LLAvatarName& av_name,
+									bool add_to_cache)
+{
+	if (add_to_cache)
+	{
+		sCache[agent_id] = av_name;
+	}
+
+	sPendingQueue.erase(agent_id);
+
+	// signal everyone waiting on this name
+	signal_map_t::iterator sig_it =	sSignalMap.find(agent_id);
+	if (sig_it != sSignalMap.end())
+	{
+		callback_signal_t* signal = sig_it->second;