diff options
193 files changed, 17082 insertions, 11901 deletions
diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp index e5068f1bc5..1c664e093b 100644 --- a/indra/llcommon/llassettype.cpp +++ b/indra/llcommon/llassettype.cpp @@ -45,9 +45,9 @@ struct AssetEntry : public LLDictionaryEntry  	AssetEntry(const char *desc_name,  			   const char *type_name, 	// 8 character limit!  			   const char *human_name, 	// for decoding to human readable form; put any and as many printable characters you want in each one -			   BOOL can_link, 			// can you create a link to this type? -			   BOOL can_fetch, 			// can you fetch this asset by ID? -			   BOOL can_know) 			// can you see this asset's ID? +			   bool can_link, 			// can you create a link to this type? +			   bool can_fetch, 			// can you fetch this asset by ID? +			   bool can_know) 			// can you see this asset's ID?  		:  		LLDictionaryEntry(desc_name),  		mTypeName(type_name), @@ -61,9 +61,9 @@ struct AssetEntry : public LLDictionaryEntry  	const char *mTypeName;  	const char *mHumanName; -	BOOL mCanLink; -	BOOL mCanFetch; -	BOOL mCanKnow; +	bool mCanLink; +	bool mCanFetch; +	bool mCanKnow;  };  class LLAssetDictionary : public LLSingleton<LLAssetDictionary>, @@ -77,30 +77,30 @@ LLAssetDictionary::LLAssetDictionary()  {  	//       												   DESCRIPTION			TYPE NAME	HUMAN NAME			CAN LINK?   CAN FETCH?  CAN KNOW?	  	//      												  |--------------------|-----------|-------------------|-----------|-----------|---------| -	addEntry(LLAssetType::AT_TEXTURE, 			new AssetEntry("TEXTURE",			"texture",	"texture",			FALSE,		FALSE,		TRUE)); -	addEntry(LLAssetType::AT_SOUND, 			new AssetEntry("SOUND",				"sound",	"sound",			FALSE,		TRUE,		TRUE)); -	addEntry(LLAssetType::AT_CALLINGCARD, 		new AssetEntry("CALLINGCARD",		"callcard",	"calling card",		FALSE,		FALSE,		FALSE)); -	addEntry(LLAssetType::AT_LANDMARK, 			new AssetEntry("LANDMARK",			"landmark",	"landmark",			FALSE,		TRUE,		TRUE)); -	addEntry(LLAssetType::AT_SCRIPT, 			new AssetEntry("SCRIPT",			"script",	"legacy script",	FALSE,		FALSE,		FALSE)); -	addEntry(LLAssetType::AT_CLOTHING, 			new AssetEntry("CLOTHING",			"clothing",	"clothing",			TRUE,		TRUE,		TRUE)); -	addEntry(LLAssetType::AT_OBJECT, 			new AssetEntry("OBJECT",			"object",	"object",			TRUE,		FALSE,		FALSE)); -	addEntry(LLAssetType::AT_NOTECARD, 			new AssetEntry("NOTECARD",			"notecard",	"note card",		FALSE,		FALSE,		TRUE)); -	addEntry(LLAssetType::AT_CATEGORY, 			new AssetEntry("CATEGORY",			"category",	"folder",			TRUE,		FALSE,		FALSE)); -	addEntry(LLAssetType::AT_LSL_TEXT, 			new AssetEntry("LSL_TEXT",			"lsltext",	"lsl2 script",		FALSE,		FALSE,		FALSE)); -	addEntry(LLAssetType::AT_LSL_BYTECODE, 		new AssetEntry("LSL_BYTECODE",		"lslbyte",	"lsl bytecode",		FALSE,		FALSE,		FALSE)); -	addEntry(LLAssetType::AT_TEXTURE_TGA, 		new AssetEntry("TEXTURE_TGA",		"txtr_tga",	"tga texture",		FALSE,		FALSE,		FALSE)); -	addEntry(LLAssetType::AT_BODYPART, 			new AssetEntry("BODYPART",			"bodypart",	"body part",		TRUE,		TRUE,		TRUE)); -	addEntry(LLAssetType::AT_SOUND_WAV, 		new AssetEntry("SOUND_WAV",			"snd_wav",	"sound",			FALSE,		FALSE,		FALSE)); -	addEntry(LLAssetType::AT_IMAGE_TGA, 		new AssetEntry("IMAGE_TGA",			"img_tga",	"targa image",		FALSE,		FALSE,		FALSE)); -	addEntry(LLAssetType::AT_IMAGE_JPEG, 		new AssetEntry("IMAGE_JPEG",		"jpeg",		"jpeg image",		FALSE,		FALSE,		FALSE)); -	addEntry(LLAssetType::AT_ANIMATION, 		new AssetEntry("ANIMATION",			"animatn",	"animation",		FALSE,		TRUE,		TRUE)); -	addEntry(LLAssetType::AT_GESTURE, 			new AssetEntry("GESTURE",			"gesture",	"gesture",			TRUE,		TRUE,		TRUE)); -	addEntry(LLAssetType::AT_SIMSTATE, 			new AssetEntry("SIMSTATE",			"simstate",	"simstate",			FALSE,		FALSE,		FALSE)); +	addEntry(LLAssetType::AT_TEXTURE, 			new AssetEntry("TEXTURE",			"texture",	"texture",			false,		false,		true)); +	addEntry(LLAssetType::AT_SOUND, 			new AssetEntry("SOUND",				"sound",	"sound",			false,		true,		true)); +	addEntry(LLAssetType::AT_CALLINGCARD, 		new AssetEntry("CALLINGCARD",		"callcard",	"calling card",		false,		false,		false)); +	addEntry(LLAssetType::AT_LANDMARK, 			new AssetEntry("LANDMARK",			"landmark",	"landmark",			false,		true,		true)); +	addEntry(LLAssetType::AT_SCRIPT, 			new AssetEntry("SCRIPT",			"script",	"legacy script",	false,		false,		false)); +	addEntry(LLAssetType::AT_CLOTHING, 			new AssetEntry("CLOTHING",			"clothing",	"clothing",			true,		true,		true)); +	addEntry(LLAssetType::AT_OBJECT, 			new AssetEntry("OBJECT",			"object",	"object",			true,		false,		false)); +	addEntry(LLAssetType::AT_NOTECARD, 			new AssetEntry("NOTECARD",			"notecard",	"note card",		false,		false,		true)); +	addEntry(LLAssetType::AT_CATEGORY, 			new AssetEntry("CATEGORY",			"category",	"folder",			true,		false,		false)); +	addEntry(LLAssetType::AT_LSL_TEXT, 			new AssetEntry("LSL_TEXT",			"lsltext",	"lsl2 script",		false,		false,		false)); +	addEntry(LLAssetType::AT_LSL_BYTECODE, 		new AssetEntry("LSL_BYTECODE",		"lslbyte",	"lsl bytecode",		false,		false,		false)); +	addEntry(LLAssetType::AT_TEXTURE_TGA, 		new AssetEntry("TEXTURE_TGA",		"txtr_tga",	"tga texture",		false,		false,		false)); +	addEntry(LLAssetType::AT_BODYPART, 			new AssetEntry("BODYPART",			"bodypart",	"body part",		true,		true,		true)); +	addEntry(LLAssetType::AT_SOUND_WAV, 		new AssetEntry("SOUND_WAV",			"snd_wav",	"sound",			false,		false,		false)); +	addEntry(LLAssetType::AT_IMAGE_TGA, 		new AssetEntry("IMAGE_TGA",			"img_tga",	"targa image",		false,		false,		false)); +	addEntry(LLAssetType::AT_IMAGE_JPEG, 		new AssetEntry("IMAGE_JPEG",		"jpeg",		"jpeg image",		false,		false,		false)); +	addEntry(LLAssetType::AT_ANIMATION, 		new AssetEntry("ANIMATION",			"animatn",	"animation",		false,		true,		true)); +	addEntry(LLAssetType::AT_GESTURE, 			new AssetEntry("GESTURE",			"gesture",	"gesture",			true,		true,		true)); +	addEntry(LLAssetType::AT_SIMSTATE, 			new AssetEntry("SIMSTATE",			"simstate",	"simstate",			false,		false,		false)); -	addEntry(LLAssetType::AT_LINK, 				new AssetEntry("LINK",				"link",		"sym link",			FALSE,		FALSE,		TRUE)); -	addEntry(LLAssetType::AT_LINK_FOLDER, 		new AssetEntry("FOLDER_LINK",		"link_f", 	"sym folder link",	FALSE,		FALSE,		TRUE)); +	addEntry(LLAssetType::AT_LINK, 				new AssetEntry("LINK",				"link",		"sym link",			false,		false,		true)); +	addEntry(LLAssetType::AT_LINK_FOLDER, 		new AssetEntry("FOLDER_LINK",		"link_f", 	"sym folder link",	false,		false,		true)); -	addEntry(LLAssetType::AT_NONE, 				new AssetEntry("NONE",				"-1",		NULL,		  		FALSE,		FALSE,		FALSE)); +	addEntry(LLAssetType::AT_NONE, 				new AssetEntry("NONE",				"-1",		NULL,		  		false,		false,		false));  };  // static @@ -202,7 +202,7 @@ LLAssetType::EType LLAssetType::lookupHumanReadable(const std::string& readable_  }  // static -BOOL LLAssetType::lookupCanLink(EType asset_type) +bool LLAssetType::lookupCanLink(EType asset_type)  {  	const LLAssetDictionary *dict = LLAssetDictionary::getInstance();  	const AssetEntry *entry = dict->lookup(asset_type); @@ -210,18 +210,18 @@ BOOL LLAssetType::lookupCanLink(EType asset_type)  	{  		return entry->mCanLink;  	} -	return FALSE; +	return false;  }  // static  // Not adding this to dictionary since we probably will only have these two types -BOOL LLAssetType::lookupIsLinkType(EType asset_type) +bool LLAssetType::lookupIsLinkType(EType asset_type)  {  	if (asset_type == AT_LINK || asset_type == AT_LINK_FOLDER)  	{ -		return TRUE; +		return true;  	} -	return FALSE; +	return false;  }  // static @@ -233,7 +233,7 @@ const std::string &LLAssetType::badLookup()  }  // static -BOOL LLAssetType::lookupIsAssetFetchByIDAllowed(EType asset_type) +bool LLAssetType::lookupIsAssetFetchByIDAllowed(EType asset_type)  {  	const LLAssetDictionary *dict = LLAssetDictionary::getInstance();  	const AssetEntry *entry = dict->lookup(asset_type); @@ -241,11 +241,11 @@ BOOL LLAssetType::lookupIsAssetFetchByIDAllowed(EType asset_type)  	{  		return entry->mCanFetch;  	} -	return FALSE; +	return false;  }  // static -BOOL LLAssetType::lookupIsAssetIDKnowable(EType asset_type) +bool LLAssetType::lookupIsAssetIDKnowable(EType asset_type)  {  	const LLAssetDictionary *dict = LLAssetDictionary::getInstance();  	const AssetEntry *entry = dict->lookup(asset_type); @@ -253,5 +253,5 @@ BOOL LLAssetType::lookupIsAssetIDKnowable(EType asset_type)  	{  		return entry->mCanKnow;  	} -	return FALSE; +	return false;  } diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h index 4440e1bac3..2c2dc27aaa 100644 --- a/indra/llcommon/llassettype.h +++ b/indra/llcommon/llassettype.h @@ -143,11 +143,11 @@ public:  	static EType 				getType(const std::string& desc_name);  	static const std::string&	getDesc(EType asset_type); -	static BOOL 				lookupCanLink(EType asset_type); -	static BOOL 				lookupIsLinkType(EType asset_type); +	static bool 				lookupCanLink(EType asset_type); +	static bool 				lookupIsLinkType(EType asset_type); -	static BOOL 				lookupIsAssetFetchByIDAllowed(EType asset_type); // the asset allows direct download -	static BOOL 				lookupIsAssetIDKnowable(EType asset_type); // asset data can be known by the viewer +	static bool 				lookupIsAssetFetchByIDAllowed(EType asset_type); // the asset allows direct download +	static bool 				lookupIsAssetIDKnowable(EType asset_type); // asset data can be known by the viewer  	static const std::string&	badLookup(); // error string when a lookup fails diff --git a/indra/llcommon/lluri.cpp b/indra/llcommon/lluri.cpp index 9d4f3a98f0..0e8f3f0f73 100644 --- a/indra/llcommon/lluri.cpp +++ b/indra/llcommon/lluri.cpp @@ -231,7 +231,8 @@ static BOOL isDefault(const std::string& scheme, U16 port)  void LLURI::parseAuthorityAndPathUsingOpaque()  {  	if (mScheme == "http" || mScheme == "https" || -		mScheme == "ftp" || mScheme == "secondlife" ) +		mScheme == "ftp" || mScheme == "secondlife" ||  +		mScheme == "x-grid-location-info")  	{  		if (mEscapedOpaque.substr(0,2) != "//")  		{ diff --git a/indra/llcommon/lluuid.cpp b/indra/llcommon/lluuid.cpp index bcbae06ec5..583c1e589b 100644 --- a/indra/llcommon/lluuid.cpp +++ b/indra/llcommon/lluuid.cpp @@ -33,9 +33,12 @@  // We can't use WIN32_LEAN_AND_MEAN here, needs lots of includes.  #if LL_WINDOWS -#	undef WIN32_LEAN_AND_MEAN -#	include <winsock2.h> -#	include <windows.h> +#undef WIN32_LEAN_AND_MEAN +#include <winsock2.h> +#include <windows.h> +// ugh, this is ugly.  We need to straighten out our linking for this library +#pragma comment(lib, "IPHLPAPI.lib") +#include <iphlpapi.h>  #endif  #include "lldefs.h" @@ -452,67 +455,102 @@ static void get_random_bytes(void *buf, int nbytes)  	return;	  } -#if LL_WINDOWS -typedef struct _ASTAT_ -{ -	ADAPTER_STATUS adapt; -	NAME_BUFFER    NameBuff [30]; -}ASTAT, * PASTAT; +#if	LL_WINDOWS +// Code	copied from	http://msdn.microsoft.com/en-us/library/aa365939(VS.85).aspx +// This	code grabs the first hardware	address, rather	than the first interface. +// Using a VPN can cause the first returned	interface	to be	changed. + +const	S32	MAC_ADDRESS_BYTES=6; +  // static -S32 LLUUID::getNodeID(unsigned char * node_id) -{ -	  ASTAT Adapter; -      NCB Ncb; -      UCHAR uRetCode; -      LANA_ENUM   lenum; -      int      i; -	  int retval = 0; - -      memset( &Ncb, 0, sizeof(Ncb) ); -      Ncb.ncb_command = NCBENUM; -      Ncb.ncb_buffer = (UCHAR *)&lenum; -      Ncb.ncb_length = sizeof(lenum); -      uRetCode = Netbios( &Ncb ); - //     printf( "The NCBENUM return code is: 0x%x \n", uRetCode ); - -      for(i=0; i < lenum.length ;i++) -      { -          memset( &Ncb, 0, sizeof(Ncb) ); -          Ncb.ncb_command = NCBRESET; -          Ncb.ncb_lana_num = lenum.lana[i]; - -          uRetCode = Netbios( &Ncb ); - //         printf( "The NCBRESET on LANA %d return code is: 0x%x \n", - //                 lenum.lana[i], uRetCode ); - -          memset( &Ncb, 0, sizeof (Ncb) ); -          Ncb.ncb_command = NCBASTAT; -          Ncb.ncb_lana_num = lenum.lana[i]; - -          strcpy( (char *)Ncb.ncb_callname,  "*              " );		/* Flawfinder: ignore */ -          Ncb.ncb_buffer = (unsigned char *)&Adapter; -          Ncb.ncb_length = sizeof(Adapter); - -          uRetCode = Netbios( &Ncb ); -//          printf( "The NCBASTAT on LANA %d return code is: 0x%x \n", -//                 lenum.lana[i], uRetCode ); -          if ( uRetCode == 0 ) -          { -//            printf( "The Ethernet Number on LANA %d is: %02x%02x%02x%02x%02x%02x\n", -//	 			  lenum.lana[i], -//                  Adapter.adapt.adapter_address[0], -//                  Adapter.adapt.adapter_address[1], -//                  Adapter.adapt.adapter_address[2], -//                  Adapter.adapt.adapter_address[3], -//                  Adapter.adapt.adapter_address[4], -//                  Adapter.adapt.adapter_address[5] ); -			memcpy(node_id,Adapter.adapt.adapter_address,6);		/* Flawfinder: ignore */ -			retval = 1; - -          } -	  } -	return retval; +S32	LLUUID::getNodeID(unsigned char	*node_id) +{ + +	// Declare and initialize variables. +	DWORD	dwSize = 0; +	DWORD	dwRetVal = 0; +	int	i; + +/* variables used	for	GetIfTable and GetIfEntry	*/ +	MIB_IFTABLE	*pIfTable; +	MIB_IFROW	*pIfRow; + +	// Allocate	memory for our pointers. +	pIfTable = (MIB_IFTABLE	*) malloc(sizeof (MIB_IFTABLE)); +	if (pIfTable ==	NULL)	 +	{ +			printf("Error allocating memory needed to call GetIfTable\n"); +			return 0; +	} + +	// Before	calling	GetIfEntry,	we call	GetIfTable to	make +	// sure	there	are	entries	to get and retrieve	the	interface	index. + +	// Make	an initial call	to GetIfTable	to get the +	// necessary size	into dwSize +	if (GetIfTable(pIfTable, &dwSize,	0) ==	ERROR_INSUFFICIENT_BUFFER) { +			free(pIfTable); +			pIfTable = (MIB_IFTABLE	*) malloc(dwSize); +			if (pIfTable ==	NULL)	 +			{ +					printf("Error	allocating memory\n"); +					return 0; +			} +	} +	//	Make a second	call to	GetIfTable to	get	the	actual +	// data	we want. +	if ((dwRetVal = GetIfTable(pIfTable, &dwSize,	0))	== NO_ERROR)  +	{ +		if (pIfTable->dwNumEntries > 0)	 +		{ +			pIfRow = (MIB_IFROW	*) malloc(sizeof (MIB_IFROW)); +			if (pIfRow ==	NULL)	 +			{ +					printf("Error allocating memory\n"); +					if (pIfTable != NULL)	 +					{ +						free(pIfTable); +						pIfTable = NULL; +					} +					return 0; +			} + +			int	limit	=	MAC_ADDRESS_BYTES; +			memcpy(node_id,	"\0\0\0\0\0\0",	limit);	// zero	out	array	of bytes	  +			for	(i = 0;	i < (int) pIfTable->dwNumEntries; i++)  +			{ +				pIfRow->dwIndex	= pIfTable->table[i].dwIndex; +				if ((dwRetVal = GetIfEntry(pIfRow)) == NO_ERROR)  +				{ +					switch (pIfRow->dwType)	 +					{ +						case IF_TYPE_ETHERNET_CSMACD: +						case IF_TYPE_IEEE80211:		  +							 limit = min((int) pIfRow->dwPhysAddrLen, limit); +							 if	(pIfRow->dwPhysAddrLen == 0) +									 break; +							 memcpy(node_id, (UCHAR *)&pIfRow->bPhysAddr[0], limit);		 //	just incase	the	PhysAddr is	not	the	expected MAC_Address size +							 free(pIfTable); +							 return 1;	//return first hardware	device found.	 +							break; + +						case IF_TYPE_OTHER: +						case IF_TYPE_PPP:										  +						case IF_TYPE_SOFTWARE_LOOPBACK:										  +						case IF_TYPE_ISO88025_TOKENRING:										 +						case IF_TYPE_IEEE1394:																		 +						case IF_TYPE_ATM:										  +						case IF_TYPE_TUNNEL:										 +								default: +									break; +					} +				} +			} +		} +	} +	free(pIfTable); +	return 0;  }  #elif LL_DARWIN diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index 024e17a777..91e11b8c0d 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -89,10 +89,6 @@ S32 gCurlMultiCount = 0;  std::vector<LLMutex*> LLCurl::sSSLMutex;  std::string LLCurl::sCAPath;  std::string LLCurl::sCAFile; -// Verify SSL certificates by default (matches libcurl default). The ability -// to alter this flag is only to allow us to suppress verification if it's -// broken for some reason. -bool LLCurl::sSSLVerify = true;  //static  void LLCurl::setCAPath(const std::string& path) @@ -107,18 +103,6 @@ void LLCurl::setCAFile(const std::string& file)  }  //static -void LLCurl::setSSLVerify(bool verify) -{ -	sSSLVerify = verify; -} - -//static -bool LLCurl::getSSLVerify() -{ -	return sSSLVerify; -} - -//static  std::string LLCurl::getVersionString()  {  	return std::string(curl_version()); @@ -481,8 +465,7 @@ void LLCurl::Easy::prepRequest(const std::string& url,  	setErrorBuffer();  	setCA(); -	setopt(CURLOPT_SSL_VERIFYPEER, LLCurl::getSSLVerify()); -	setopt(CURLOPT_SSL_VERIFYHOST, LLCurl::getSSLVerify()? 2 : 0); +	setopt(CURLOPT_SSL_VERIFYPEER, true);  	setopt(CURLOPT_TIMEOUT, CURL_REQUEST_TIMEOUT);  	setoptString(CURLOPT_URL, url); @@ -912,6 +895,15 @@ void LLCurlEasyRequest::setReadCallback(curl_read_callback callback, void* userd  	}  } +void LLCurlEasyRequest::setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata) +{ +	if (mEasy) +	{ +		mEasy->setopt(CURLOPT_SSL_CTX_FUNCTION, (void*)callback); +		mEasy->setopt(CURLOPT_SSL_CTX_DATA, userdata); +	} +} +  void LLCurlEasyRequest::slist_append(const char* str)  {  	if (mEasy) @@ -1061,3 +1053,4 @@ void LLCurl::cleanupClass()  #endif  	curl_global_cleanup();  } + diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h index caf02cccd9..b6a637ae5b 100644 --- a/indra/llmessage/llcurl.h +++ b/indra/llmessage/llcurl.h @@ -158,16 +158,6 @@ public:  	static const std::string& getCAPath() { return sCAPath; }  	/** -	 * @ brief Set flag controlling whether to verify HTTPS certs. -	 */ -	static void setSSLVerify(bool verify); - -	/** -	 * @ brief Get flag controlling whether to verify HTTPS certs. -	 */ -	static bool getSSLVerify(); - -	/**  	 * @ brief Initialize LLCurl class  	 */  	static void initClass(); @@ -192,7 +182,6 @@ public:  private:  	static std::string sCAPath;  	static std::string sCAFile; -	static bool sSSLVerify;  };  namespace boost @@ -240,6 +229,7 @@ public:  	void setHeaderCallback(curl_header_callback callback, void* userdata);  	void setWriteCallback(curl_write_callback callback, void* userdata);  	void setReadCallback(curl_read_callback callback, void* userdata); +	void setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata);  	void slist_append(const char* str);  	void sendRequest(const std::string& url);  	void requestComplete(); diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index dd56e18caf..345b76d1a1 100644 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -31,7 +31,7 @@   */  #include "linden_common.h" - +#include <openssl/x509_vfy.h>  #include "llhttpclient.h"  #include "llassetstorage.h" @@ -46,7 +46,10 @@  #include "message.h"  #include <curl/curl.h> +  const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f; +LLURLRequest::SSLCertVerifyCallback LLHTTPClient::mCertVerifyCallback = NULL; +  ////////////////////////////////////////////////////////////////////////////  // Responder class moved to LLCurl @@ -206,13 +209,19 @@ namespace  	LLPumpIO* theClientPump = NULL;  } +void LLHTTPClient::setCertVerifyCallback(LLURLRequest::SSLCertVerifyCallback callback) +{ +	LLHTTPClient::mCertVerifyCallback = callback; +} +  static void request(  	const std::string& url,  	LLURLRequest::ERequestAction method,  	Injector* body_injector,  	LLCurl::ResponderPtr responder,  	const F32 timeout = HTTP_REQUEST_EXPIRY_SECS, -	const LLSD& headers = LLSD()) +	const LLSD& headers = LLSD() +    )  {  	if (!LLHTTPClient::hasPump())  	{ @@ -222,7 +231,7 @@ static void request(  	LLPumpIO::chain_t chain;  	LLURLRequest* req = new LLURLRequest(method, url); -	req->checkRootCertificate(LLCurl::getSSLVerify()); +	req->setSSLVerifyCallback(LLHTTPClient::getCertVerifyCallback(), (void *)req);  	lldebugs << LLURLRequest::actionAsVerb(method) << " " << url << " " @@ -417,7 +426,6 @@ static LLSD blocking_request(  	std::string body_str;  	// other request method checks root cert first, we skip? -	//req->checkRootCertificate(true);  	// * Set curl handle options  	curl_easy_setopt(curlp, CURLOPT_NOSIGNAL, 1);	// don't use SIGALRM for timeouts diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h index 3d0646e5fe..8afbc9e0fc 100644 --- a/indra/llmessage/llhttpclient.h +++ b/indra/llmessage/llhttpclient.h @@ -40,7 +40,8 @@  #include <string>  #include <boost/intrusive_ptr.hpp> - +#include <openssl/x509_vfy.h> +#include "llurlrequest.h"  #include "llassettype.h"  #include "llcurl.h"  #include "lliopipe.h" @@ -61,6 +62,7 @@ public:  	typedef LLCurl::Responder Responder;  	typedef LLCurl::ResponderPtr ResponderPtr; +	  	/** @name non-blocking API */  	//@{  	static void head( @@ -155,7 +157,12 @@ public:  	static void setPump(LLPumpIO& pump);  		///< must be called before any of the above calls are made  	static bool hasPump(); -		///< for testing + +	static void setCertVerifyCallback(LLURLRequest::SSLCertVerifyCallback callback); +	static  LLURLRequest::SSLCertVerifyCallback getCertVerifyCallback() { return mCertVerifyCallback; } + +protected: +	static LLURLRequest::SSLCertVerifyCallback mCertVerifyCallback;  };  #endif // LL_LLHTTPCLIENT_H diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index 4e7ceff984..1e76d10828 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -36,7 +36,8 @@  #include "llurlrequest.h"  #include <algorithm> - +#include <openssl/x509_vfy.h> +#include <openssl/ssl.h>  #include "llcurl.h"  #include "llioutil.h"  #include "llmemtype.h" @@ -56,6 +57,8 @@ const std::string CONTEXT_TRANSFERED_BYTES("transfered_bytes");  static size_t headerCallback(void* data, size_t size, size_t nmemb, void* user); + +  /**   * class LLURLRequestDetail   */ @@ -72,6 +75,7 @@ public:  	U32 mBodyLimit;  	S32 mByteAccumulator;  	bool mIsBodyLimitSet; +	LLURLRequest::SSLCertVerifyCallback mSSLVerifyCallback;  };  LLURLRequestDetail::LLURLRequestDetail() : @@ -80,7 +84,8 @@ LLURLRequestDetail::LLURLRequestDetail() :  	mLastRead(NULL),  	mBodyLimit(0),  	mByteAccumulator(0), -	mIsBodyLimitSet(false) +	mIsBodyLimitSet(false), +    mSSLVerifyCallback(NULL)  {  	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);  	mCurlRequest = new LLCurlEasyRequest(); @@ -94,6 +99,36 @@ LLURLRequestDetail::~LLURLRequestDetail()  	mLastRead = NULL;  } +void LLURLRequest::setSSLVerifyCallback(SSLCertVerifyCallback callback, void *param) +{ +	mDetail->mSSLVerifyCallback = callback; +	mDetail->mCurlRequest->setSSLCtxCallback(LLURLRequest::_sslCtxCallback, (void *)this); +	mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, true); +	mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, 2);	 +} + + +// _sslCtxFunction +// Callback function called when an SSL Context is created via CURL +// used to configure the context for custom cert validation + +CURLcode LLURLRequest::_sslCtxCallback(CURL * curl, void *sslctx, void *param) +{	 +	LLURLRequest *req = (LLURLRequest *)param; +	if(req == NULL || req->mDetail->mSSLVerifyCallback == NULL) +	{ +		SSL_CTX_set_cert_verify_callback((SSL_CTX *)sslctx, NULL, NULL); +		return CURLE_OK; +	} +	SSL_CTX * ctx = (SSL_CTX *) sslctx; +	// disable any default verification for server certs +	SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); +	// set the verification callback. +	SSL_CTX_set_cert_verify_callback(ctx, req->mDetail->mSSLVerifyCallback, (void *)req); +	// the calls are void +	return CURLE_OK; +	 +}  /**   * class LLURLRequest @@ -148,6 +183,11 @@ void LLURLRequest::setURL(const std::string& url)  	mDetail->mURL = url;  } +std::string LLURLRequest::getURL() const +{ +	return mDetail->mURL; +} +  void LLURLRequest::addHeader(const char* header)  {  	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); @@ -160,13 +200,6 @@ void LLURLRequest::setBodyLimit(U32 size)  	mDetail->mIsBodyLimitSet = true;  } -void LLURLRequest::checkRootCertificate(bool check) -{ -	mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, (check? TRUE : FALSE)); -	mDetail->mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, (check? 2 : 0)); -	mDetail->mCurlRequest->setoptString(CURLOPT_ENCODING, ""); -} -  void LLURLRequest::setCallback(LLURLRequestComplete* callback)  {  	LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST); diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h index cb3c466440..69fd22e592 100644 --- a/indra/llmessage/llurlrequest.h +++ b/indra/llmessage/llurlrequest.h @@ -44,6 +44,8 @@  #include "lliopipe.h"  #include "llchainio.h"  #include "llerror.h" +#include <openssl/x509_vfy.h> +#include "llcurl.h"  extern const std::string CONTEXT_REQUEST; @@ -72,6 +74,8 @@ class LLURLRequest : public LLIOPipe  {  	LOG_CLASS(LLURLRequest);  public: + +	typedef int (* SSLCertVerifyCallback)(X509_STORE_CTX *ctx, void *param);  	/**   	 * @brief This enumeration is for specifying the type of request.  	 */ @@ -125,7 +129,7 @@ public:  	 *   	 */  	void setURL(const std::string& url); - +	std::string getURL() const;  	/**   	 * @brief Add a header to the http post.  	 * @@ -143,8 +147,9 @@ public:  	 * Set whether request will check that remote server  	 * certificates are signed by a known root CA when using HTTPS.  	 */ -	void checkRootCertificate(bool check); +	void setSSLVerifyCallback(SSLCertVerifyCallback callback, void * param); +	  	/**  	 * @brief Return at most size bytes of body.  	 * @@ -189,6 +194,7 @@ public:  	 * @brief Give this pipe a chance to handle a generated error  	 */  	virtual EStatus handleError(EStatus status, LLPumpIO* pump); +  protected:  	/**  @@ -217,6 +223,8 @@ protected:  	 S32 mRequestTransferedBytes;  	 S32 mResponseTransferedBytes; +	static CURLcode _sslCtxCallback(CURL * curl, void *sslctx, void *param); +	  private:  	/**   	 * @brief Initialize the object. Called during construction. @@ -364,62 +372,6 @@ protected:  }; -/**  - * @class LLURLRequestClientFactory - * @brief Template class to build url request based client chains  - * - * This class eases construction of a basic sd rpc client. Here is an - * example of it's use: - * <code> - *  class LLUsefulService : public LLService { ... }<br> - *  LLService::registerCreator(<br> - *    "useful",<br> - *    LLService::creator_t(new LLURLRequestClientFactory<LLUsefulService>))<br> - * </code> - * - * This class should work, but I never got around to using/testing it. - * - */ -#if 0 -template<class Client> -class LLURLRequestClientFactory : public LLChainIOFactory -{ -public: -	LLURLRequestClientFactory(LLURLRequest::ERequestAction action) {} -	LLURLRequestClientFactory( -		LLURLRequest::ERequestAction action, -		const std::string& fixed_url) : -		mAction(action), -		mURL(fixed_url) -	{ -	} -	virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const -	{ -		lldebugs << "LLURLRequestClientFactory::build" << llendl; -		LLIOPipe::ptr_t service(new Client); -		chain.push_back(service); -		LLURLRequest* http(new LLURLRequest(mAction)); -		LLIOPipe::ptr_t http_pipe(http); -		// *FIX: how do we know the content type? -		//http->addHeader("Content-Type: text/llsd"); -		if(mURL.empty()) -		{ -			chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http))); -		} -		else -		{ -			http->setURL(mURL); -		} -		chain.push_back(http_pipe); -		chain.push_back(service); -		return true; -	} - -protected: -	LLURLRequest::ERequestAction mAction; -	std::string mURL; -}; -#endif  /**   * External constants diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index 1d4dc35cee..33c6a8b6ac 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -824,7 +824,7 @@ void LLButton::draw()  			x = text_right;  			break;  		case LLFontGL::HCENTER: -			x = getRect().getWidth() / 2; +			x = text_left + (text_width / 2);  			break;  		case LLFontGL::LEFT:  		default: diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index 3a8efadaa4..cc107c972d 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -495,7 +495,6 @@ void LLComboBox::createLineEditor(const LLComboBox::Params& p)  		params.max_length_bytes(mMaxChars);  		params.commit_callback.function(boost::bind(&LLComboBox::onTextCommit, this, _2));  		params.keystroke_callback(boost::bind(&LLComboBox::onTextEntry, this, _1)); -		params.handle_edit_keys_directly(true);  		params.commit_on_focus_lost(false);  		params.follows.flags(FOLLOWS_ALL);  		params.label(mLabel); diff --git a/indra/llui/lleditmenuhandler.cpp b/indra/llui/lleditmenuhandler.cpp index 821afae8fd..245bce76f5 100644 --- a/indra/llui/lleditmenuhandler.cpp +++ b/indra/llui/lleditmenuhandler.cpp @@ -37,3 +37,10 @@  /* static */  LLEditMenuHandler* LLEditMenuHandler::gEditMenuHandler = NULL; +LLEditMenuHandler::~LLEditMenuHandler() +{ +	if (gEditMenuHandler == this) +	{ +		gEditMenuHandler = NULL; +	} +} diff --git a/indra/llui/lleditmenuhandler.h b/indra/llui/lleditmenuhandler.h index 1de9c56afb..d72283cd99 100644 --- a/indra/llui/lleditmenuhandler.h +++ b/indra/llui/lleditmenuhandler.h @@ -38,7 +38,7 @@ class LLEditMenuHandler  {  public:  	// this is needed even though this is just an interface class. -	virtual ~LLEditMenuHandler() {}; +	virtual ~LLEditMenuHandler();  	virtual void	undo() {};  	virtual BOOL	canUndo() const { return FALSE; } diff --git a/indra/llui/llflatlistview.cpp b/indra/llui/llflatlistview.cpp index d8084fd9aa..35f5a6bbb9 100644 --- a/indra/llui/llflatlistview.cpp +++ b/indra/llui/llflatlistview.cpp @@ -558,14 +558,6 @@ BOOL LLFlatListView::handleKeyHere(KEY key, MASK mask)  			}  			break;  		} -		case 'A': -		{ -			if(MASK_CONTROL & mask) -			{ -				handled = (BOOL)selectAll(); -			} -			break; -		}  		default:  			break;  	} @@ -790,10 +782,15 @@ bool LLFlatListView::selectNextItemPair(bool is_up_direction, bool reset_selecti  	return false;  } -bool LLFlatListView::selectAll() +BOOL LLFlatListView::canSelectAll() const +{ +	return !mItemPairs.empty() && mAllowSelection && mMultipleSelection; +} + +void LLFlatListView::selectAll()  {  	if (!mAllowSelection || !mMultipleSelection) -		return false; +		return;  	mSelectedItemPairs.clear(); @@ -813,8 +810,6 @@ bool LLFlatListView::selectAll()  	// Stretch selected item rect to ensure it won't be clipped  	mSelectedItemsBorder->setRect(getLastSelectedItemRect().stretch(-1)); - -	return true;  }  bool LLFlatListView::isSelected(item_pair_t* item_pair) const @@ -952,11 +947,17 @@ void LLFlatListView::getValues(std::vector<LLSD>& values) const  void LLFlatListView::onFocusReceived()  {  	mSelectedItemsBorder->setVisible(TRUE); +	gEditMenuHandler = this;  }  // virtual  void LLFlatListView::onFocusLost()  {  	mSelectedItemsBorder->setVisible(FALSE); +	// Route menu back to the default + 	if( gEditMenuHandler == this ) +	{ +		gEditMenuHandler = NULL; +	}  }  //virtual  diff --git a/indra/llui/llflatlistview.h b/indra/llui/llflatlistview.h index dc6400c926..e3c07e811f 100644 --- a/indra/llui/llflatlistview.h +++ b/indra/llui/llflatlistview.h @@ -58,7 +58,7 @@   * - Order of returned selected items are not guaranteed   * - The control assumes that all items being added are unique.   */ -class LLFlatListView : public LLScrollContainer +class LLFlatListView : public LLScrollContainer, public LLEditMenuHandler  {  public: @@ -114,8 +114,6 @@ public:  		Params();  	}; -	virtual ~LLFlatListView() { clear(); }; -  	/**  	 * Connects callback to signal called when Return key is pressed.  	 */ @@ -344,7 +342,8 @@ protected:  	virtual bool selectNextItemPair(bool is_up_direction, bool reset_selection); -	virtual bool selectAll(); +	virtual BOOL canSelectAll() const; +	virtual void selectAll();  	virtual bool isSelected(item_pair_t* item_pair) const; diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 483a394bbd..843f72d8e4 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -91,7 +91,6 @@ LLLineEditor::Params::Params()  	background_image_disabled("background_image_disabled"),  	background_image_focused("background_image_focused"),  	select_on_focus("select_on_focus", false), -	handle_edit_keys_directly("handle_edit_keys_directly", false),  	revert_on_esc("revert_on_esc", true),  	commit_on_focus_lost("commit_on_focus_lost", true),  	ignore_tab("ignore_tab", true), @@ -136,7 +135,6 @@ LLLineEditor::LLLineEditor(const LLLineEditor::Params& p)  	mIgnoreArrowKeys( FALSE ),  	mIgnoreTab( p.ignore_tab ),  	mDrawAsterixes( FALSE ), -	mHandleEditKeysDirectly(p.handle_edit_keys_directly),  	mSelectAllonFocusReceived( p.select_on_focus ),  	mPassDelete(FALSE),  	mReadOnly(FALSE), @@ -192,12 +190,8 @@ LLLineEditor::~LLLineEditor()  {  	mCommitOnFocusLost = FALSE; +	// calls onCommit() while LLLineEditor still valid  	gFocusMgr.releaseFocusIfNeeded( this ); - -	if( gEditMenuHandler == this ) -	{ -		gEditMenuHandler = NULL; -	}  } @@ -497,6 +491,7 @@ void LLLineEditor::selectAll()  	setCursor(mSelectionEnd);  	//mScrollHPos = 0;  	mIsSelecting = TRUE; +	updatePrimary();  } @@ -788,7 +783,7 @@ void LLLineEditor::removeChar()  	}  	else  	{ -		reportBadKeystroke(); +		LLUI::reportBadKeystroke();  	}  } @@ -827,7 +822,7 @@ void LLLineEditor::addChar(const llwchar uni_char)  	}  	else  	{ -		reportBadKeystroke(); +		LLUI::reportBadKeystroke();  	}  	getWindow()->hideCursorUntilMouseMove(); @@ -916,7 +911,7 @@ BOOL LLLineEditor::handleSelectionKey(KEY key, MASK mask)  			}  			else  			{ -				reportBadKeystroke(); +				LLUI::reportBadKeystroke();  			}  			break; @@ -932,7 +927,7 @@ BOOL LLLineEditor::handleSelectionKey(KEY key, MASK mask)  			}  			else  			{ -				reportBadKeystroke(); +				LLUI::reportBadKeystroke();  			}  			break; @@ -958,22 +953,6 @@ BOOL LLLineEditor::handleSelectionKey(KEY key, MASK mask)  		}  	} -	if (!handled && mHandleEditKeysDirectly) -	{ -		if( (MASK_CONTROL & mask) && ('A' == key) ) -		{ -			if( canSelectAll() ) -			{ -				selectAll(); -			} -			else -			{ -				reportBadKeystroke(); -			} -			handled = TRUE; -		} -	} -  	if(handled)  	{  		// take selection to 'primary' clipboard @@ -1020,7 +999,7 @@ void LLLineEditor::cut()  		if( need_to_rollback )  		{  			rollback.doRollback( this ); -			reportBadKeystroke(); +			LLUI::reportBadKeystroke();  		}  		else  		if( mKeystrokeCallback ) @@ -1129,7 +1108,7 @@ void LLLineEditor::pasteHelper(bool is_primary)  				}  				// Truncate the clean string at the limit of what will fit  				clean_string = clean_string.substr(0, wchars_that_fit); -				reportBadKeystroke(); +				LLUI::reportBadKeystroke();  			}  			mText.insert(getCursor(), clean_string); @@ -1141,7 +1120,7 @@ void LLLineEditor::pasteHelper(bool is_primary)  			if( need_to_rollback )  			{  				rollback.doRollback( this ); -				reportBadKeystroke(); +				LLUI::reportBadKeystroke();  			}  			else  			if( mKeystrokeCallback ) @@ -1206,7 +1185,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)  			}  			else  			{ -				reportBadKeystroke(); +				LLUI::reportBadKeystroke();  			}  		}  		handled = TRUE; @@ -1255,7 +1234,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)  			}  			else  			{ -				reportBadKeystroke(); +				LLUI::reportBadKeystroke();  			}  			handled = TRUE;  		} @@ -1282,7 +1261,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)  			}  			else  			{ -				reportBadKeystroke(); +				LLUI::reportBadKeystroke();  			}  			handled = TRUE;  		} @@ -1299,7 +1278,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)  			}  			else  			{ -				reportBadKeystroke(); +				LLUI::reportBadKeystroke();  			}  			handled = TRUE;  		} @@ -1316,7 +1295,7 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)  			}  			else  			{ -				reportBadKeystroke(); +				LLUI::reportBadKeystroke();  			}  			handled = TRUE;  		} @@ -1339,64 +1318,6 @@ BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)  		break;  	} -	if( !handled && mHandleEditKeysDirectly ) -	{ -		// Standard edit keys (Ctrl-X, Delete, etc,) are handled here instead of routed by the menu system. -		if( KEY_DELETE == key ) -		{ -			if( canDoDelete() ) -			{ -				doDelete(); -			} -			else -			{ -				reportBadKeystroke(); -			} -			handled = TRUE; -		} -		else -		if( MASK_CONTROL & mask ) -		{ -			if( 'C' == key ) -			{ -				if( canCopy() ) -				{ -					copy(); -				} -				else -				{ -					reportBadKeystroke(); -				} -				handled = TRUE; -			} -			else -			if( 'V' == key ) -			{ -				if( canPaste() ) -				{ -					paste(); -				} -				else -				{ -					reportBadKeystroke(); -				} -				handled = TRUE; -			} -			else -			if( 'X' == key ) -			{ -				if( canCut() ) -				{ -					cut(); -				} -				else -				{ -					reportBadKeystroke(); -				} -				handled = TRUE; -			} -		} -	}  	return handled;  } @@ -1451,7 +1372,7 @@ BOOL LLLineEditor::handleKeyHere(KEY key, MASK mask )  			{  				rollback.doRollback(this); -				reportBadKeystroke(); +				LLUI::reportBadKeystroke();  			}  			// Notify owner if requested @@ -1499,7 +1420,7 @@ BOOL LLLineEditor::handleUnicodeCharHere(llwchar uni_char)  		{  			rollback.doRollback( this ); -			reportBadKeystroke(); +			LLUI::reportBadKeystroke();  		}  		// Notify owner if requested @@ -1544,7 +1465,7 @@ void LLLineEditor::doDelete()  		if( need_to_rollback )  		{  			rollback.doRollback( this ); -			reportBadKeystroke(); +			LLUI::reportBadKeystroke();  		}  		else  		{ @@ -1879,11 +1800,6 @@ S32 LLLineEditor::findPixelNearestPos(const S32 cursor_offset) const  	return result;  } -void LLLineEditor::reportBadKeystroke() -{ -	make_ui_sound("UISndBadKeystroke"); -} -  //virtual  void LLLineEditor::clear()  { diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h index b62138426b..9489e723e3 100644 --- a/indra/llui/lllineeditor.h +++ b/indra/llui/lllineeditor.h @@ -81,7 +81,6 @@ public:  										background_image_focused;  		Optional<bool>					select_on_focus, -										handle_edit_keys_directly,  										revert_on_esc,  										commit_on_focus_lost,  										ignore_tab; @@ -215,7 +214,6 @@ public:  	void			extendSelection(S32 new_cursor_pos);  	void			deleteSelection(); -	void			setHandleEditKeysDirectly( BOOL b ) { mHandleEditKeysDirectly = b; }  	void			setSelectAllonFocusReceived(BOOL b);  	typedef boost::function<void (LLLineEditor* caller, void* user_data)> callback_t; @@ -247,7 +245,6 @@ private:  	void			addChar(const llwchar c);  	void			setCursorAtLocalPos(S32 local_mouse_x);  	S32				findPixelNearestPos(S32 cursor_offset = 0) const; -	void			reportBadKeystroke();  	BOOL			handleSpecialKey(KEY key, MASK mask);  	BOOL			handleSelectionKey(KEY key, MASK mask);  	BOOL			handleControlKey(KEY key, MASK mask); @@ -325,7 +322,6 @@ protected:  	BOOL		mIgnoreTab;  	BOOL		mDrawAsterixes; -	BOOL		mHandleEditKeysDirectly;  // If true, the standard edit keys (Ctrl-X, Delete, etc,) are handled here instead of routed by the menu system  	BOOL		mSelectAllonFocusReceived;  	BOOL		mPassDelete; diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index fb4a9d032d..e0e86ae228 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -3345,7 +3345,7 @@ void LLMenuHolderGL::draw()  	LLView::draw();  	// now draw last selected item as overlay  	LLMenuItemGL* selecteditem = (LLMenuItemGL*)sItemLastSelectedHandle.get(); -	if (selecteditem && sItemActivationTimer.getStarted() && sItemActivationTimer.getElapsedTimeF32() < ACTIVATE_HIGHLIGHT_TIME) +	if (selecteditem && selecteditem->getVisible() && sItemActivationTimer.getStarted() && sItemActivationTimer.getElapsedTimeF32() < ACTIVATE_HIGHLIGHT_TIME)  	{  		// make sure toggle items, for example, show the proper state when fading out  		selecteditem->buildDrawLabel(); diff --git a/indra/llui/llmultifloater.cpp b/indra/llui/llmultifloater.cpp index 4af9108329..3aea648562 100644 --- a/indra/llui/llmultifloater.cpp +++ b/indra/llui/llmultifloater.cpp @@ -345,7 +345,7 @@ void LLMultiFloater::setVisible(BOOL visible)  BOOL LLMultiFloater::handleKeyHere(KEY key, MASK mask)  { -	if (key == 'W' && mask == MASK_CONTROL) +	if (key == 'W' && mask == (MASK_CONTROL|MASK_SHIFT))  	{  		LLFloater* floater = getActiveFloater();  		// is user closeable and is system closeable diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index bf0866a655..db0f2bd6e2 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -324,11 +324,6 @@ LLScrollListCtrl::~LLScrollListCtrl()  	delete mSortCallback;  	std::for_each(mItemList.begin(), mItemList.end(), DeletePointer()); - -	if( gEditMenuHandler == this ) -	{ -		gEditMenuHandler = NULL; -	}  } diff --git a/indra/llui/lltextbase.cpp b/indra/llui/lltextbase.cpp index 56d7a63832..e08026eaf4 100644 --- a/indra/llui/lltextbase.cpp +++ b/indra/llui/lltextbase.cpp @@ -1059,6 +1059,13 @@ void LLTextBase::setValue(const LLSD& value )  }  //virtual +BOOL LLTextBase::canDeselect() const  +{  +	return hasSelection();  +} + + +//virtual  void LLTextBase::deselect()  {  	mSelectionStart = 0; diff --git a/indra/llui/lltextbase.h b/indra/llui/lltextbase.h index 5b24c63557..8ed0680df9 100644 --- a/indra/llui/lltextbase.h +++ b/indra/llui/lltextbase.h @@ -132,6 +132,7 @@ public:  	/*virtual*/ LLTextViewModel* getViewModel() const;  	// LLEditMenuHandler interface +	/*virtual*/ BOOL		canDeselect() const;  	/*virtual*/ void		deselect();  	// used by LLTextSegment layout code diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 7d230f7d42..a1cae4bb98 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -240,7 +240,6 @@ LLTextEditor::Params::Params()  	prevalidate_callback("prevalidate_callback"),  	embedded_items("embedded_items", false),  	ignore_tab("ignore_tab", true), -	handle_edit_keys_directly("handle_edit_keys_directly", false),  	show_line_numbers("show_line_numbers", false),  	default_color("default_color"),      commit_on_focus_lost("commit_on_focus_lost", false), @@ -258,7 +257,6 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) :  	mShowLineNumbers ( p.show_line_numbers ),  	mCommitOnFocusLost( p.commit_on_focus_lost),  	mAllowEmbeddedItems( p.embedded_items ), -	mHandleEditKeysDirectly( p.handle_edit_keys_directly ),  	mMouseDownX(0),  	mMouseDownY(0),  	mTabsToNextField(p.ignore_tab), @@ -305,12 +303,6 @@ LLTextEditor::~LLTextEditor()  {  	gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() while LLTextEditor still valid -	// Route menu back to the default -	if( gEditMenuHandler == this ) -	{ -		gEditMenuHandler = NULL; -	} -  	// Scrollbar is deleted by LLView  	std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer()); @@ -507,21 +499,6 @@ void LLTextEditor::getSegmentsInRange(LLTextEditor::segment_vec_t& segments_out,  	}  } -// virtual -BOOL LLTextEditor::canDeselect() const -{ -	return hasSelection();  -} - - -void LLTextEditor::deselect() -{ -	mSelectionStart = 0; -	mSelectionEnd = 0; -	mIsSelecting = FALSE; -} - -  BOOL LLTextEditor::selectionContainsLineBreaks()  {  	if (hasSelection()) @@ -668,6 +645,7 @@ void LLTextEditor::selectAll()  	mSelectionStart = getLength();  	mSelectionEnd = 0;  	setCursorPos(mSelectionEnd); +	updatePrimary();  }  BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) @@ -1025,7 +1003,7 @@ void LLTextEditor::removeCharOrTab()  	}  	else  	{ -		reportBadKeystroke(); +		LLUI::reportBadKeystroke();  	}  } @@ -1048,7 +1026,7 @@ void LLTextEditor::removeChar()  	}  	else  	{ -		reportBadKeystroke(); +		LLUI::reportBadKeystroke();  	}  } @@ -1198,22 +1176,6 @@ BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask)  		}  	} -	if( !handled && mHandleEditKeysDirectly ) -	{ -		if( (MASK_CONTROL & mask) && ('A' == key) ) -		{ -			if( canSelectAll() ) -			{ -				selectAll(); -			} -			else -			{ -				reportBadKeystroke(); -			} -			handled = TRUE; -		} -	} -  	if( handled )  	{  		// take selection to 'primary' clipboard @@ -1247,6 +1209,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)  		case KEY_DOWN:  			changeLine( 1 ); +			deselect();  			break;  		case KEY_PAGE_DOWN: @@ -1260,7 +1223,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)  		case KEY_LEFT:  			if( hasSelection() )  			{ -				setCursorPos(llmin( mCursorPos - 1, mSelectionStart, mSelectionEnd )); +				setCursorPos(llmin( mSelectionStart, mSelectionEnd ));  			}  			else  			{ @@ -1270,7 +1233,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)  				}  				else  				{ -					reportBadKeystroke(); +					LLUI::reportBadKeystroke();  				}  			}  			break; @@ -1278,7 +1241,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)  		case KEY_RIGHT:  			if( hasSelection() )  			{ -				setCursorPos(llmax( mCursorPos + 1, mSelectionStart, mSelectionEnd )); +				setCursorPos(llmax( mSelectionStart, mSelectionEnd ));  			}  			else  			{ @@ -1288,7 +1251,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)  				}  				else  				{ -					reportBadKeystroke(); +					LLUI::reportBadKeystroke();  				}  			}	  			break; @@ -1299,6 +1262,11 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)  		}  	} +	if (handled) +	{ +		deselect(); +	} +	  	return handled;  } @@ -1551,75 +1519,13 @@ BOOL LLTextEditor::handleControlKey(const KEY key, const MASK mask)  	return handled;  } -BOOL LLTextEditor::handleEditKey(const KEY key, const MASK mask) -{ -	BOOL handled = FALSE; -	// Standard edit keys (Ctrl-X, Delete, etc,) are handled here instead of routed by the menu system. -	if( KEY_DELETE == key ) -	{ -		if( canDoDelete() ) -		{ -			doDelete(); -		} -		else -		{ -			reportBadKeystroke(); -		} -		handled = TRUE; -	} -	else -	if( MASK_CONTROL & mask ) +BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask)	  	{ -		if( 'C' == key ) -		{ -			if( canCopy() ) -			{ -				copy(); -			} -			else -			{ -				reportBadKeystroke(); -			} -			handled = TRUE; -		} -		else -		if( 'V' == key ) -		{ -			if( canPaste() ) -			{ -				paste(); -			} -			else -			{ -				reportBadKeystroke(); -			} -			handled = TRUE; -		} -		else -		if( 'X' == key ) -		{ -			if( canCut() ) -			{ -				cut(); -			} -			else -			{ -				reportBadKeystroke(); -			} -			handled = TRUE; -		} -	} - -	return handled; -} - -	 -BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask, BOOL* return_key_hit)	 -{ -	*return_key_hit = FALSE;  	BOOL handled = TRUE; +	if (mReadOnly) return FALSE; +  	switch( key )  	{  	case KEY_INSERT: @@ -1641,7 +1547,7 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask, BOOL* return  		}  		else  		{ -			reportBadKeystroke(); +			LLUI::reportBadKeystroke();  		}  		break; @@ -1694,6 +1600,10 @@ BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask, BOOL* return  		break;  	} +	if (handled) +	{ +		onKeyStroke(); +	}  	return handled;  } @@ -1714,9 +1624,6 @@ void LLTextEditor::unindentLineBeforeCloseBrace()  BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask )  {  	BOOL	handled = FALSE; -	BOOL	selection_modified = FALSE; -	BOOL	return_key_hit = FALSE; -	BOOL	text_may_have_changed = TRUE;  	// Special case for TAB.  If want to move to next field, report  	// not handled and let the parent take care of field movement. @@ -1724,116 +1631,24 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask )  	{  		return FALSE;  	} -	/* -	if (KEY_F10 == key) -	{ -		LLComboBox::Params cp; -		cp.name = "combo box"; -		cp.label = "my combo"; -		cp.rect.width = 100; -		cp.rect.height = 20; -		cp.items.add().label = "item 1"; -		cp.items.add().label = "item 2"; -		cp.items.add().label = "item 3"; -		appendWidget(LLUICtrlFactory::create<LLComboBox>(cp), "combo", true, false); -	} -	if (KEY_F11 == key) -	{ -		LLButton::Params bp; -		bp.name = "text button"; -		bp.label = "Click me"; -		bp.rect.width = 100; -		bp.rect.height = 20; - -		appendWidget(LLUICtrlFactory::create<LLButton>(bp), "button", true, false); -	} -	*/ -	if (mReadOnly) +	if (mReadOnly && mScroller)  	{ -		if(mScroller) -		{ -			handled = mScroller->handleKeyHere( key, mask ); +		handled = (mScroller && mScroller->handleKeyHere( key, mask )) +				|| handleSelectionKey(key, mask) +				|| handleControlKey(key, mask);  		}  		else   		{ -			handled = handleNavigationKey( key, mask ); -		} - -	} -	else -	{ -		// handle navigation keys ourself -		handled = handleNavigationKey( key, mask ); -	} - - -	if( handled ) -	{ -		text_may_have_changed = FALSE; -	} -		 -	if( !handled ) -	{ -		handled = handleSelectionKey( key, mask ); -		if( handled ) -		{ -			selection_modified = TRUE; -		} -	} - -	if( !handled ) -	{ -		handled = handleControlKey( key, mask ); -		if( handled ) -		{ -			selection_modified = TRUE; -		} -	} - -	if( !handled && mHandleEditKeysDirectly ) -	{ -		handled = handleEditKey( key, mask ); -		if( handled ) -		{ -			selection_modified = TRUE; -			text_may_have_changed = TRUE; -		} -	} - -	// Handle most keys only if the text editor is writeable. -	if( !mReadOnly ) -	{ -		if( !handled ) -		{ -			handled = handleSpecialKey( key, mask, &return_key_hit ); -			if( handled ) -			{ -				selection_modified = TRUE; -				text_may_have_changed = TRUE; -			} -		} - +		handled = handleNavigationKey( key, mask ) +				|| handleSelectionKey(key, mask) +				|| handleControlKey(key, mask) +				|| handleSpecialKey(key, mask);  	}  	if( handled )  	{  		resetCursorBlink(); - -		// Most keystrokes will make the selection box go away, but not all will. -		if( !selection_modified && -			KEY_SHIFT != key && -			KEY_CONTROL != key && -			KEY_ALT != key && -			KEY_CAPSLOCK ) -		{ -			deselect(); -		} - -		if(text_may_have_changed) -		{ -			onKeyStroke(); -		}  		needsScroll();  	} @@ -2334,7 +2149,7 @@ void LLTextEditor::getCurrentLineAndColumn( S32* line, S32* col, BOOL include_wo  void LLTextEditor::autoIndent()  {  	// Count the number of spaces in the current line -	S32 line = getLineNumFromDocIndex(mCursorPos); +	S32 line = getLineNumFromDocIndex(mCursorPos, false);  	S32 line_start = getLineStart(line);  	S32 space_count = 0;  	S32 i; diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 71d937b2c4..9b3ab9414c 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -68,7 +68,6 @@ public:  		Optional<bool>			embedded_items,  								ignore_tab, -								handle_edit_keys_directly,  								show_line_numbers,  								commit_on_focus_lost,  								show_context_menu; @@ -146,8 +145,6 @@ public:  	virtual BOOL	canDoDelete() const;  	virtual void	selectAll();  	virtual BOOL	canSelectAll()	const; -	virtual void	deselect(); -	virtual BOOL	canDeselect() const;  	void			selectNext(const std::string& search_text_in, BOOL case_insensitive, BOOL wrap = TRUE);  	BOOL			replaceText(const std::string& search_text, const std::string& replace_text, BOOL case_insensitive, BOOL wrap = TRUE); @@ -218,13 +215,10 @@ protected:  	S32				indentLine( S32 pos, S32 spaces );  	void			unindentLineBeforeCloseBrace(); -	void			reportBadKeystroke() { make_ui_sound("UISndBadKeystroke"); } -  	BOOL			handleNavigationKey(const KEY key, const MASK mask); -	BOOL			handleSpecialKey(const KEY key, const MASK mask, BOOL* return_key_hit); +	BOOL			handleSpecialKey(const KEY key, const MASK mask);  	BOOL			handleSelectionKey(const KEY key, const MASK mask);  	BOOL			handleControlKey(const KEY key, const MASK mask); -	BOOL			handleEditKey(const KEY key, const MASK mask);  	BOOL			selectionContainsLineBreaks();  	void			deleteSelection(BOOL transient_operation); @@ -329,10 +323,6 @@ private:  	LLUUID			mSourceID; -	// If true, the standard edit keys (Ctrl-X, Delete, etc,) are handled here  -	//instead of routed by the menu system -	BOOL			mHandleEditKeysDirectly;   -  	LLCoordGL		mLastIMEPosition;		// Last position of the IME editor  	keystroke_signal_t mKeystrokeSignal; diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index b049895526..f9a4ed7285 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -1914,7 +1914,12 @@ void LLUI::clearPopups()  	}  } - +//static +void LLUI::reportBadKeystroke() +{ +	make_ui_sound("UISndBadKeystroke"); +} +	  //static  // spawn_x and spawn_y are top left corner of view in screen GL coordinates  void LLUI::positionViewNearMouse(LLView* view, S32 spawn_x, S32 spawn_y) diff --git a/indra/llui/llui.h b/indra/llui/llui.h index 30f3623ded..c18262ef76 100644 --- a/indra/llui/llui.h +++ b/indra/llui/llui.h @@ -217,6 +217,8 @@ public:  	static void removePopup(LLView*);  	static void clearPopups(); +	static void reportBadKeystroke(); +  	// Ensures view does not overlap mouse cursor, but is inside  	// the view's parent rectangle.  Used for tooltips, inspectors.  	// Optionally override the view's default X/Y, which are relative to the diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 2f93ab0f04..d7666ca4c3 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -41,6 +41,9 @@  #include "lltrans.h"  #include "lluicolortable.h" +#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))" + +  LLUrlEntryBase::LLUrlEntryBase() :  	mColor(LLUIColorTable::instance().getColor("HTMLLinkColor")),  	mDisabledLink(false) @@ -303,10 +306,11 @@ std::string LLUrlEntrySLURL::getLocation(const std::string &url) const  //  // LLUrlEntryAgent Describes a Second Life agent Url, e.g.,  // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about  //  LLUrlEntryAgent::LLUrlEntryAgent()  { -	mPattern = boost::regex("secondlife:///app/agent/[\\da-f-]+/\\w+", +	mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/\\w+",  							boost::regex::perl|boost::regex::icase);  	mMenuName = "menu_url_agent.xml";  	mIcon = "Generic_Person"; @@ -418,10 +422,11 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa  // LLUrlEntryGroup Describes a Second Life group Url, e.g.,  // secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about  // secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect +// x-grid-location-info://lincoln.lindenlab.com/app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect  //  LLUrlEntryGroup::LLUrlEntryGroup()  { -	mPattern = boost::regex("secondlife:///app/group/[\\da-f-]+/\\w+", +	mPattern = boost::regex(APP_HEADER_REGEX "/group/[\\da-f-]+/\\w+",  							boost::regex::perl|boost::regex::icase);  	mMenuName = "menu_url_group.xml";  	mIcon = "Generic_Group"; @@ -482,7 +487,8 @@ LLUrlEntryInventory::LLUrlEntryInventory()  	//*TODO: add supporting of inventory item names with whitespaces  	//this pattern cann't parse for example   	//secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces¶m2=value -	mPattern = boost::regex("secondlife:///app/inventory/[\\da-f-]+/\\w+\\S*", +	//x-grid-location-info://lincoln.lindenlab.com/app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces¶m2=value +	mPattern = boost::regex(APP_HEADER_REGEX "/inventory/[\\da-f-]+/\\w+\\S*",  							boost::regex::perl|boost::regex::icase);  	mMenuName = "menu_url_inventory.xml";  } @@ -525,10 +531,11 @@ std::string LLUrlEntryObjectIM::getLocation(const std::string &url) const  ///  /// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,  /// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about +/// x-grid-location-info://lincoln.lindenlab.com/app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about  ///  LLUrlEntryParcel::LLUrlEntryParcel()  { -	mPattern = boost::regex("secondlife:///app/parcel/[\\da-f-]+/about", +	mPattern = boost::regex(APP_HEADER_REGEX "/parcel/[\\da-f-]+/about",  							boost::regex::perl|boost::regex::icase);  	mMenuName = "menu_url_parcel.xml";  	mTooltip = LLTrans::getString("TooltipParcelUrl"); @@ -544,7 +551,7 @@ std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelC  //  LLUrlEntryPlace::LLUrlEntryPlace()  { -	mPattern = boost::regex("secondlife://(?!/)\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?", +	mPattern = boost::regex("((x-grid-location-info://[-\\w\\.]+/region/)|(secondlife://))\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?",  							boost::regex::perl|boost::regex::icase);  	mMenuName = "menu_url_slurl.xml";  	mTooltip = LLTrans::getString("TooltipSLURL"); @@ -589,10 +596,11 @@ std::string LLUrlEntryPlace::getLocation(const std::string &url) const  //  // LLUrlEntryTeleport Describes a Second Life teleport Url, e.g.,  // secondlife:///app/teleport/Ahern/50/50/50/ +// x-grid-location-info://lincoln.lindenlab.com/app/teleport/Ahern/50/50/50/  //  LLUrlEntryTeleport::LLUrlEntryTeleport()  { -	mPattern = boost::regex("secondlife:///app/teleport/\\S+(/\\d+)?(/\\d+)?(/\\d+)?/?\\S*", +	mPattern = boost::regex(APP_HEADER_REGEX "/teleport/\\S+(/\\d+)?(/\\d+)?(/\\d+)?/?\\S*",  							boost::regex::perl|boost::regex::icase);  	mMenuName = "menu_url_teleport.xml";  	mTooltip = LLTrans::getString("TooltipTeleportUrl"); @@ -610,7 +618,12 @@ std::string LLUrlEntryTeleport::getLabel(const std::string &url, const LLUrlLabe  	LLURI uri(url);  	LLSD path_array = uri.pathArray();  	S32 path_parts = path_array.size(); -	const std::string label = LLTrans::getString("SLurlLabelTeleport"); +	std::string host = uri.hostName(); +	std::string label = LLTrans::getString("SLurlLabelTeleport"); +	if (!host.empty()) +	{ +		label += " " + host; +	}  	if (path_parts == 6)  	{  		// handle teleport url with (X,Y,Z) coordinates @@ -709,7 +722,7 @@ std::string LLUrlEntrySLLabel::getTooltip(const std::string &string) const  //  LLUrlEntryWorldMap::LLUrlEntryWorldMap()  { -	mPattern = boost::regex("secondlife:///app/worldmap/\\S+/?(\\d+)?/?(\\d+)?/?(\\d+)?/?\\S*", +	mPattern = boost::regex(APP_HEADER_REGEX "/worldmap/\\S+/?(\\d+)?/?(\\d+)?/?(\\d+)?/?\\S*",  							boost::regex::perl|boost::regex::icase);  	mMenuName = "menu_url_map.xml";  	mTooltip = LLTrans::getString("TooltipMapUrl"); diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp index cbb303a059..4463b6cc6f 100644 --- a/indra/llui/tests/llurlentry_test.cpp +++ b/indra/llui/tests/llurlentry_test.cpp @@ -286,6 +286,13 @@ namespace tut  				  "XXX secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar",  				  "secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar"); +		testRegex("Standalone Agent Url ", url, +				  "x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about", +				  "x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");		 + +		testRegex("Standalone Agent Url Multicase with Text", url, +				  "M x-grid-location-info://lincoln.lindenlab.com/app/AGENT/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about M", +				  "x-grid-location-info://lincoln.lindenlab.com/app/AGENT/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");		  	}  	template<> template<> @@ -315,6 +322,15 @@ namespace tut  		testRegex("Group Url multicase", url,  				  "XXX secondlife:///APP/Group/00005FF3-4044-c79f-9de8-fb28ae0df991/About XXX",  				  "secondlife:///APP/Group/00005FF3-4044-c79f-9de8-fb28ae0df991/About"); +		 +		testRegex("Standalone Group Url ", url, +				  "x-grid-location-info://lincoln.lindenlab.com/app/group/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about", +				  "x-grid-location-info://lincoln.lindenlab.com/app/group/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");		 +		 +		testRegex("Standalone Group Url Multicase ith Text", url, +				  "M x-grid-location-info://lincoln.lindenlab.com/app/GROUP/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about M", +				  "x-grid-location-info://lincoln.lindenlab.com/app/GROUP/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about");		 +		  	}  	template<> template<> @@ -361,7 +377,11 @@ namespace tut  		// DEV-35459: SLURLs and teleport Links not parsed properly  		testRegex("SLURL with quote", url,  				  "XXX secondlife://A'ksha%20Oasis/41/166/701 XXX", -				  "secondlife://A%27ksha%20Oasis/41/166/701"); +			          "secondlife://A%27ksha%20Oasis/41/166/701"); +		 +		testRegex("Standalone All Hands (50,50) [2] with text", url, +				  "XXX x-grid-location-info://lincoln.lindenlab.com/region/All%20Hands/50/50/50 XXX", +				  "x-grid-location-info://lincoln.lindenlab.com/region/All%20Hands/50/50/50");		  	}  	template<> template<> @@ -461,6 +481,10 @@ namespace tut  		testRegex("Teleport url with quote", url,  				  "XXX secondlife:///app/teleport/A'ksha%20Oasis/41/166/701 XXX",  				  "secondlife:///app/teleport/A%27ksha%20Oasis/41/166/701"); +		 +		testRegex("Standalone All Hands", url, +				  "XXX x-grid-location-info://lincoln.lindenlab.com/app/teleport/All%20Hands/50/50/50 XXX", +				  "x-grid-location-info://lincoln.lindenlab.com/app/teleport/All%20Hands/50/50/50");		  	}  	template<> template<> diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index da4abde451..b4ee42ef3a 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -459,7 +459,6 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd  	}  	//llinfos << "*** EXPANDED FILENAME: <" << expanded_filename << ">" << llendl; -  	return expanded_filename;  } @@ -565,27 +564,23 @@ std::string LLDir::getForbiddenFileChars()  	return "\\/:*?\"<>|";  } -void LLDir::setLindenUserDir(const std::string &first, const std::string &last) +void LLDir::setLindenUserDir(const std::string &username)  { -	// if both first and last aren't set, that's bad. -	if (!first.empty() && !last.empty()) +	// if the username isn't set, that's bad +	if (!username.empty())  	{  		// some platforms have case-sensitive filesystems, so be  		// utterly consistent with our firstname/lastname case. -		std::string firstlower(first); -		LLStringUtil::toLower(firstlower); -		std::string lastlower(last); -		LLStringUtil::toLower(lastlower); +		std::string userlower(username); +		LLStringUtil::toLower(userlower); +		LLStringUtil::replaceChar(userlower, ' ', '_');  		mLindenUserDir = getOSUserAppDir();  		mLindenUserDir += mDirDelimiter; -		mLindenUserDir += firstlower; -		mLindenUserDir += "_"; -		mLindenUserDir += lastlower; -		llinfos << "Got name for LLDir::setLindenUserDir(first='" << first << "', last='" << last << "')" << llendl; +		mLindenUserDir += userlower;  	}  	else  	{ -		llerrs << "Invalid name for LLDir::setLindenUserDir(first='" << first << "', last='" << last << "')" << llendl; +		llerrs << "NULL name for LLDir::setLindenUserDir" << llendl;  	}  	dumpCurrentDirectories();	 @@ -603,27 +598,25 @@ void LLDir::setChatLogsDir(const std::string &path)  	}  } -void LLDir::setPerAccountChatLogsDir(const std::string &first, const std::string &last) +void LLDir::setPerAccountChatLogsDir(const std::string &username)  {  	// if both first and last aren't set, assume we're grabbing the cached dir -	if (!first.empty() && !last.empty()) +	if (!username.empty())  	{  		// some platforms have case-sensitive filesystems, so be  		// utterly consistent with our firstname/lastname case. -		std::string firstlower(first); -		LLStringUtil::toLower(firstlower); -		std::string lastlower(last); -		LLStringUtil::toLower(lastlower); +		std::string userlower(username); +		LLStringUtil::toLower(userlower); +		LLStringUtil::replaceChar(userlower, ' ', '_');  		mPerAccountChatLogsDir = getChatLogsDir();  		mPerAccountChatLogsDir += mDirDelimiter; -		mPerAccountChatLogsDir += firstlower; -		mPerAccountChatLogsDir += "_"; -		mPerAccountChatLogsDir += lastlower; +		mPerAccountChatLogsDir += userlower;  	}  	else  	{ -		llwarns << "Invalid name for LLDir::setPerAccountChatLogsDir" << llendl; +		llerrs << "NULL name for LLDir::setPerAccountChatLogsDir" << llendl;  	} +	  }  void LLDir::setSkinFolder(const std::string &skin_folder) diff --git a/indra/llvfs/lldir.h b/indra/llvfs/lldir.h index 9067d75bac..05d5efc66f 100644 --- a/indra/llvfs/lldir.h +++ b/indra/llvfs/lldir.h @@ -137,8 +137,8 @@ class LLDir  	static std::string getForbiddenFileChars();  	virtual void setChatLogsDir(const std::string &path);		// Set the chat logs dir to this user's dir -	virtual void setPerAccountChatLogsDir(const std::string &first, const std::string &last);		// Set the per user chat log directory. -	virtual void setLindenUserDir(const std::string &first, const std::string &last);		// Set the linden user dir to this user's dir +	virtual void setPerAccountChatLogsDir(const std::string &username);		// Set the per user chat log directory. +	virtual void setLindenUserDir(const std::string &username);		// Set the linden user dir to this user's dir  	virtual void setSkinFolder(const std::string &skin_folder);  	virtual bool setCacheDir(const std::string &path); diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index d726c60018..95c1980dd6 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -2999,79 +2999,6 @@ void LLWindowWin32::spawnWebBrowser(const std::string& escaped_url, bool async)  	sei.lpVerb = L"open";  	sei.lpFile = url_utf16.c_str();  	ShellExecuteEx( &sei ); - -	//// TODO: LEAVING OLD CODE HERE SO I DON'T BONE OTHER MERGES -	//// DELETE THIS ONCE THE MERGES ARE DONE - -	// Figure out the user's default web browser -	// HKEY_CLASSES_ROOT\http\shell\open\command -	/* -	std::string reg_path_str = gURLProtocolWhitelistHandler[i] + "\\shell\\open\\command"; -	WCHAR reg_path_wstr[256]; -	mbstowcs( reg_path_wstr, reg_path_str.c_str(), LL_ARRAY_SIZE(reg_path_wstr) ); - -	HKEY key; -	WCHAR browser_open_wstr[1024]; -	DWORD buffer_length = 1024; -	RegOpenKeyEx(HKEY_CLASSES_ROOT, reg_path_wstr, 0, KEY_QUERY_VALUE, &key); -	RegQueryValueEx(key, NULL, NULL, NULL, (LPBYTE)browser_open_wstr, &buffer_length); -	RegCloseKey(key); - -	// Convert to STL string -	LLWString browser_open_wstring = utf16str_to_wstring(browser_open_wstr); - -	if (browser_open_wstring.length() < 2) -	{ -		LL_WARNS("Window") << "Invalid browser executable in registry " << browser_open_wstring << LL_ENDL; -		return; -	} - -	// Extract the process that's supposed to be launched -	LLWString browser_executable; -	if (browser_open_wstring[0] == '"') -	{ -		// executable is quoted, find the matching quote -		size_t quote_pos = browser_open_wstring.find('"', 1); -		// copy out the string including both quotes -		browser_executable = browser_open_wstring.substr(0, quote_pos+1); -	} -	else -	{ -		// executable not quoted, find a space -		size_t space_pos = browser_open_wstring.find(' ', 1); -		browser_executable = browser_open_wstring.substr(0, space_pos); -	} - -	LL_DEBUGS("Window") << "Browser reg key: " << wstring_to_utf8str(browser_open_wstring) << LL_ENDL; -	LL_INFOS("Window") << "Browser executable: " << wstring_to_utf8str(browser_executable) << LL_ENDL; - -	// Convert URL to wide string for Windows API -	// Assume URL is UTF8, as can come from scripts -	LLWString url_wstring = utf8str_to_wstring(escaped_url); -	llutf16string url_utf16 = wstring_to_utf16str(url_wstring); - -	// Convert executable and path to wide string for Windows API -	llutf16string browser_exec_utf16 = wstring_to_utf16str(browser_executable); - -	// ShellExecute returns HINSTANCE for backwards compatiblity. -	// MS docs say to cast to int and compare to 32. -	HWND our_window = NULL; -	LPCWSTR directory_wstr = NULL; -	int retval = (int) ShellExecute(our_window, 	// Flawfinder: ignore -									L"open",  -									browser_exec_utf16.c_str(),  -									url_utf16.c_str(),  -									directory_wstr, -									SW_SHOWNORMAL); -	if (retval > 32) -	{ -		LL_DEBUGS("Window") << "load_url success with " << retval << LL_ENDL; -	} -	else -	{ -		LL_INFOS("Window") << "load_url failure with " << retval << LL_ENDL; -	} -	*/  }  /* diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 47fde08a9d..e0a31875c8 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -7,6 +7,7 @@ include(Boost)  include(BuildVersion)  include(DBusGlib)  include(DirectX) +include(OpenSSL)  include(DragDrop)  include(ELFIO)  include(FMOD) @@ -371,6 +372,8 @@ set(viewer_SOURCE_FILES      llscrollingpanelparam.cpp      llsearchcombobox.cpp      llsearchhistory.cpp +    llsecapi.cpp +    llsechandler_basic.cpp      llselectmgr.cpp      llsidepanelappearance.cpp      llsidepanelinventory.cpp @@ -445,7 +448,6 @@ set(viewer_SOURCE_FILES      llurldispatcherlistener.cpp      llurlhistory.cpp      llurllineeditorctrl.cpp -    llurlsimstring.cpp      llurlwhitelist.cpp      llvectorperfoptions.cpp      llversioninfo.cpp @@ -512,7 +514,9 @@ set(viewer_SOURCE_FILES      llvoground.cpp      llvoicechannel.cpp      llvoiceclient.cpp +    llvoicedw.cpp      llvoicevisualizer.cpp +    llvoicevivox.cpp      llvoinventorylistener.cpp      llvopartgroup.cpp      llvosky.cpp @@ -872,6 +876,8 @@ set(viewer_HEADER_FILES      llscrollingpanelparam.h      llsearchcombobox.h      llsearchhistory.h +    llsecapi.h +    llsechandler_basic.h      llselectmgr.h      llsidepanelappearance.h      llsidepanelinventory.h @@ -948,7 +954,6 @@ set(viewer_HEADER_FILES      llurldispatcherlistener.h      llurlhistory.h      llurllineeditorctrl.h -    llurlsimstring.h      llurlwhitelist.h      llvectorperfoptions.h      llversioninfo.h @@ -1012,7 +1017,9 @@ set(viewer_HEADER_FILES      llvoground.h      llvoicechannel.h      llvoiceclient.h +    llvoicedw.h      llvoicevisualizer.h +    llvoicevivox.h      llvoinventorylistener.h      llvopartgroup.h      llvosky.h @@ -1403,8 +1410,8 @@ if (WINDOWS)          ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libtcmalloc_minimal.dll          ${SHARED_LIB_STAGING_DIR}/Debug/libtcmalloc_minimal-debug.dll          ) -    endif(USE_GOOGLE_PERFTOOLS) -   +     endif(USE_GOOGLE_PERFTOOLS) +       set(COPY_INPUT_DEPENDECIES        # The following commented dependencies are determined at variably at build time. Can't do this here. @@ -1621,6 +1628,8 @@ target_link_libraries(${VIEWER_BINARY_NAME}      ${WINDOWS_LIBRARIES}      ${XMLRPCEPI_LIBRARIES}      ${ELFIO_LIBRARIES} +    ${OPENSSL_LIBRARIES} +    ${CRYPTO_LIBRARIES}      ${LLLOGIN_LIBRARIES}      ${GOOGLE_PERFTOOLS_LIBRARIES}      ) @@ -1797,6 +1806,43 @@ if (LL_TESTS)      "${CMAKE_SOURCE_DIR}/llmessage/tests/test_llsdmessage_peer.py"      ) +  set(test_libs  +    ${LLMESSAGE_LIBRARIES}  +    ${WINDOWS_LIBRARIES}  +    ${LLVFS_LIBRARIES} +    ${LLMATH_LIBRARIES} +    ${LLCOMMON_LIBRARIES}  +    ${GOOGLEMOCK_LIBRARIES} +    ${OPENSSL_LIBRARIES} +    ${CRYPTO_LIBRARIES} +  ) + +    LL_ADD_INTEGRATION_TEST(llsechandler_basic +     llsechandler_basic.cpp +    "${test_libs}" +    ) + +  LL_ADD_INTEGRATION_TEST(llsecapi +     llsecapi.cpp +    "${test_libs}" +    ) + +  set(llslurl_test_sources +      llslurl.cpp +      llviewernetwork.cpp +  ) + + +  LL_ADD_INTEGRATION_TEST(llslurl +     "${llslurl_test_sources}" +    "${test_libs}" +    ) + +  LL_ADD_INTEGRATION_TEST(llviewernetwork +     llviewernetwork.cpp +    "${test_libs}" +    ) +    #ADD_VIEWER_BUILD_TEST(llmemoryview viewer)    #ADD_VIEWER_BUILD_TEST(llagentaccess viewer)    #ADD_VIEWER_BUILD_TEST(llworldmap viewer) @@ -1804,6 +1850,7 @@ if (LL_TESTS)    #ADD_VIEWER_BUILD_TEST(lltextureinfo viewer)    #ADD_VIEWER_BUILD_TEST(lltextureinfodetails viewer)    #ADD_VIEWER_BUILD_TEST(lltexturestatsuploader viewer) +  endif (LL_TESTS) diff --git a/indra/newview/Info-SecondLife.plist b/indra/newview/Info-SecondLife.plist index 4cb01a0f33..7436c5642e 100644 --- a/indra/newview/Info-SecondLife.plist +++ b/indra/newview/Info-SecondLife.plist @@ -18,6 +18,33 @@  	<string>APPL</string>  	<key>CFBundleSignature</key>  	<string>????</string> +        <key>CFBundleDocumentTypes</key> +        <array> +                <dict> +                        <key>CFBundleTypeExtensions</key> +                        <array> +                                <string>slurl</string> +                        </array> +                        <key>CFBundleTypeIconFile</key> +                        <string>seconlife</string> +                        <key>CFBundleTypeMIMETypes</key> +                        <array> +                                <string>application/x-grid-location-info</string> +                        </array> +                        <key>CFBundleTypeName</key> +                        <string>Secondlife SLURL</string> +			<key>CFBundleTypeOSTypes</key> +			<array> +			  <string>SLRL</string> +			</array> +                        <key>CFBundleTypeRole</key> +                        <string>Viewer</string> +                        <key>LSTypeIsPackage</key> +			<true/> +                        <key>NSDocumentClass</key> +                        <string>SecondLifeSLURL</string> +                </dict> +        </array>  	<key>CFBundleURLTypes</key>  	<array>  		<dict> @@ -26,6 +53,7 @@  			<key>CFBundleURLSchemes</key>  			<array>  				<string>secondlife</string> +				<string>x-grid-location-info</string>  			</array>  			<key>LSIsAppleDefaultForScheme</key>  			<true/> diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 92226f4148..442cd5d31e 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -1264,6 +1264,17 @@        <key>Value</key>        <integer>0</integer>      </map> +    <key>CertStore</key> +    <map> +      <key>Comment</key> +      <string>Specifies the Certificate Store for certificate trust verification</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>String</string> +      <key>Value</key> +      <string>default</string> +    </map>      <key>ChatBarStealsFocus</key>      <map>        <key>Comment</key> @@ -1640,6 +1651,17 @@        <key>Value</key>        <integer>1</integer>      </map> +    <key>CurrentGrid</key> +    <map> +      <key>Comment</key> +      <string>Currently Selected Grid</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>String</string> +      <key>Value</key> +      <string></string> +    </map>      <key>CustomServer</key>      <map>        <key>Comment</key> @@ -2366,6 +2388,29 @@        <key>Value</key>        <integer>0</integer>      </map> +	<key>DefaultFemaleAvatar</key> +	<map> +	  <key>Comment</key> +	  <string>Default Female Avatar</string> +	  <key>Persist</key> +	  <integer>1</integer> +	  <key>Type</key> +	  <string>String</string> +	  <key>Value</key> +	  <string>Female Shape & Outfit</string> +	</map> +	<key>DefaultMaleAvatar</key> +	<map> +	  <key>Comment</key> +	  <string>Default Male Avatar</string> +	  <key>Persist</key> +	  <integer>1</integer> +	  <key>Type</key> +	  <string>String</string> +	  <key>Value</key> +	  <string>Male Shape & Outfit</string> +	</map> +      <key>DefaultObjectTexture</key>      <map>        <key>Comment</key> @@ -2564,6 +2609,17 @@        <key>Value</key>        <real>0.10000000149</real>      </map> +    <key>DragAndDropDistanceThreshold</key> +    <map> +      <key>Comment</key> +      <string>Number of pixels that mouse should move before triggering drag and drop mode</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>S32</string> +      <key>Value</key> +      <integer>3</integer> +    </map>      <key>DropShadowButton</key>      <map>        <key>Comment</key> @@ -3431,7 +3487,7 @@        <key>Type</key>        <string>Boolean</string>        <key>Value</key> -      <integer>0</integer> +      <integer>1</integer>      </map>      <key>ForceMandatoryUpdate</key>      <map> @@ -7685,6 +7741,17 @@        <key>Value</key>        <integer>0</integer>      </map> +    <key>SecondLifeEnterprise</key> +    <map> +      <key>Comment</key> +      <string>Enables Second Life Enterprise features</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>Boolean</string> +      <key>Value</key> +      <integer>0</integer> +    </map>	      <key>SelectMovableOnly</key>      <map>        <key>Comment</key> @@ -8489,7 +8556,7 @@        <key>Type</key>        <string>Boolean</string>        <key>Value</key> -      <integer>0</integer> +      <integer>1</integer>      </map>      <key>ShowTangentBasis</key>      <map> @@ -8653,17 +8720,6 @@        <key>Value</key>        <integer>1</integer>      </map> -    <key>SmallAvatarNames</key> -    <map> -      <key>Comment</key> -      <string>Display avatar name text in smaller font</string> -      <key>Persist</key> -      <integer>1</integer> -      <key>Type</key> -      <string>Boolean</string> -      <key>Value</key> -      <integer>1</integer> -    </map>      <key>SnapEnabled</key>      <map>        <key>Comment</key> @@ -10393,6 +10449,17 @@        <key>Value</key>        <string></string>      </map> +    <key>VivoxDebugSIPURIHostName</key> +    <map> +      <key>Comment</key> +      <string>Hostname portion of vivox SIP URIs (empty string for the default).</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>String</string> +      <key>Value</key> +      <string></string> +    </map>      <key>VivoxDebugVoiceAccountServerURI</key>      <map>        <key>Comment</key> @@ -10404,6 +10471,28 @@        <key>Value</key>        <string></string>      </map> +    <key>VivoxVoiceHost</key> +    <map> +      <key>Comment</key> +      <string>Client SLVoice host to connect to</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>String</string> +      <key>Value</key> +      <string>127.0.0.1</string> +    </map> +    <key>VivoxVoicePort</key> +    <map> +      <key>Comment</key> +      <string>Client SLVoice port to connect to</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>U32</string> +      <key>Value</key> +      <integer>44125</integer> +    </map>      <key>VoiceCallsFriendsOnly</key>      <map>        <key>Comment</key> @@ -10536,6 +10625,17 @@        <key>Value</key>        <string>Default</string>      </map> +    <key>VoiceLogFile</key> +    <map> +      <key>Comment</key> +      <string>Log file to use when launching the voice daemon</string> +      <key>Persist</key> +      <integer>1</integer> +      <key>Type</key> +      <string>String</string> +      <key>Value</key> +      <string></string> +    </map>      <key>VoiceOutputAudioDevice</key>      <map>        <key>Comment</key> @@ -10580,6 +10680,17 @@        <key>Value</key>        <integer>0</integer>      </map> +    <key>VoiceServerType</key> +    <map> +      <key>Comment</key> +      <string>The type of voice server to connect to.</string> +      <key>Persist</key> +      <integer>0</integer> +      <key>Type</key> +      <string>String</string> +      <key>Value</key> +      <string>vivox</string> +    </map>      <key>WLSkyDetail</key>      <map>        <key>Comment</key> diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi index a7322749ca..b7b4c54001 100644 --- a/indra/newview/installers/windows/installer_template.nsi +++ b/indra/newview/installers/windows/installer_template.nsi @@ -797,6 +797,12 @@ WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}\DefaultIcon" "" '"$INSTDIR\$INSTEXE"'  ;; URL param must be last item passed to viewer, it ignores subsequent params  ;; to avoid parameter injection attacks.  WriteRegExpandStr HKEY_CLASSES_ROOT "${URLNAME}\shell\open\command" "" '"$INSTDIR\$INSTEXE" $INSTFLAGS -url "%1"' +WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info"(default)" "URL:Second Life" +WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info" "URL Protocol" "" +WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info\DefaultIcon" "" '"$INSTDIR\$INSTEXE"' +;; URL param must be last item passed to viewer, it ignores subsequent params +;; to avoid parameter injection attacks. +WriteRegExpandStr HKEY_CLASSES_ROOT "x-grid-location-info\shell\open\command" "" '"$INSTDIR\$INSTEXE" $INSTFLAGS -url "%1"'  ; write out uninstaller  WriteUninstaller "$INSTDIR\uninst.exe" diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index f434782977..37d1bd15e1 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -3231,7 +3231,7 @@ bool LLAgent::teleportCore(bool is_local)  	// MBW -- Let the voice client know a teleport has begun so it can leave the existing channel.  	// This was breaking the case of teleporting within a single sim.  Backing it out for now. -//	gVoiceClient->leaveChannel(); +//	LLVoiceClient::getInstance()->leaveChannel();  	return true;  } @@ -3375,7 +3375,7 @@ void LLAgent::setTeleportState(ETeleportState state)  	if (mTeleportState == TELEPORT_MOVING)  	{  		// We're outa here. Save "back" slurl. -		mTeleportSourceSLURL = LLAgentUI::buildSLURL(); +		LLAgentUI::buildSLURL(mTeleportSourceSLURL);  	}  	else if(mTeleportState == TELEPORT_ARRIVING)  	{ diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index a460077b7e..32f9b00135 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -42,6 +42,7 @@  #include "llpointer.h"  #include "lluicolor.h"  #include "llvoavatardefines.h" +#include "llslurl.h"  extern const BOOL 	ANIMATE;  extern const U8 	AGENT_STATE_TYPING;  // Typing indication @@ -514,13 +515,13 @@ public:  public:  	static void 	parseTeleportMessages(const std::string& xml_filename); -	const std::string getTeleportSourceSLURL() const { return mTeleportSourceSLURL; } +	const void getTeleportSourceSLURL(LLSLURL& slurl) const { slurl = mTeleportSourceSLURL; }  public:  	// ! TODO ! Define ERROR and PROGRESS enums here instead of exposing the mappings.  	static std::map<std::string, std::string> sTeleportErrorMessages;  	static std::map<std::string, std::string> sTeleportProgressMessages;  private: -	std::string		mTeleportSourceSLURL; 			// SLURL where last TP began +	LLSLURL	mTeleportSourceSLURL; 			// SLURL where last TP began  	//--------------------------------------------------------------------  	// Teleport Actions diff --git a/indra/newview/llagentcamera.cpp b/indra/newview/llagentcamera.cpp index bb06255fd1..9638d0e94f 100644 --- a/indra/newview/llagentcamera.cpp +++ b/indra/newview/llagentcamera.cpp @@ -107,6 +107,8 @@ LLAgentCamera gAgentCamera;  // LLAgentCamera()  //-----------------------------------------------------------------------------  LLAgentCamera::LLAgentCamera() : +	mInitialized(false), +  	mDrawDistance( DEFAULT_FAR_PLANE ),  	mLookAt(NULL), @@ -142,7 +144,7 @@ LLAgentCamera::LLAgentCamera() :  	mSitCameraEnabled(FALSE),  	mCameraSmoothingLastPositionGlobal(),  	mCameraSmoothingLastPositionAgent(), -	mCameraSmoothingStop(FALSE), +	mCameraSmoothingStop(false),  	mCameraUpVector(LLVector3::z_axis), // default is straight up @@ -219,7 +221,7 @@ void LLAgentCamera::init()  	mCameraZoomFraction = 1.f;  	mTrackFocusObject = gSavedSettings.getBOOL("TrackFocusObject"); -	mInitialized = TRUE; +	mInitialized = true;  }  //----------------------------------------------------------------------------- @@ -1042,7 +1044,7 @@ void LLAgentCamera::cameraPanLeft(F32 meters)  	mFocusGlobal = mFocusTargetGlobal;  	// disable smoothing for camera pan, which causes some residents unhappiness -	mCameraSmoothingStop = TRUE; +	mCameraSmoothingStop = true;  	cameraZoomIn(1.f);  	updateFocusOffset(); @@ -1062,7 +1064,7 @@ void LLAgentCamera::cameraPanUp(F32 meters)  	mFocusGlobal = mFocusTargetGlobal;  	// disable smoothing for camera pan, which causes some residents unhappiness -	mCameraSmoothingStop = TRUE; +	mCameraSmoothingStop = true;  	cameraZoomIn(1.f);  	updateFocusOffset(); @@ -1122,9 +1124,9 @@ void LLAgentCamera::updateLookAt(const S32 mouse_x, const S32 mouse_y)  		{  			// range from -.5 to .5  			F32 x_from_center =  -				((F32) mouse_x / (F32) gViewerWindow->getWindowWidthScaled() ) - 0.5f; +				((F32) mouse_x / (F32) gViewerWindow->getWorldViewWidthScaled() ) - 0.5f;  			F32 y_from_center =  -				((F32) mouse_y / (F32) gViewerWindow->getWindowHeightScaled() ) - 0.5f; +				((F32) mouse_y / (F32) gViewerWindow->getWorldViewHeightScaled() ) - 0.5f;  			frameCamera.yaw( - x_from_center * gSavedSettings.getF32("YawFromMousePosition") * DEG_TO_RAD);  			frameCamera.pitch( - y_from_center * gSavedSettings.getF32("PitchFromMousePosition") * DEG_TO_RAD); @@ -1364,7 +1366,8 @@ void LLAgentCamera::updateCamera()  		LLVector3d camera_pos_agent = camera_pos_global - agent_pos;  		// Sitting on what you're manipulating can cause camera jitter with smoothing.   		// This turns off smoothing while editing. -MG -		mCameraSmoothingStop |= (BOOL)LLToolMgr::getInstance()->inBuildMode(); +		bool in_build_mode = LLToolMgr::getInstance()->inBuildMode(); +		mCameraSmoothingStop = mCameraSmoothingStop || in_build_mode;  		if (cameraThirdPerson() && !mCameraSmoothingStop)  		{ @@ -1396,7 +1399,7 @@ void LLAgentCamera::updateCamera()  		mCameraSmoothingLastPositionGlobal = camera_pos_global;  		mCameraSmoothingLastPositionAgent = camera_pos_agent; -		mCameraSmoothingStop = FALSE; +		mCameraSmoothingStop = false;  	} diff --git a/indra/newview/llagentcamera.h b/indra/newview/llagentcamera.h index 3ba24ef32b..5cbb1de6f4 100644 --- a/indra/newview/llagentcamera.h +++ b/indra/newview/llagentcamera.h @@ -82,7 +82,7 @@ public:  	void			cleanup();  	void		    setAvatarObject(LLVOAvatarSelf* avatar);  private: -	BOOL			mInitialized; +	bool			mInitialized;  	//-------------------------------------------------------------------- @@ -151,7 +151,7 @@ private:  	LLVector3		mCameraVirtualPositionAgent;	// Camera virtual position (target) before performing FOV zoom  	LLVector3d      mCameraSmoothingLastPositionGlobal;      	LLVector3d      mCameraSmoothingLastPositionAgent; -	BOOL            mCameraSmoothingStop; +	bool            mCameraSmoothingStop;  	LLVector3		mCameraLag;						// Third person camera lag  	LLVector3		mCameraUpVector;				// Camera's up direction in world coordinates (determines the 'roll' of the view) diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index b3ed7c353e..7a8205acb5 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -53,7 +53,10 @@ void LLAgentListener::requestTeleport(LLSD const & event_data) const  	}  	else  	{ -		std::string url = LLSLURL::buildSLURL(event_data["regionname"], event_data["x"], event_data["y"], event_data["z"]); +		std::string url = LLSLURL(event_data["regionname"],  +								  LLVector3(event_data["x"].asReal(),  +											event_data["y"].asReal(),  +											event_data["z"].asReal())).getSLURLString();  		LLURLDispatcher::dispatch(url, NULL, false);  	}  } diff --git a/indra/newview/llagentui.cpp b/indra/newview/llagentui.cpp index c4597ad6f8..15d9f36b74 100644 --- a/indra/newview/llagentui.cpp +++ b/indra/newview/llagentui.cpp @@ -76,16 +76,15 @@ void LLAgentUI::buildFullname(std::string& name)  }  //static -std::string LLAgentUI::buildSLURL(const bool escaped /*= true*/) +void LLAgentUI::buildSLURL(LLSLURL& slurl, const bool escaped /*= true*/)  { -	std::string slurl; -	LLViewerRegion *regionp = gAgent.getRegion(); -	if (regionp) -	{ -		LLVector3d agentPos = gAgent.getPositionGlobal(); -		slurl = LLSLURL::buildSLURLfromPosGlobal(regionp->getName(), agentPos, escaped); -	} -	return slurl; +      LLSLURL return_slurl; +      LLViewerRegion *regionp = gAgent.getRegion(); +      if (regionp) +      { +		  return_slurl = LLSLURL(regionp->getName(), gAgent.getPositionGlobal()); +      } +	slurl = return_slurl;  }  //static diff --git a/indra/newview/llagentui.h b/indra/newview/llagentui.h index 3478793e38..577b752fbe 100644 --- a/indra/newview/llagentui.h +++ b/indra/newview/llagentui.h @@ -33,6 +33,8 @@  #ifndef LLAGENTUI_H  #define LLAGENTUI_H +class LLSLURL; +  class LLAgentUI  {  public: @@ -48,7 +50,7 @@ public:  	static void buildName(std::string& name);  	static void buildFullname(std::string &name); -	static std::string buildSLURL(const bool escaped = true); +	static void buildSLURL(LLSLURL& slurl, const bool escaped = true);  	//build location string using the current position of gAgent.  	static BOOL buildLocationString(std::string& str, ELocationFormat fmt = LOCATION_FORMAT_LANDMARK);  	//build location string using a region position of the avatar.  diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index 2bc6d28daa..0542e73bfd 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -1524,7 +1524,6 @@ void LLAgentWearables::removeWearableFinal(const EWearableType type, bool do_rem  }  // Assumes existing wearables are not dirty. -// MULTI_WEARABLE: assumes one wearable per type.  void LLAgentWearables::setWearableOutfit(const LLInventoryItem::item_array_t& items,  										 const LLDynamicArray< LLWearable* >& wearables,  										 BOOL remove) @@ -1782,16 +1781,24 @@ void LLAgentWearables::queryWearableCache()  	gAgentQueryManager.mWearablesCacheQueryID++;  } -// MULTI_WEARABLE: need a way to specify by wearable rather than by type.  // User has picked "remove from avatar" from a menu.  // static -void LLAgentWearables::userRemoveWearable(EWearableType& type) +void LLAgentWearables::userRemoveWearable(const EWearableType &type, const U32 &index)  { -	if (!(type==WT_SHAPE || type==WT_SKIN || type==WT_HAIR)) //&& +	if (!(type==WT_SHAPE || type==WT_SKIN || type==WT_HAIR || type==WT_EYES)) //&&  		//!((!gAgent.isTeen()) && (type==WT_UNDERPANTS || type==WT_UNDERSHIRT)))  	{ -		// MULTI_WEARABLE: fixed to 0th for now. -		gAgentWearables.removeWearable(type,false,0); +		gAgentWearables.removeWearable(type,false,index); +	} +} + +//static  +void LLAgentWearables::userRemoveWearablesOfType(const EWearableType &type) +{ +	if (!(type==WT_SHAPE || type==WT_SKIN || type==WT_HAIR || type==WT_EYES)) //&& +		//!((!gAgent.isTeen()) && (type==WT_UNDERPANTS || type==WT_UNDERSHIRT))) +	{ +		gAgentWearables.removeWearable(type,true,0);  	}  } diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h index 652ffd4587..9f8aadeae7 100644 --- a/indra/newview/llagentwearables.h +++ b/indra/newview/llagentwearables.h @@ -193,8 +193,8 @@ public:  	// Static UI hooks  	//--------------------------------------------------------------------  public: -	// MULTI-WEARABLE: assuming one wearable per type.  Need upstream changes. -	static void		userRemoveWearable(EWearableType& type); +	static void		userRemoveWearable(const EWearableType &type, const U32 &index); +	static void		userRemoveWearablesOfType(const EWearableType &type);  	static void		userRemoveAllClothes();	  	typedef std::vector<LLViewerObject*> llvo_vec_t; diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 43c8c679c6..2a355474b1 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -153,7 +153,7 @@  #include "llworld.h"  #include "llhudeffecttrail.h"  #include "llvectorperfoptions.h" -#include "llurlsimstring.h" +#include "llslurl.h"  #include "llwatchdog.h"  // Included so that constants/settings might be initialized @@ -193,6 +193,9 @@  #include "llparcel.h"  #include "llavatariconctrl.h" +// Include for security api initialization +#include "llsecapi.h" +  // *FIX: These extern globals should be cleaned up.  // The globals either represent state/config/resource-storage of either   // this app, or another 'component' of the viewer. App globals should be  @@ -507,35 +510,6 @@ public:  	}  }; -void LLAppViewer::initGridChoice() -{ -	// Load	up the initial grid	choice from: -	//	- hard coded defaults... -	//	- command line settings... -	//	- if dev build,	persisted settings... - -	// Set the "grid choice", this is specified	by command line. -	std::string	grid_choice	= gSavedSettings.getString("CmdLineGridChoice"); -	LLViewerLogin::getInstance()->setGridChoice(grid_choice); - -	// Load last server choice by default  -	// ignored if the command line grid	choice has been	set -	if(grid_choice.empty()) -	{ -		S32	server = gSavedSettings.getS32("ServerChoice"); -		server = llclamp(server, 0,	(S32)GRID_INFO_COUNT - 1); -		if(server == GRID_INFO_OTHER) -		{ -			std::string custom_server = gSavedSettings.getString("CustomServer"); -			LLViewerLogin::getInstance()->setGridChoice(custom_server); -		} -		else if(server != (S32)GRID_INFO_NONE) -		{ -			LLViewerLogin::getInstance()->setGridChoice((EGridInfo)server); -		} -	} -} -  //virtual  bool LLAppViewer::initSLURLHandler()  { @@ -647,7 +621,6 @@ bool LLAppViewer::init()      LLCurl::initClass();      initThreads(); -      writeSystemInfo();  	// Build a string representing the current version number. @@ -777,10 +750,6 @@ bool LLAppViewer::init()  		return false;  	} -	// Always fetch the Ethernet MAC address, needed both for login -	// and password load. -	LLUUID::getNodeID(gMACAddress); -  	// Prepare for out-of-memory situations, during which we will crash on  	// purpose and save a dump.  #if LL_WINDOWS && LL_RELEASE_FOR_DOWNLOAD && LL_USE_SMARTHEAP @@ -892,6 +861,7 @@ bool LLAppViewer::init()  		}  	} +  	// save the graphics card  	gDebugInfo["GraphicsCard"] = LLFeatureManager::getInstance()->getGPUString(); @@ -902,6 +872,17 @@ bool LLAppViewer::init()  	gSimFrames = (F32)gFrameCount;  	LLViewerJoystick::getInstance()->init(false); + +	try { +		initializeSecHandler(); +	} +	catch (LLProtectedDataException ex) +	{ +	  LLNotificationsUtil::add("CorruptedProtectedDataStore"); +	} +	LLHTTPClient::setCertVerifyCallback(secapiSSLCertVerifyCallback); + +  	gGLActive = FALSE;  	if (gSavedSettings.getBOOL("QAMode") && gSavedSettings.getS32("QAModeEventHostPort") > 0)  	{ @@ -936,13 +917,11 @@ bool LLAppViewer::mainLoop()  	gServicePump = new LLPumpIO(gAPRPoolp);  	LLHTTPClient::setPump(*gServicePump);  	LLCurl::setCAFile(gDirUtilp->getCAFile()); -	LLCurl::setSSLVerify(! gSavedSettings.getBOOL("NoVerifySSLCert")); -	 +  	// Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated.  	LLVoiceChannel::initClass(); -	LLVoiceClient::init(gServicePump); - +	LLVoiceClient::getInstance()->init(gServicePump);  	LLTimer frameTimer,idleTimer;  	LLTimer debugTime;  	LLViewerJoystick* joystick(LLViewerJoystick::getInstance()); @@ -1273,7 +1252,7 @@ bool LLAppViewer::cleanup()  	// to ensure shutdown order  	LLMortician::setZealous(TRUE); -	LLVoiceClient::terminate(); +	LLVoiceClient::getInstance()->terminate();  	disconnectViewer(); @@ -1472,13 +1451,6 @@ bool LLAppViewer::cleanup()  	llinfos << "Saving Data" << llendflush; -	// Quitting with "Remember Password" turned off should always stomp your -	// saved password, whether or not you successfully logged in.  JC -	if (!gSavedSettings.getBOOL("RememberPassword")) -	{ -		LLStartUp::deletePasswordFromDisk(); -	} -	  	// Store the time of our current logoff  	gSavedPerAccountSettings.setU32("LastLogoff", time_corrected()); @@ -2047,7 +2019,6 @@ bool LLAppViewer::initConfiguration()          }      } -    initGridChoice();  	// If we have specified crash on startup, set the global so we'll trigger the crash at the right time  	if(clp.hasOption("crashonstartup")) @@ -2141,30 +2112,17 @@ bool LLAppViewer::initConfiguration()      // injection and steal passwords. Phoenix. SL-55321      if(clp.hasOption("url"))      { -        std::string slurl = clp.getOption("url")[0]; -        if (LLSLURL::isSLURLCommand(slurl)) -        { -	        LLStartUp::sSLURLCommand = slurl; -        } -        else -        { -	        LLURLSimString::setString(slurl); -        } +		LLStartUp::setStartSLURL(LLSLURL(clp.getOption("url")[0])); +		if(LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION)  +		{   +			LLGridManager::getInstance()->setGridChoice(LLStartUp::getStartSLURL().getGrid()); +			 +		}        }      else if(clp.hasOption("slurl"))      { -        std::string slurl = clp.getOption("slurl")[0]; -        if(LLSLURL::isSLURL(slurl)) -        { -            if (LLSLURL::isSLURLCommand(slurl)) -            { -	            LLStartUp::sSLURLCommand = slurl; -            } -            else -            { -	            LLURLSimString::setString(slurl); -            } -        } +		LLSLURL start_slurl(clp.getOption("slurl")[0]); +		LLStartUp::setStartSLURL(start_slurl);      }      const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent"); @@ -2245,18 +2203,10 @@ bool LLAppViewer::initConfiguration()  	// don't call anotherInstanceRunning() when doing URL handoff, as  	// it relies on checking a marker file which will not work when running  	// out of different directories -	std::string slurl; -	if (!LLStartUp::sSLURLCommand.empty()) -	{ -		slurl = LLStartUp::sSLURLCommand; -	} -	else if (LLURLSimString::parse()) -	{ -		slurl = LLURLSimString::getURL(); -	} -	if (!slurl.empty()) + +	if (LLStartUp::getStartSLURL().isValid())  	{ -		if (sendURLToOtherInstance(slurl)) +		if (sendURLToOtherInstance(LLStartUp::getStartSLURL().getSLURLString()))  		{  			// successfully handed off URL to existing instance, exit  			return false; @@ -2312,9 +2262,9 @@ bool LLAppViewer::initConfiguration()     	// need to do this here - need to have initialized global settings first  	std::string nextLoginLocation = gSavedSettings.getString( "NextLoginLocation" ); -	if ( nextLoginLocation.length() ) +	if ( !nextLoginLocation.empty() )  	{ -		LLURLSimString::setString( nextLoginLocation ); +		LLStartUp::setStartSLURL(LLSLURL(nextLoginLocation));  	};  	gLastRunVersion = gSavedSettings.getString("LastRunVersion"); @@ -2535,7 +2485,7 @@ void LLAppViewer::writeSystemInfo()  	// The user is not logged on yet, but record the current grid choice login url  	// which may have been the intended grid. This can b -	gDebugInfo["GridName"] = LLViewerLogin::getInstance()->getGridLabel(); +	gDebugInfo["GridName"] = LLGridManager::getInstance()->getGridLabel();  	// *FIX:Mani - move this ddown in llappviewerwin32  #ifdef LL_WINDOWS @@ -3886,7 +3836,7 @@ void LLAppViewer::sendLogoutRequest()  		gLogoutMaxTime = LOGOUT_REQUEST_TIME;  		mLogoutRequestSent = TRUE; -		gVoiceClient->leaveChannel(); +		LLVoiceClient::getInstance()->leaveChannel();  		//Set internal status variables and marker files  		gLogoutInProgress = TRUE; @@ -4306,7 +4256,7 @@ void LLAppViewer::launchUpdater()  #endif  	// *TODO change userserver to be grid on both viewer and sim, since  	// userserver no longer exists. -	query_map["userserver"] = LLViewerLogin::getInstance()->getGridLabel(); +	query_map["userserver"] = LLGridManager::getInstance()->getGridLabel();  	query_map["channel"] = gSavedSettings.getString("VersionChannelName");  	// *TODO constantize this guy  	// *NOTE: This URL is also used in win_setup/lldownloader.cpp @@ -4319,10 +4269,10 @@ void LLAppViewer::launchUpdater()  	LLAppViewer::sUpdaterInfo = new LLAppViewer::LLUpdaterInfo() ;  	// if a sim name was passed in via command line parameter (typically through a SLURL) -	if ( LLURLSimString::sInstance.mSimString.length() ) +	if ( LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION )  	{  		// record the location to start at next time -		gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString );  +		gSavedSettings.setString( "NextLoginLocation", LLStartUp::getStartSLURL().getSLURLString());   	};  #if LL_WINDOWS diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index a915b7fa50..27e8bec1d5 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -191,7 +191,6 @@ private:  	bool initThreads(); // Initialize viewer threads, return false on failure.  	bool initConfiguration(); // Initialize settings from the command line/config file. -	void initGridChoice();  	bool initCache(); // Initialize local client cache. diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp index d34bcb4a68..78b0f7ba83 100644 --- a/indra/newview/llappviewerlinux.cpp +++ b/indra/newview/llappviewerlinux.cpp @@ -604,7 +604,7 @@ void LLAppViewerLinux::handleCrashReporting(bool reportFreeze)  				{cmd.c_str(),  				 ask_dialog,  				 "-user", -				 (char*)LLViewerLogin::getInstance()->getGridLabel().c_str(), +				 (char*)LLGridManager::getInstance()->getGridLabel().c_str(),  				 "-name",  				 LLAppViewer::instance()->getSecondLifeTitle().c_str(),  				 NULL}; diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp index 80d9b14345..58d28883c6 100644 --- a/indra/newview/llappviewermacosx.cpp +++ b/indra/newview/llappviewermacosx.cpp @@ -44,7 +44,6 @@  #include "llviewernetwork.h"  #include "llviewercontrol.h"  #include "llmd5.h" -#include "llurlsimstring.h"  #include "llfloaterworldmap.h"  #include "llurldispatcher.h"  #include <Carbon/Carbon.h> diff --git a/indra/newview/llavataractions.cpp b/indra/newview/llavataractions.cpp index c85c72837c..48a85dc73f 100644 --- a/indra/newview/llavataractions.cpp +++ b/indra/newview/llavataractions.cpp @@ -282,7 +282,7 @@ bool LLAvatarActions::isCalling(const LLUUID &id)  //static  bool LLAvatarActions::canCall()  { -		return LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking(); +		return LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking();  }  // static diff --git a/indra/newview/llbottomtray.cpp b/indra/newview/llbottomtray.cpp index 41bee540fc..9824f59358 100644 --- a/indra/newview/llbottomtray.cpp +++ b/indra/newview/llbottomtray.cpp @@ -319,7 +319,7 @@ void LLBottomTray::onChange(EStatusType status, const std::string &channelURI, b  	// skipped to avoid button blinking  	if (status != STATUS_JOINING && status!= STATUS_LEFT_CHANNEL)  	{ -		mSpeakBtn->setFlyoutBtnEnabled(LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking()); +		mSpeakBtn->setFlyoutBtnEnabled(LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking());  	}  } @@ -489,7 +489,7 @@ BOOL LLBottomTray::postBuild()  	mSpeakBtn->setShowToolTip( getString("VoiceControlBtnToolTip") );  	// Registering Chat Bar to receive Voice client status change notifications. -	gVoiceClient->addObserver(this); +	LLVoiceClient::getInstance()->addObserver(this);  	mObjectDefaultWidthMap[RS_BUTTON_GESTURES] = mGesturePanel->getRect().getWidth();  	mObjectDefaultWidthMap[RS_BUTTON_MOVEMENT] = mMovementPanel->getRect().getWidth(); diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp index 4ea3c61ab2..132e4f0933 100644 --- a/indra/newview/llcallfloater.cpp +++ b/indra/newview/llcallfloater.cpp @@ -131,11 +131,9 @@ LLCallFloater::~LLCallFloater()  	mAvatarListRefreshConnection.disconnect();  	mVoiceChannelStateChangeConnection.disconnect(); -	// Don't use LLVoiceClient::getInstance() here  -	// singleton MAY have already been destroyed. -	if(gVoiceClient) +	if(LLVoiceClient::instanceExists())  	{ -		gVoiceClient->removeObserver(this); +		LLVoiceClient::getInstance()->removeObserver(this);  	}  	LLTransientFloaterMgr::getInstance()->removeControlView(this);  } @@ -191,7 +189,7 @@ void LLCallFloater::draw()  	// Seems this is a problem somewhere in Voice Client (LLVoiceClient::participantAddedEvent)  //	onChange(); -	bool is_moderator_muted = gVoiceClient->getIsModeratorMuted(gAgentID); +	bool is_moderator_muted = LLVoiceClient::getInstance()->getIsModeratorMuted(gAgentID);  	if (mIsModeratorMutedVoice != is_moderator_muted)  	{ @@ -209,7 +207,6 @@ void LLCallFloater::draw()  void LLCallFloater::onChange()  {  	if (NULL == mParticipants) return; -  	updateParticipantsVoiceState();  	// Add newly joined participants. @@ -239,11 +236,11 @@ void LLCallFloater::updateSession()  	LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel();  	if (voice_channel)  	{ -		lldebugs << "Current voice channel: " << voice_channel->getSessionID() << llendl; +		LL_DEBUGS("Voice") << "Current voice channel: " << voice_channel->getSessionID() << LL_ENDL;  		if (mSpeakerManager && voice_channel->getSessionID() == mSpeakerManager->getSessionID())  		{ -			lldebugs << "Speaker manager is already set for session: " << voice_channel->getSessionID() << llendl; +			LL_DEBUGS("Voice") << "Speaker manager is already set for session: " << voice_channel->getSessionID() << LL_ENDL;  			return;  		}  		else @@ -253,7 +250,6 @@ void LLCallFloater::updateSession()  	}  	const LLUUID& session_id = voice_channel ? voice_channel->getSessionID() : LLUUID::null; -	lldebugs << "Set speaker manager for session: " << session_id << llendl;  	LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(session_id);  	if (im_session) @@ -293,7 +289,7 @@ void LLCallFloater::updateSession()  	{  		// by default let show nearby chat participants  		mSpeakerManager = LLLocalSpeakerMgr::getInstance(); -		lldebugs << "Set DEFAULT speaker manager" << llendl; +		LL_DEBUGS("Voice") << "Set DEFAULT speaker manager" << LL_ENDL;  		mVoiceType = VC_LOCAL_CHAT;  	} @@ -472,16 +468,15 @@ void LLCallFloater::updateAgentModeratorState()  static void get_voice_participants_uuids(uuid_vec_t& speakers_uuids)  {  	// Get a list of participants from VoiceClient -	LLVoiceClient::participantMap *voice_map = gVoiceClient->getParticipantList(); -	if (voice_map) +       std::set<LLUUID> participants; +       LLVoiceClient::getInstance()->getParticipantList(participants); +	 +	for (std::set<LLUUID>::const_iterator iter = participants.begin(); +		 iter != participants.end(); ++iter)  	{ -		for (LLVoiceClient::participantMap::const_iterator iter = voice_map->begin(); -			iter != voice_map->end(); ++iter) -		{ -			LLUUID id = (*iter).second->mAvatarID; -			speakers_uuids.push_back(id); -		} +		speakers_uuids.push_back(*iter);  	} +  }  void LLCallFloater::initParticipantsVoiceState() @@ -557,7 +552,7 @@ void LLCallFloater::updateParticipantsVoiceState()  		uuid_vec_t::iterator speakers_iter = std::find(speakers_uuids.begin(), speakers_uuids.end(), participant_id); -		lldebugs << "processing speaker: " << item->getAvatarName() << ", " << item->getAvatarId() << llendl; +		LL_DEBUGS("Voice") << "processing speaker: " << item->getAvatarName() << ", " << item->getAvatarId() << LL_ENDL;  		// If an avatarID assigned to a panel is found in a speakers list  		// obtained from VoiceClient we assign the JOINED status to the owner @@ -727,7 +722,7 @@ void LLCallFloater::connectToChannel(LLVoiceChannel* channel)  void LLCallFloater::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state)  {  	// check is voice operational and if it doesn't work hide VCP (EXT-4397) -	if(LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking()) +	if(LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking())  	{  		updateState(new_state);  	} diff --git a/indra/newview/llcallingcard.cpp b/indra/newview/llcallingcard.cpp index 79a2631c31..1a6c11fa73 100644 --- a/indra/newview/llcallingcard.cpp +++ b/indra/newview/llcallingcard.cpp @@ -700,6 +700,7 @@ void LLAvatarTracker::processNotify(LLMessageSystem* msg, bool online)  						args["FIRST"] = first;  						args["LAST"] = last;  					} +  				}  			}  			else diff --git a/indra/newview/llchathistory.cpp b/indra/newview/llchathistory.cpp index 68c31d87fa..31feabe722 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -647,20 +647,19 @@ void LLChatHistory::appendMessage(const LLChat& chat, const LLSD &args, const LL  			if ( chat.mSourceType == CHAT_SOURCE_OBJECT && chat.mFromID.notNull())  			{  				// for object IMs, create a secondlife:///app/objectim SLapp -				std::string url = LLSLURL::buildCommand("objectim", chat.mFromID, ""); +				std::string url = LLSLURL("objectim", chat.mFromID, "").getSLURLString();  				url += "?name=" + chat.mFromName;  				url += "&owner=" + args["owner_id"].asString();  				std::string slurl = args["slurl"].asString();  				if (slurl.empty())  				{ -					LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosAgent(chat.mPosAgent); -					if (region) -					{ -						S32 x, y, z; -						LLSLURL::globalPosToXYZ(LLVector3d(chat.mPosAgent), x, y, z); -						slurl = region->getName() + llformat("/%d/%d/%d", x, y, z); -					} +				    LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosAgent(chat.mPosAgent); +				    if(region) +				      { +					LLSLURL region_slurl(region->getName(), chat.mPosAgent); +					slurl = region_slurl.getLocationString(); +				      }  				}  				url += "&slurl=" + slurl; diff --git a/indra/newview/llcurrencyuimanager.cpp b/indra/newview/llcurrencyuimanager.cpp index be6c15eab4..fd3df359bd 100644 --- a/indra/newview/llcurrencyuimanager.cpp +++ b/indra/newview/llcurrencyuimanager.cpp @@ -284,7 +284,7 @@ void LLCurrencyUIManager::Impl::startTransaction(TransactionType type,  	static std::string transactionURI;  	if (transactionURI.empty())  	{ -		transactionURI = LLViewerLogin::getInstance()->getHelperURI() + "currency.php"; +		transactionURI = LLGridManager::getInstance()->getHelperURI() + "currency.php";  	}  	delete mTransaction; diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp index ef69f39ad2..56bc4a7933 100644 --- a/indra/newview/llfloaterabout.cpp +++ b/indra/newview/llfloaterabout.cpp @@ -266,8 +266,18 @@ LLSD LLFloaterAbout::getInfo()  	info["J2C_VERSION"] = LLImageJ2C::getEngineInfo();  	bool want_fullname = true;  	info["AUDIO_DRIVER_VERSION"] = gAudiop ? LLSD(gAudiop->getDriverName(want_fullname)) : LLSD(); -	info["VIVOX_VERSION"] = gVoiceClient ? gVoiceClient->getAPIVersion() : LLTrans::getString("NotConnected"); - +	if(LLVoiceClient::getInstance()->voiceEnabled()) +	{ +		LLVoiceVersionInfo version = LLVoiceClient::getInstance()->getVersion(); +		std::ostringstream version_string; +		version_string << version.serverType << " " << version.serverVersion << std::endl; +		info["VOICE_VERSION"] = version_string.str(); +	} +	else  +	{ +		info["VOICE_VERSION"] = LLTrans::getString("NotConnected"); +	} +	  	// TODO: Implement media plugin version query  	info["QT_WEBKIT_VERSION"] = "4.6 (version number hard-coded)"; diff --git a/indra/newview/llfloateravatartextures.cpp b/indra/newview/llfloateravatartextures.cpp index 8c7899af3e..18db60705b 100644 --- a/indra/newview/llfloateravatartextures.cpp +++ b/indra/newview/llfloateravatartextures.cpp @@ -31,15 +31,14 @@   */  #include "llviewerprecompiledheaders.h" -  #include "llfloateravatartextures.h" +#include "llagent.h" +#include "llagentwearables.h"  #include "lltexturectrl.h" -  #include "lluictrlfactory.h"  #include "llviewerobjectlist.h"  #include "llvoavatar.h" -#include "llagentwearables.h"  using namespace LLVOAvatarDefines; @@ -75,7 +74,6 @@ void LLFloaterAvatarTextures::draw()  	LLFloater::draw();  } -#if !LL_RELEASE_FOR_DOWNLOAD  static void update_texture_ctrl(LLVOAvatar* avatarp,  								 LLTextureCtrl* ctrl,  								 ETextureIndex te) @@ -132,72 +130,68 @@ static LLVOAvatar* find_avatar(const LLUUID& id)  void LLFloaterAvatarTextures::refresh()  { -	LLVOAvatar *avatarp = find_avatar(mID); -	if (avatarp) +	if (gAgent.isGodlike())  	{ -		std::string fullname; -		if (gCacheName->getFullName(avatarp->getID(), fullname)) +		LLVOAvatar *avatarp = find_avatar(mID); +		if (avatarp)  		{ -			setTitle(mTitle + ": " + fullname); +			std::string fullname; +			if (gCacheName->getFullName(avatarp->getID(), fullname)) +			{ +				setTitle(mTitle + ": " + fullname); +			} +			for (U32 i=0; i < TEX_NUM_INDICES; i++) +			{ +				update_texture_ctrl(avatarp, mTextures[i], ETextureIndex(i)); +			}  		} -		for (U32 i=0; i < TEX_NUM_INDICES; i++) +		else  		{ -			update_texture_ctrl(avatarp, mTextures[i], ETextureIndex(i)); +			setTitle(mTitle + ": " + getString("InvalidAvatar") + " (" + mID.asString() + ")");  		}  	} -	else -	{ -		setTitle(mTitle + ": " + getString("InvalidAvatar") + " (" + mID.asString() + ")"); -	}  } -#else - -void LLFloaterAvatarTextures::refresh() -{ -} - -#endif -  // static  void LLFloaterAvatarTextures::onClickDump(void* data)  { -#if !LL_RELEASE_FOR_DOWNLOAD -	LLFloaterAvatarTextures* self = (LLFloaterAvatarTextures*)data; -	LLVOAvatar* avatarp = find_avatar(self->mID); -	if (!avatarp) return; - -	for (S32 i = 0; i < avatarp->getNumTEs(); i++) +	if (gAgent.isGodlike())  	{ -		const LLTextureEntry* te = avatarp->getTE(i); -		if (!te) continue; +		LLFloaterAvatarTextures* self = (LLFloaterAvatarTextures*)data; +		LLVOAvatar* avatarp = find_avatar(self->mID); +		if (!avatarp) return; -		if (LLVOAvatar::isIndexLocalTexture((ETextureIndex)i)) +		for (S32 i = 0; i < avatarp->getNumTEs(); i++)  		{ -			LLUUID id = IMG_DEFAULT_AVATAR; -			EWearableType wearable_type = LLVOAvatarDictionary::getInstance()->getTEWearableType((ETextureIndex)i); -			LLWearable *wearable = gAgentWearables.getWearable(wearable_type, 0); -			if (wearable) +			const LLTextureEntry* te = avatarp->getTE(i); +			if (!te) continue; + +			if (LLVOAvatar::isIndexLocalTexture((ETextureIndex)i))  			{ -				LLLocalTextureObject *lto = wearable->getLocalTextureObject(i); -				if (lto) +				LLUUID id = IMG_DEFAULT_AVATAR; +				EWearableType wearable_type = LLVOAvatarDictionary::getInstance()->getTEWearableType((ETextureIndex)i); +				LLWearable *wearable = gAgentWearables.getWearable(wearable_type, 0); +				if (wearable)  				{ -					id = lto->getID(); +					LLLocalTextureObject *lto = wearable->getLocalTextureObject(i); +					if (lto) +					{ +						id = lto->getID(); +					} +				} +				if (id != IMG_DEFAULT_AVATAR) +				{ +					llinfos << "Avatar TE " << i << " id " << id << llendl; +				} +				else +				{ +					llinfos << "Avatar TE " << i << " id " << "<DEFAULT>" << llendl;  				} -			} -			if (id != IMG_DEFAULT_AVATAR) -			{ -				llinfos << "Avatar TE " << i << " id " << id << llendl;  			}  			else  			{ -				llinfos << "Avatar TE " << i << " id " << "<DEFAULT>" << llendl; +				llinfos << "Avatar TE " << i << " id " << te->getID() << llendl;  			}  		} -		else -		{ -			llinfos << "Avatar TE " << i << " id " << te->getID() << llendl; -		}  	} -#endif  } diff --git a/indra/newview/llfloaterbuyland.cpp b/indra/newview/llfloaterbuyland.cpp index d37bc01885..464b3a7214 100644 --- a/indra/newview/llfloaterbuyland.cpp +++ b/indra/newview/llfloaterbuyland.cpp @@ -830,7 +830,7 @@ void LLFloaterBuyLandUI::updateNames()  	else  	{  		mParcelSellerName = -			LLSLURL::buildCommand("agent", parcelp->getOwnerID(), "inspect"); +			LLSLURL("agent", parcelp->getOwnerID(), "inspect").getSLURLString();  	}  } @@ -859,7 +859,7 @@ void LLFloaterBuyLandUI::startTransaction(TransactionType type, const LLXMLRPCVa  	static std::string transaction_uri;  	if (transaction_uri.empty())  	{ -		transaction_uri = LLViewerLogin::getInstance()->getHelperURI() + "landtool.php"; +		transaction_uri = LLGridManager::getInstance()->getHelperURI() + "landtool.php";  	}  	const char* method; diff --git a/indra/newview/llfloaterchat.cpp b/indra/newview/llfloaterchat.cpp index cdb9b8edb8..882d12f68e 100644 --- a/indra/newview/llfloaterchat.cpp +++ b/indra/newview/llfloaterchat.cpp @@ -166,7 +166,7 @@ void add_timestamped_line(LLViewerTextEditor* edit, LLChat chat, const LLColor4&  	if (chat.mSourceType == CHAT_SOURCE_AGENT &&  		chat.mFromID != LLUUID::null)  	{ -		chat.mURL = LLSLURL::buildCommand("agent", chat.mFromID, "inspect"); +		chat.mURL = LLSLURL("agent", chat.mFromID, "inspect").getSLURLString();  	}  	// If the chat line has an associated url, link it up to the name. diff --git a/indra/newview/llfloaterchatterbox.cpp b/indra/newview/llfloaterchatterbox.cpp index 774caaec90..a15cef7ea4 100644 --- a/indra/newview/llfloaterchatterbox.cpp +++ b/indra/newview/llfloaterchatterbox.cpp @@ -318,7 +318,7 @@ LLFloaterChatterBox* LLFloaterChatterBox::getInstance()  //static   LLFloater* LLFloaterChatterBox::getCurrentVoiceFloater()  { -	if (!LLVoiceClient::voiceEnabled()) +	if (!LLVoiceClient::getInstance()->voiceEnabled())  	{  		return NULL;  	} diff --git a/indra/newview/llfloaterevent.cpp b/indra/newview/llfloaterevent.cpp index 97ebab3425..84a5c3dc77 100644 --- a/indra/newview/llfloaterevent.cpp +++ b/indra/newview/llfloaterevent.cpp @@ -193,7 +193,7 @@ void LLFloaterEvent::processEventInfoReply(LLMessageSystem *msg, void **)  		floater->mTBCategory->setText(floater->mEventInfo.mCategoryStr);  		floater->mTBDate->setText(floater->mEventInfo.mTimeStr);  		floater->mTBDesc->setText(floater->mEventInfo.mDesc); -		floater->mTBRunBy->setText(LLSLURL::buildCommand("agent", floater->mEventInfo.mRunByID, "inspect")); +		floater->mTBRunBy->setText(LLSLURL("agent", floater->mEventInfo.mRunByID, "inspect").getSLURLString());  		floater->mTBDuration->setText(llformat("%d:%.2d", floater->mEventInfo.mDuration / 60, floater->mEventInfo.mDuration % 60)); diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 02c83dcd09..25d3f971b5 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -42,7 +42,6 @@  #include "llnotificationsutil.h"  #include "llparcel.h"  #include "message.h" -#include "lluserauth.h"  #include "llagent.h"  #include "llbutton.h" @@ -804,7 +803,7 @@ void LLPanelLandGeneral::refreshNames()  	else  	{  		// Figure out the owner's name -		owner = LLSLURL::buildCommand("agent", parcel->getOwnerID(), "inspect"); +		owner = LLSLURL("agent", parcel->getOwnerID(), "inspect").getSLURLString();  	}  	if(LLParcel::OS_LEASE_PENDING == parcel->getOwnershipStatus()) @@ -816,7 +815,7 @@ void LLPanelLandGeneral::refreshNames()  	std::string group;  	if (!parcel->getGroupID().isNull())  	{ -		group = LLSLURL::buildCommand("group", parcel->getGroupID(), "inspect"); +		group = LLSLURL("group", parcel->getGroupID(), "inspect").getSLURLString();  	}  	mTextGroup->setText(group); @@ -825,9 +824,9 @@ void LLPanelLandGeneral::refreshNames()  		const LLUUID& auth_buyer_id = parcel->getAuthorizedBuyerID();  		if(auth_buyer_id.notNull())  		{ -			std::string name; -			name = LLSLURL::buildCommand("agent", auth_buyer_id, "inspect"); -			mSaleInfoForSale2->setTextArg("[BUYER]", name); +		  std::string name; +		  name = LLSLURL("agent", auth_buyer_id, "inspect").getSLURLString(); +		  mSaleInfoForSale2->setTextArg("[BUYER]", name);  		}  		else  		{ diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 1172064b59..804ef609ec 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -182,7 +182,6 @@ void LLVoiceSetKeyDialog::onCancel(void* user_data)  // if creating/destroying these is too slow, we'll need to create  // a static member and update all our static callbacks -void handleNameTagOptionChanged(const LLSD& newvalue);	  bool callback_clear_browser_cache(const LLSD& notification, const LLSD& response);  //bool callback_skip_dialogs(const LLSD& notification, const LLSD& response, LLFloaterPreference* floater); @@ -218,15 +217,6 @@ bool callback_clear_browser_cache(const LLSD& notification, const LLSD& response  	return false;  } -void handleNameTagOptionChanged(const LLSD& newvalue) -{ -	S32 name_tag_option = S32(newvalue); -	if(name_tag_option==2) -	{ -		gSavedSettings.setBOOL("SmallAvatarNames", TRUE); -	} -} -  /*bool callback_skip_dialogs(const LLSD& notification, const LLSD& response, LLFloaterPreference* floater)  {  	S32 option = LLNotificationsUtil::getSelectedOption(notification, response); @@ -319,8 +309,6 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key)  	mCommitCallbackRegistrar.add("Pref.MaturitySettings",		boost::bind(&LLFloaterPreference::onChangeMaturity, this));  	sSkin = gSavedSettings.getString("SkinCurrent"); -	 -	gSavedSettings.getControl("AvatarNameTagMode")->getCommitSignal()->connect(boost::bind(&handleNameTagOptionChanged,  _2));  }  BOOL LLFloaterPreference::postBuild() @@ -336,8 +324,6 @@ BOOL LLFloaterPreference::postBuild()  	LLTabContainer* tabcontainer = getChild<LLTabContainer>("pref core");  	if (!tabcontainer->selectTab(gSavedSettings.getS32("LastPrefTab")))  		tabcontainer->selectFirstTab(); -	S32 show_avatar_nametag_options = gSavedSettings.getS32("AvatarNameTagMode"); -	handleNameTagOptionChanged(LLSD(show_avatar_nametag_options));  	std::string cache_location = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, "");  	childSetText("cache_location", cache_location); @@ -611,7 +597,7 @@ void LLFloaterPreference::onBtnOK()  		llinfos << "Can't close preferences!" << llendl;  	} -	LLPanelLogin::refreshLocation( false ); +	LLPanelLogin::updateLocationCombo( false );  }  // static  @@ -628,7 +614,7 @@ void LLFloaterPreference::onBtnApply( )  	apply();  	saveSettings(); -	LLPanelLogin::refreshLocation( false ); +	LLPanelLogin::updateLocationCombo( false );  }  // static  diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 3758cbe74f..8c219cb3fd 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -2922,8 +2922,7 @@ bool LLDispatchEstateUpdateInfo::operator()(  	LLUUID owner_id(strings[1]);  	regionp->setOwner(owner_id);  	// Update estate owner name in UI -	std::string owner_name = -		LLSLURL::buildCommand("agent", owner_id, "inspect"); +	std::string owner_name = LLSLURL("agent", owner_id, "inspect").getSLURLString();  	panel->setOwnerName(owner_name);  	U32 estate_id = strtoul(strings[2].c_str(), NULL, 10); diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index b42b34835d..f7c8855bf6 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -126,7 +126,9 @@ void LLFloaterReporter::processRegionInfo(LLMessageSystem* msg)  // virtual  BOOL LLFloaterReporter::postBuild()  { -	childSetText("abuse_location_edit", LLAgentUI::buildSLURL()); +	LLSLURL slurl; +	LLAgentUI::buildSLURL(slurl); +	childSetText("abuse_location_edit", slurl.getSLURLString());  	enableControls(TRUE); @@ -280,7 +282,6 @@ void LLFloaterReporter::getObjectInfo(const LLUUID& object_id)  				{  					object_owner.append("Unknown");  				} -  				setFromAvatar(mObjectID, object_owner);  			}  			else @@ -325,7 +326,8 @@ void LLFloaterReporter::setFromAvatar(const LLUUID& avatar_id, const std::string  	mAbuserID = mObjectID = avatar_id;  	mOwnerName = avatar_name; -	std::string avatar_link = LLSLURL::buildCommand("agent", mObjectID, "inspect"); +	std::string avatar_link = +	  LLSLURL("agent", mObjectID, "inspect").getSLURLString();  	childSetText("owner_name", avatar_link);  	childSetText("object_name", avatar_name);  	childSetText("abuser_name_edit", avatar_name); @@ -504,7 +506,7 @@ void LLFloaterReporter::setPickedObjectProperties(const std::string& object_name  {  	childSetText("object_name", object_name);  	std::string owner_link = -		LLSLURL::buildCommand("agent", owner_id, "inspect"); +		LLSLURL("agent", owner_id, "inspect").getSLURLString();  	childSetText("owner_name", owner_link);  	childSetText("abuser_name_edit", owner_name);  	mAbuserID = owner_id; @@ -566,7 +568,7 @@ LLSD LLFloaterReporter::gatherReport()  	mCopyrightWarningSeen = FALSE;  	std::ostringstream summary; -	if (!LLViewerLogin::getInstance()->isInProductionGrid()) +	if (!LLGridManager::getInstance()->isInProductionGrid())  	{  		summary << "Preview ";  	} diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp index adac9861d4..aae379afe2 100644 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -1123,7 +1123,7 @@ void LLSnapshotLivePreview::saveWeb(std::string url)  void LLSnapshotLivePreview::regionNameCallback(std::string url, LLSD body, const std::string& name, S32 x, S32 y, S32 z)  { -	body["slurl"] = LLSLURL::buildSLURL(name, x, y, z); +	body["slurl"] = LLSLURL(name, LLVector3d(x, y, z)).getSLURLString();  	LLHTTPClient::post(url, body,  		new LLSendWebResponder()); diff --git a/indra/newview/llfloatervoicedevicesettings.cpp b/indra/newview/llfloatervoicedevicesettings.cpp index 638c9f1b8c..63365e3461 100644 --- a/indra/newview/llfloatervoicedevicesettings.cpp +++ b/indra/newview/llfloatervoicedevicesettings.cpp @@ -64,9 +64,6 @@ LLPanelVoiceDeviceSettings::LLPanelVoiceDeviceSettings()  	// grab "live" mic volume level  	mMicVolume = gSavedSettings.getF32("AudioLevelMic"); -	// ask for new device enumeration -	// now do this in onOpen() instead... -	//gVoiceClient->refreshDeviceLists();  }  LLPanelVoiceDeviceSettings::~LLPanelVoiceDeviceSettings() @@ -105,7 +102,7 @@ void LLPanelVoiceDeviceSettings::draw()  	refresh();  	// let user know that volume indicator is not yet available -	bool is_in_tuning_mode = gVoiceClient->inTuningMode(); +	bool is_in_tuning_mode = LLVoiceClient::getInstance()->inTuningMode();  	childSetVisible("wait_text", !is_in_tuning_mode);  	LLPanel::draw(); @@ -113,7 +110,7 @@ void LLPanelVoiceDeviceSettings::draw()  	if (is_in_tuning_mode)  	{  		const S32 num_bars = 5; -		F32 voice_power = gVoiceClient->tuningGetEnergy() / LLVoiceClient::OVERDRIVEN_POWER_LEVEL; +		F32 voice_power = LLVoiceClient::getInstance()->tuningGetEnergy() / LLVoiceClient::OVERDRIVEN_POWER_LEVEL;  		S32 discrete_power = llmin(num_bars, llfloor(voice_power * (F32)num_bars + 0.1f));  		for(S32 power_bar_idx = 0; power_bar_idx < num_bars; power_bar_idx++) @@ -194,13 +191,13 @@ void LLPanelVoiceDeviceSettings::refresh()  	LLSlider* volume_slider = getChild<LLSlider>("mic_volume_slider");  	// set mic volume tuning slider based on last mic volume setting  	F32 current_volume = (F32)volume_slider->getValue().asReal(); -	gVoiceClient->tuningSetMicVolume(current_volume); +	LLVoiceClient::getInstance()->tuningSetMicVolume(current_volume);  	// Fill in popup menus  	mCtrlInputDevices = getChild<LLComboBox>("voice_input_device");  	mCtrlOutputDevices = getChild<LLComboBox>("voice_output_device"); -	if(!gVoiceClient->deviceSettingsAvailable()) +	if(!LLVoiceClient::getInstance()->deviceSettingsAvailable())  	{  		// The combo boxes are disabled, since we can't get the device settings from the daemon just now.  		// Put the currently set default (ONLY) in the box, and select it. @@ -219,17 +216,16 @@ void LLPanelVoiceDeviceSettings::refresh()  	}  	else if (!mDevicesUpdated)  	{ -		LLVoiceClient::deviceList *devices; -		 -		LLVoiceClient::deviceList::iterator iter; +		LLVoiceDeviceList::const_iterator iter;  		if(mCtrlInputDevices)  		{  			mCtrlInputDevices->removeall();  			mCtrlInputDevices->add( getString("default_text"), ADD_BOTTOM ); -			devices = gVoiceClient->getCaptureDevices(); -			for(iter=devices->begin(); iter != devices->end(); iter++) +			for(iter=LLVoiceClient::getInstance()->getCaptureDevices().begin();  +				iter != LLVoiceClient::getInstance()->getCaptureDevices().end(); +				iter++)  			{  				mCtrlInputDevices->add( *iter, ADD_BOTTOM );  			} @@ -245,8 +241,8 @@ void LLPanelVoiceDeviceSettings::refresh()  			mCtrlOutputDevices->removeall();  			mCtrlOutputDevices->add( getString("default_text"), ADD_BOTTOM ); -			devices = gVoiceClient->getRenderDevices(); -			for(iter=devices->begin(); iter != devices->end(); iter++) +			for(iter= LLVoiceClient::getInstance()->getRenderDevices().begin();  +				iter !=  LLVoiceClient::getInstance()->getRenderDevices().end(); iter++)  			{  				mCtrlOutputDevices->add( *iter, ADD_BOTTOM );  			} @@ -268,37 +264,34 @@ void LLPanelVoiceDeviceSettings::initialize()  	mDevicesUpdated = FALSE;  	// ask for new device enumeration -	gVoiceClient->refreshDeviceLists(); +	LLVoiceClient::getInstance()->refreshDeviceLists();  	// put voice client in "tuning" mode -	gVoiceClient->tuningStart(); +	LLVoiceClient::getInstance()->tuningStart();  	LLVoiceChannel::suspend();  }  void LLPanelVoiceDeviceSettings::cleanup()  { -	if (gVoiceClient) -	{ -		gVoiceClient->tuningStop(); -	} +	LLVoiceClient::getInstance()->tuningStop();  	LLVoiceChannel::resume();  }  // static  void LLPanelVoiceDeviceSettings::onCommitInputDevice(LLUICtrl* ctrl, void* user_data)  { -	if(gVoiceClient) +	if(LLVoiceClient::getInstance())  	{ -		gVoiceClient->setCaptureDevice(ctrl->getValue().asString()); +		LLVoiceClient::getInstance()->setCaptureDevice(ctrl->getValue().asString());  	}  }  // static  void LLPanelVoiceDeviceSettings::onCommitOutputDevice(LLUICtrl* ctrl, void* user_data)  { -	if(gVoiceClient) +	if(LLVoiceClient::getInstance())  	{ -		gVoiceClient->setRenderDevice(ctrl->getValue().asString()); +		LLVoiceClient::getInstance()->setRenderDevice(ctrl->getValue().asString());  	}  } diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index f17c9765b9..896c410e32 100644 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -461,7 +461,7 @@ void LLFloaterWorldMap::draw()  	childSetEnabled("Teleport", (BOOL)tracking_status);  //	childSetEnabled("Clear", (BOOL)tracking_status);  	childSetEnabled("Show Destination", (BOOL)tracking_status || LLWorldMap::getInstance()->isTracking()); -	childSetEnabled("copy_slurl", (mSLURL.size() > 0) ); +	childSetEnabled("copy_slurl", (mSLURL.isValid()) );  	setMouseOpaque(TRUE);  	getDragHandle()->setMouseOpaque(TRUE); @@ -660,14 +660,8 @@ void LLFloaterWorldMap::updateLocation()  				childSetValue("location", agent_sim_name);  				// Figure out where user is -				LLVector3d agentPos = gAgent.getPositionGlobal(); - -				S32 agent_x = llround( (F32)fmod( agentPos.mdV[VX], (F64)REGION_WIDTH_METERS ) ); -				S32 agent_y = llround( (F32)fmod( agentPos.mdV[VY], (F64)REGION_WIDTH_METERS ) ); -				S32 agent_z = llround( (F32)agentPos.mdV[VZ] ); -  				// Set the current SLURL -				mSLURL = LLSLURL::buildSLURL(agent_sim_name, agent_x, agent_y, agent_z); +				mSLURL = LLSLURL(agent_sim_name, gAgent.getPositionGlobal());  			}  		} @@ -694,18 +688,15 @@ void LLFloaterWorldMap::updateLocation()  		}  		childSetValue("location", sim_name); -		 -		F32 region_x = (F32)fmod( pos_global.mdV[VX], (F64)REGION_WIDTH_METERS ); -		F32 region_y = (F32)fmod( pos_global.mdV[VY], (F64)REGION_WIDTH_METERS );  		// simNameFromPosGlobal can fail, so don't give the user an invalid SLURL  		if ( gotSimName )  		{ -			mSLURL = LLSLURL::buildSLURL(sim_name, llround(region_x), llround(region_y), llround((F32)pos_global.mdV[VZ])); +		  mSLURL = LLSLURL(sim_name, pos_global);  		}  		else  		{	// Empty SLURL will disable the "Copy SLURL to clipboard" button -			mSLURL = ""; +			mSLURL = LLSLURL();  		}  	}  } @@ -1174,7 +1165,7 @@ void LLFloaterWorldMap::onClearBtn()  	mTrackedStatus = LLTracker::TRACKING_NOTHING;  	LLTracker::stopTracking((void *)(intptr_t)TRUE);  	LLWorldMap::getInstance()->cancelTracking(); -	mSLURL = "";					// Clear the SLURL since it's invalid +	mSLURL = LLSLURL();					// Clear the SLURL since it's invalid  	mSetToUserPosition = TRUE;	// Revert back to the current user position  } @@ -1197,10 +1188,10 @@ void LLFloaterWorldMap::onClickTeleportBtn()  void LLFloaterWorldMap::onCopySLURL()  { -	getWindow()->copyTextToClipboard(utf8str_to_wstring(mSLURL)); +	getWindow()->copyTextToClipboard(utf8str_to_wstring(mSLURL.getSLURLString()));  	LLSD args; -	args["SLURL"] = mSLURL; +	args["SLURL"] = mSLURL.getSLURLString();  	LLNotificationsUtil::add("CopySLURL", args);  } diff --git a/indra/newview/llfloaterworldmap.h b/indra/newview/llfloaterworldmap.h index 00f5e788fb..52809ff830 100644 --- a/indra/newview/llfloaterworldmap.h +++ b/indra/newview/llfloaterworldmap.h @@ -43,6 +43,7 @@  #include "llhudtext.h"  #include "llmapimagetype.h"  #include "lltracker.h" +#include "llslurl.h"  class LLEventInfo;  class LLFriendObserver; @@ -183,7 +184,7 @@ private:  	LLTracker::ETrackingStatus mTrackedStatus;  	std::string				mTrackedSimName;  	std::string				mTrackedAvatarName; -	std::string				mSLURL; +	LLSLURL  				mSLURL;  };  extern LLFloaterWorldMap* gFloaterWorldMap; diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index 43743ec37a..83eb9579b2 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -276,11 +276,6 @@ LLFolderView::~LLFolderView( void )  	mRenamer = NULL;  	mStatusTextBox = NULL; -	if( gEditMenuHandler == this ) -	{ -		gEditMenuHandler = NULL; -	} -  	mAutoOpenItems.removeAllNodes();  	gIdleCallbacks.deleteFunction(idle, this); diff --git a/indra/newview/llfolderviewitem.cpp b/indra/newview/llfolderviewitem.cpp index ecbaac5743..c916e4b98c 100644 --- a/indra/newview/llfolderviewitem.cpp +++ b/indra/newview/llfolderviewitem.cpp @@ -437,11 +437,8 @@ S32 LLFolderViewItem::arrange( S32* width, S32* height, S32 filter_generation)  S32 LLFolderViewItem::getItemHeight()  { -	if (mHidden) return 0; +	if (getHidden()) return 0; -	//S32 icon_height = mIcon->getHeight(); -	//S32 label_height = llround(getLabelFontForStyle(mLabelStyle)->getLineHeight()); -	//return llmax( icon_height, label_height ) + ICON_PAD;  	return mItemHeight;  } @@ -824,32 +821,34 @@ BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,  	return handled;  } -  void LLFolderViewItem::draw()  { -	if (mHidden) return; +	if (getHidden()) +	{ +		return; +	}  	static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE);  	static LLUIColor sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE);  	static LLUIColor sHighlightFgColor = LLUIColorTable::instance().getColor("MenuItemHighlightFgColor", DEFAULT_WHITE); -	static LLUIColor sFocusOutlineColor = -		LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE); +	static LLUIColor sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE);  	static LLUIColor sFilterBGColor = LLUIColorTable::instance().getColor("FilterBackgroundColor", DEFAULT_WHITE);  	static LLUIColor sFilterTextColor = LLUIColorTable::instance().getColor("FilterTextColor", DEFAULT_WHITE);  	static LLUIColor sSuffixColor = LLUIColorTable::instance().getColor("InventoryItemSuffixColor", DEFAULT_WHITE);  	static LLUIColor sSearchStatusColor = LLUIColorTable::instance().getColor("InventorySearchStatusColor", DEFAULT_WHITE); -  	const Params& default_params = LLUICtrlFactory::getDefaultParams<LLFolderViewItem>();  	const S32 TOP_PAD = default_params.item_top_pad; +	const S32 FOCUS_LEFT = 1; +	const LLFontGL* font = getLabelFontForStyle(mLabelStyle); -	bool possibly_has_children = false; -	bool up_to_date = mListener && mListener->isUpToDate(); -	if((up_to_date && hasVisibleChildren() ) || // we fetched our children and some of them have passed the filter... -		(!up_to_date && mListener && mListener->hasChildren())) // ...or we know we have children but haven't fetched them (doesn't obey filter) -	{ -		possibly_has_children = true; -	} -	if(/*mControlLabel[0] != '\0' && */possibly_has_children) + +	//--------------------------------------------------------------------------------// +	// Draw open folder arrow +	// +	const bool up_to_date = mListener && mListener->isUpToDate(); +	const bool possibly_has_children = ((up_to_date && hasVisibleChildren()) || // we fetched our children and some of them have passed the filter... +										(!up_to_date && mListener && mListener->hasChildren())); // ...or we know we have children but haven't fetched them (doesn't obey filter) +	if (possibly_has_children)  	{  		LLUIImage* arrow_image = default_params.folder_arrow_image;  		gl_draw_scaled_rotated_image( @@ -857,22 +856,16 @@ void LLFolderViewItem::draw()  			ARROW_SIZE, ARROW_SIZE, mControlLabelRotation, arrow_image->getImage(), sFgColor);  	} -	// See also LLFolderView::updateRenamerPosition() -	F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation); - -	LLFontGL* font = getLabelFontForStyle(mLabelStyle); - -	// If we have keyboard focus, draw selection filled -	BOOL show_context = getRoot()->getShowSelectionContext(); -	BOOL filled = show_context || (getRoot()->getParentPanel()->hasFocus()); -	const S32 FOCUS_LEFT = 1; -	S32 focus_top = getRect().getHeight(); -	S32 focus_bottom = getRect().getHeight() - mItemHeight; -	bool folder_open = (getRect().getHeight() > mItemHeight + 4); -	// always render "current" item, only render other selected items if -	// mShowSingleSelection is FALSE -	if( mIsSelected ) +	//--------------------------------------------------------------------------------// +	// Draw highlight for selected items +	// +	const BOOL show_context = getRoot()->getShowSelectionContext(); +	const BOOL filled = show_context || (getRoot()->getParentPanel()->hasFocus()); // If we have keyboard focus, draw selection filled +	const S32 focus_top = getRect().getHeight(); +	const S32 focus_bottom = getRect().getHeight() - mItemHeight; +	const bool folder_open = (getRect().getHeight() > mItemHeight + 4); +	if (mIsSelected) // always render "current" item.  Only render other selected items if mShowSingleSelection is FALSE  	{  		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);  		LLColor4 bg_color = sHighlightBgColor; @@ -891,152 +884,169 @@ void LLFolderViewItem::draw()  				bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, 0.f, bg_color.mV[VALPHA]);  			}  		} - -		gl_rect_2d( -			FOCUS_LEFT, -			focus_top,  -			getRect().getWidth() - 2, -			focus_bottom, -			bg_color, filled); +		gl_rect_2d(FOCUS_LEFT, +				   focus_top,  +				   getRect().getWidth() - 2, +				   focus_bottom, +				   bg_color, filled);  		if (mIsCurSelection)  		{ -			gl_rect_2d( -				FOCUS_LEFT,  -				focus_top,  -				getRect().getWidth() - 2, -				focus_bottom, -				sFocusOutlineColor, FALSE); +			gl_rect_2d(FOCUS_LEFT,  +					   focus_top,  +					   getRect().getWidth() - 2, +					   focus_bottom, +					   sFocusOutlineColor, FALSE);  		}  		if (folder_open)  		{ -			gl_rect_2d( -				FOCUS_LEFT, -				focus_bottom + 1, // overlap with bottom edge of above rect -				getRect().getWidth() - 2, -				0, -				sFocusOutlineColor, FALSE); +			gl_rect_2d(FOCUS_LEFT, +					   focus_bottom + 1, // overlap with bottom edge of above rect +					   getRect().getWidth() - 2, +					   0, +					   sFocusOutlineColor, FALSE);  			if (show_context)  			{ -				gl_rect_2d( -					FOCUS_LEFT, -					focus_bottom + 1, -					getRect().getWidth() - 2, -					0, -					sHighlightBgColor, TRUE); +				gl_rect_2d(FOCUS_LEFT, +						   focus_bottom + 1, +						   getRect().getWidth() - 2, +						   0, +						   sHighlightBgColor, TRUE);  			}  		}  	} + +	//--------------------------------------------------------------------------------// +	// Draw DragNDrop highlight +	//  	if (mDragAndDropTarget)  	{  		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); -		gl_rect_2d( -			FOCUS_LEFT,  -			focus_top,  -			getRect().getWidth() - 2, -			focus_bottom, -			sHighlightBgColor, FALSE); +		gl_rect_2d(FOCUS_LEFT,  +				   focus_top,  +				   getRect().getWidth() - 2, +				   focus_bottom, +				   sHighlightBgColor, FALSE);  		if (folder_open)  		{ -			gl_rect_2d( -				FOCUS_LEFT, -				focus_bottom + 1, // overlap with bottom edge of above rect -				getRect().getWidth() - 2, -				0, -				sHighlightBgColor, FALSE); +			gl_rect_2d(FOCUS_LEFT, +					   focus_bottom + 1, // overlap with bottom edge of above rect +					   getRect().getWidth() - 2, +					   0, +					   sHighlightBgColor, FALSE);  		}  		mDragAndDropTarget = FALSE;  	} -	S32 icon_x = mIndentation + ARROW_SIZE + TEXT_PAD; -	// First case is used for open folders -	if (!mIconOpen.isNull() && (llabs(mControlLabelRotation) > 80)) +	 +	//--------------------------------------------------------------------------------// +	// Draw open icon +	// +	const S32 icon_x = mIndentation + ARROW_SIZE + TEXT_PAD; +	if (!mIconOpen.isNull() && (llabs(mControlLabelRotation) > 80)) // For open folders   	{  		mIconOpen->draw(icon_x, getRect().getHeight() - mIconOpen->getHeight() - TOP_PAD + 1);  	} -	else if(mIcon) +	else if (mIcon)  	{   		mIcon->draw(icon_x, getRect().getHeight() - mIcon->getHeight() - TOP_PAD + 1);   	} -	if (!mLabel.empty()) + +	//--------------------------------------------------------------------------------// +	// Exit if no label to draw +	// +	if (mLabel.empty())  	{ -		// highlight filtered text -		BOOL debug_filters = getRoot()->getDebugFilters(); -		LLColor4 color = ( (mIsSelected && filled) ? sHighlightFgColor : sFgColor ); -		F32 right_x; -		F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD; +		return; +	} -		if (debug_filters) -		{ -			if (!getFiltered() && !possibly_has_children) -			{ -				color.mV[VALPHA] *= 0.5f; -			} +	LLColor4 color = (mIsSelected && filled) ? sHighlightFgColor : sFgColor; +	F32 right_x  = 0; +	F32 y = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD; +	F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation); -			LLColor4 filter_color = mLastFilterGeneration >= getRoot()->getFilter()->getCurrentGeneration() ? LLColor4(0.5f, 0.8f, 0.5f, 1.f) : LLColor4(0.8f, 0.5f, 0.5f, 1.f); -			LLFontGL::getFontMonospace()->renderUTF8( -				mStatusText, 0, text_left, y, filter_color, -				LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, -				S32_MAX, S32_MAX, &right_x, FALSE ); -			text_left = right_x; +	//--------------------------------------------------------------------------------// +	// Highlight filtered text +	// +	if (getRoot()->getDebugFilters()) +	{ +		if (!getFiltered() && !possibly_has_children) +		{ +			color.mV[VALPHA] *= 0.5f;  		} +		LLColor4 filter_color = mLastFilterGeneration >= getRoot()->getFilter()->getCurrentGeneration() ?  +			LLColor4(0.5f, 0.8f, 0.5f, 1.f) :  +			LLColor4(0.8f, 0.5f, 0.5f, 1.f); +		LLFontGL::getFontMonospace()->renderUTF8(mStatusText, 0, text_left, y, filter_color, +												 LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, +												 S32_MAX, S32_MAX, &right_x, FALSE ); +		text_left = right_x; +	} -		font->renderUTF8( mLabel, 0, text_left, y, color, -						  LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, -						  S32_MAX, getRect().getWidth() - (S32) text_left, &right_x, TRUE); +	//--------------------------------------------------------------------------------// +	// Draw the actual label text +	// +	font->renderUTF8(mLabel, 0, text_left, y, color, +					 LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, +					 S32_MAX, getRect().getWidth() - (S32) text_left, &right_x, TRUE); -//		LLViewerInventoryCategory *item = 0; -//		if (getListener()) -//			item = gInventory.getCategory(getListener()->getUUID()); -		bool root_is_loading = false; -		if (getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(),gInventory.getRootFolderID())) -		{ -			// Descendent of my inventory. -			root_is_loading = LLInventoryModelBackgroundFetch::instance().inventoryFetchInProgress(); -		} -		if (getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(),gInventory.getLibraryRootFolderID())) -		{ -			// Descendent of library -			root_is_loading = LLInventoryModelBackgroundFetch::instance().libraryFetchInProgress(); -		} -			 -		if ( (mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= gSavedSettings.getF32("FolderLoadingMessageWaitTime")) -			|| (LLInventoryModelBackgroundFetch::instance().backgroundFetchActive() && root_is_loading && mShowLoadStatus) ) -		{ -			std::string load_string = " ( " + LLTrans::getString("LoadingData") + " ) "; -			font->renderUTF8(load_string, 0, right_x, y, sSearchStatusColor, -					  LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, &right_x, FALSE); -		} +	//--------------------------------------------------------------------------------// +	// Draw "Loading..." text +	// +	bool root_is_loading = false; +	if (getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(),  +														 gInventory.getRootFolderID())) // Descendent of my inventory +	{ +		root_is_loading = LLInventoryModelBackgroundFetch::instance().inventoryFetchInProgress();  +	} +	if (getListener() && gInventory.isObjectDescendentOf(getListener()->getUUID(),  +														 gInventory.getLibraryRootFolderID())) // Descendent of library +	{ +		root_is_loading = LLInventoryModelBackgroundFetch::instance().libraryFetchInProgress(); +	} +	if ((mIsLoading && mTimeSinceRequestStart.getElapsedTimeF32() >= gSavedSettings.getF32("FolderLoadingMessageWaitTime")) || +		(LLInventoryModelBackgroundFetch::instance().backgroundFetchActive() && root_is_loading && (mShowLoadStatus || mHidden))) +	{ +		std::string load_string = " ( " + LLTrans::getString("LoadingData") + " ) "; +		font->renderUTF8(load_string, 0, right_x, y, sSearchStatusColor, +						 LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW,  +						 S32_MAX, S32_MAX, &right_x, FALSE); +	} -		if (!mLabelSuffix.empty()) -		{ -			font->renderUTF8( mLabelSuffix, 0, right_x, y, sSuffixColor, -					   LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, -				S32_MAX, S32_MAX, &right_x, FALSE ); -		} +	//--------------------------------------------------------------------------------// +	// Draw label suffix +	// +	if (!mLabelSuffix.empty()) +	{ +		font->renderUTF8( mLabelSuffix, 0, right_x, y, sSuffixColor, +						  LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, +						  S32_MAX, S32_MAX, &right_x, FALSE ); +	} -		if (mStringMatchOffset != std::string::npos) +	//--------------------------------------------------------------------------------// +	// Highlight string match +	// +	if (mStringMatchOffset != std::string::npos) +	{ +		// don't draw backgrounds for zero-length strings +		S32 filter_string_length = getRoot()->getFilterSubString().size(); +		if (filter_string_length > 0)  		{ -			// don't draw backgrounds for zero-length strings -			S32 filter_string_length = getRoot()->getFilterSubString().size(); -			if (filter_string_length > 0) -			{ -				std::string combined_string = mLabel + mLabelSuffix; -				S32 left = llround(text_left) + font->getWidth(combined_string, 0, mStringMatchOffset) - 1; -				S32 right = left + font->getWidth(combined_string, mStringMatchOffset, filter_string_length) + 2; -				S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD); -				S32 top = getRect().getHeight() - TOP_PAD; +			std::string combined_string = mLabel + mLabelSuffix; +			S32 left = llround(text_left) + font->getWidth(combined_string, 0, mStringMatchOffset) - 1; +			S32 right = left + font->getWidth(combined_string, mStringMatchOffset, filter_string_length) + 2; +			S32 bottom = llfloor(getRect().getHeight() - font->getLineHeight() - 3 - TOP_PAD); +			S32 top = getRect().getHeight() - TOP_PAD; -				LLUIImage* box_image = default_params.selection_image; -				LLRect box_rect(left, top, right, bottom); -				box_image->draw(box_rect, sFilterBGColor); -				F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, mStringMatchOffset); -				F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD; -				font->renderUTF8( combined_string, mStringMatchOffset, match_string_left, yy, -								  sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, -								  filter_string_length, S32_MAX, &right_x, FALSE ); -			} +			LLUIImage* box_image = default_params.selection_image; +			LLRect box_rect(left, top, right, bottom); +			box_image->draw(box_rect, sFilterBGColor); +			F32 match_string_left = text_left + font->getWidthF32(combined_string, 0, mStringMatchOffset); +			F32 yy = (F32)getRect().getHeight() - font->getLineHeight() - (F32)TEXT_PAD - (F32)TOP_PAD; +			font->renderUTF8( combined_string, mStringMatchOffset, match_string_left, yy, +							  sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, +							  filter_string_length, S32_MAX, &right_x, FALSE );  		}  	}  } diff --git a/indra/newview/llgrouplist.cpp b/indra/newview/llgrouplist.cpp index 8a056f836f..03a47b5983 100644 --- a/indra/newview/llgrouplist.cpp +++ b/indra/newview/llgrouplist.cpp @@ -287,7 +287,7 @@ bool LLGroupList::onContextMenuItemEnable(const LLSD& userdata)  		return gAgent.getGroupID() != selected_group_id;  	if (userdata.asString() == "call") -		return real_group_selected && LLVoiceClient::voiceEnabled()&&gVoiceClient->voiceWorking(); +	  return real_group_selected && LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking();  	return real_group_selected;  } diff --git a/indra/newview/llimfloater.cpp b/indra/newview/llimfloater.cpp index 3ec8d11fb0..9704c7537a 100644 --- a/indra/newview/llimfloater.cpp +++ b/indra/newview/llimfloater.cpp @@ -58,7 +58,6 @@  #include "lltransientfloatermgr.h"  #include "llinventorymodel.h"  #include "llrootview.h" -  #include "llspeakers.h" diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index 4bdf5f42dc..0e3b78df7f 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -300,7 +300,7 @@ void LLFloaterIMPanel::onVolumeChange(LLUICtrl* source, void* user_data)  	LLFloaterIMPanel* floaterp = (LLFloaterIMPanel*)user_data;  	if (floaterp)  	{ -		gVoiceClient->setUserVolume(floaterp->mOtherParticipantUUID, (F32)source->getValue().asReal()); +		LLVoiceClient::getInstance()->setUserVolume(floaterp->mOtherParticipantUUID, (F32)source->getValue().asReal());  	}  } @@ -312,7 +312,7 @@ void LLFloaterIMPanel::draw()  	BOOL enable_connect = (region && region->getCapability("ChatSessionRequest") != "")  					  && mSessionInitialized -					  && LLVoiceClient::voiceEnabled() +					  && LLVoiceClient::getInstance()->voiceEnabled()  					  && mCallBackEnabled;  	// hide/show start call and end call buttons @@ -320,8 +320,8 @@ void LLFloaterIMPanel::draw()  	if (!voice_channel)  		return; -	childSetVisible("end_call_btn", LLVoiceClient::voiceEnabled() && voice_channel->getState() >= LLVoiceChannel::STATE_CALL_STARTED); -	childSetVisible("start_call_btn", LLVoiceClient::voiceEnabled() && voice_channel->getState() < LLVoiceChannel::STATE_CALL_STARTED); +	childSetVisible("end_call_btn", LLVoiceClient::getInstance()->voiceEnabled() && voice_channel->getState() >= LLVoiceChannel::STATE_CALL_STARTED); +	childSetVisible("start_call_btn", LLVoiceClient::getInstance()->voiceEnabled() && voice_channel->getState() < LLVoiceChannel::STATE_CALL_STARTED);  	childSetEnabled("start_call_btn", enable_connect);  	childSetEnabled("send_btn", !childGetValue("chat_editor").asString().empty()); @@ -384,11 +384,11 @@ void LLFloaterIMPanel::draw()  	else  	{  		// refresh volume and mute checkbox -		childSetVisible("speaker_volume", LLVoiceClient::voiceEnabled() && voice_channel->isActive()); -		childSetValue("speaker_volume", gVoiceClient->getUserVolume(mOtherParticipantUUID)); +		childSetVisible("speaker_volume", LLVoiceClient::getInstance()->voiceEnabled() && voice_channel->isActive()); +		childSetValue("speaker_volume", LLVoiceClient::getInstance()->getUserVolume(mOtherParticipantUUID));  		childSetValue("mute_btn", LLMuteList::getInstance()->isMuted(mOtherParticipantUUID, LLMute::flagVoiceChat)); -		childSetVisible("mute_btn", LLVoiceClient::voiceEnabled() && voice_channel->isActive()); +		childSetVisible("mute_btn", LLVoiceClient::getInstance()->voiceEnabled() && voice_channel->isActive());  	}  	LLFloater::draw();  } diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index e0f155a6a9..5201f92dbc 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -344,13 +344,13 @@ LLIMModel::LLIMSession::~LLIMSession()  	mSpeakers = NULL;  	// End the text IM session if necessary -	if(gVoiceClient && mOtherParticipantID.notNull()) +	if(LLVoiceClient::getInstance() && mOtherParticipantID.notNull())  	{  		switch(mType)  		{  		case IM_NOTHING_SPECIAL:  		case IM_SESSION_P2P_INVITE: -			gVoiceClient->endUserIMSession(mOtherParticipantID); +			LLVoiceClient::getInstance()->endUserIMSession(mOtherParticipantID);  			break;  		default: @@ -925,7 +925,7 @@ void LLIMModel::sendMessage(const std::string& utf8_text,  	if((offline == IM_OFFLINE) && (LLVoiceClient::getInstance()->isOnlineSIP(other_participant_id)))  	{  		// User is online through the OOW connector, but not with a regular viewer.  Try to send the message via SLVoice. -		sent = gVoiceClient->sendTextMessage(other_participant_id, utf8_text); +		sent = LLVoiceClient::getInstance()->sendTextMessage(other_participant_id, utf8_text);  	}  	if(!sent) @@ -1717,7 +1717,7 @@ void LLOutgoingCallDialog::show(const LLSD& key)  		// skipping "You will now be reconnected to nearby" in notification when call is ended by disabling voice,  		// so no reconnection to nearby chat happens (EXT-4397) -		bool voice_works = LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking(); +		bool voice_works = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking();  		std::string reconnect_nearby = voice_works ? LLTrans::getString("reconnect_nearby") : std::string();  		childSetTextArg("nearby", "[RECONNECT_NEARBY]", reconnect_nearby); @@ -1843,7 +1843,11 @@ LLCallDialog(payload)  void LLIncomingCallDialog::onLifetimeExpired()  {  	// check whether a call is valid or not -	if (LLVoiceClient::getInstance()->findSession(mPayload["caller_id"].asUUID())) +	LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mPayload["session_id"].asUUID()); +	if(channelp && +	   (channelp->getState() != LLVoiceChannel::STATE_NO_CHANNEL_INFO) && +	   (channelp->getState() != LLVoiceChannel::STATE_ERROR) && +	   (channelp->getState() != LLVoiceChannel::STATE_HUNG_UP))  	{  		// restart notification's timer if call is still valid  		mLifetimeTimer.start(); @@ -2077,10 +2081,10 @@ void LLIncomingCallDialog::processCallResponse(S32 response)  	{  		if (type == IM_SESSION_P2P_INVITE)  		{ -			if(gVoiceClient) +			if(LLVoiceClient::getInstance())  			{  				std::string s = mPayload["session_handle"].asString(); -				gVoiceClient->declineInvite(s); +				LLVoiceClient::getInstance()->declineInvite(s);  			}  		}  		else @@ -2168,11 +2172,8 @@ bool inviteUserResponse(const LLSD& notification, const LLSD& response)  	{  		if (type == IM_SESSION_P2P_INVITE)  		{ -			if(gVoiceClient) -			{ -				std::string s = payload["session_handle"].asString(); -				gVoiceClient->declineInvite(s); -			} +		  std::string s = payload["session_handle"].asString(); +		  LLVoiceClient::getInstance()->declineInvite(s);  		}  		else  		{ @@ -3078,7 +3079,7 @@ public:  				return;  			} -			if(!LLVoiceClient::voiceEnabled()) +			if(!LLVoiceClient::getInstance()->voiceEnabled())  			{  				// Don't display voice invites unless the user has voice enabled.  				return; diff --git a/indra/newview/llinspectavatar.cpp b/indra/newview/llinspectavatar.cpp index 94ea236757..1299324105 100644 --- a/indra/newview/llinspectavatar.cpp +++ b/indra/newview/llinspectavatar.cpp @@ -536,8 +536,7 @@ void LLInspectAvatar::toggleSelectedVoice(bool enabled)  void LLInspectAvatar::updateVolumeSlider()  { - -	bool voice_enabled = gVoiceClient->getVoiceEnabled(mAvatarID); +	bool voice_enabled = LLVoiceClient::getInstance()->getVoiceEnabled(mAvatarID);  	// Do not display volume slider and mute button if it   	// is ourself or we are not in a voice channel together @@ -567,6 +566,7 @@ void LLInspectAvatar::updateVolumeSlider()  		volume_slider->setEnabled( !is_muted );  		F32 volume; +		  		if (is_muted)  		{  			// it's clearer to display their volume as zero @@ -575,7 +575,7 @@ void LLInspectAvatar::updateVolumeSlider()  		else  		{  			// actual volume -			volume = gVoiceClient->getUserVolume(mAvatarID); +			volume = LLVoiceClient::getInstance()->getUserVolume(mAvatarID);  		}  		volume_slider->setValue( (F64)volume );  	} @@ -604,7 +604,7 @@ void LLInspectAvatar::onClickMuteVolume()  void LLInspectAvatar::onVolumeChange(const LLSD& data)  {  	F32 volume = (F32)data.asReal(); -	gVoiceClient->setUserVolume(mAvatarID, volume); +	LLVoiceClient::getInstance()->setUserVolume(mAvatarID, volume);  }  void LLInspectAvatar::nameUpdatedCallback( diff --git a/indra/newview/llinspectobject.cpp b/indra/newview/llinspectobject.cpp index 91cbbbf430..a2b5ffbac4 100644 --- a/indra/newview/llinspectobject.cpp +++ b/indra/newview/llinspectobject.cpp @@ -480,7 +480,7 @@ void LLInspectObject::updateCreator(LLSelectNode* nodep)  		// Objects cannot be created by a group, so use agent URL format  		LLUUID creator_id = nodep->mPermissions->getCreator();  		std::string creator_url = -			LLSLURL::buildCommand("agent", creator_id, "about"); +			LLSLURL("agent", creator_id, "about").getSLURLString();  		args["[CREATOR]"] = creator_url;  		// created by one user but owned by another @@ -490,12 +490,12 @@ void LLInspectObject::updateCreator(LLSelectNode* nodep)  		if (group_owned)  		{  			owner_id = nodep->mPermissions->getGroup(); -			owner_url =	LLSLURL::buildCommand("group", owner_id, "about"); +			owner_url =	LLSLURL("group", owner_id, "about").getSLURLString();  		}  		else  		{  			owner_id = nodep->mPermissions->getOwner(); -			owner_url =	LLSLURL::buildCommand("agent", owner_id, "about"); +			owner_url =	LLSLURL("agent", owner_id, "about").getSLURLString();  		}  		args["[OWNER]"] = owner_url; diff --git a/indra/newview/llinspectremoteobject.cpp b/indra/newview/llinspectremoteobject.cpp index 66e4a1bf66..97ff771658 100644 --- a/indra/newview/llinspectremoteobject.cpp +++ b/indra/newview/llinspectremoteobject.cpp @@ -176,11 +176,11 @@ void LLInspectRemoteObject::update()  	{  		if (mGroupOwned)  		{ -			owner = LLSLURL::buildCommand("group", mOwnerID, "about"); +			owner = LLSLURL("group", mOwnerID, "about").getSLURLString();  		}  		else  		{ -			owner = LLSLURL::buildCommand("agent", mOwnerID, "about"); +			owner = LLSLURL("agent", mOwnerID, "about").getSLURLString();  		}  	}  	else diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index d1cc0ae936..b6202c6a8c 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -1329,7 +1329,6 @@ bool LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id)  	return cat->fetchDescendents();  } -  void LLInventoryModel::cache(  	const LLUUID& parent_folder_id,  	const LLUUID& agent_id) diff --git a/indra/newview/lllandmarkactions.cpp b/indra/newview/lllandmarkactions.cpp index 7336efb62a..539ca97a93 100644 --- a/indra/newview/lllandmarkactions.cpp +++ b/indra/newview/lllandmarkactions.cpp @@ -299,7 +299,7 @@ void LLLandmarkActions::getSLURLfromPosGlobal(const LLVector3d& global_pos, slur  	bool gotSimName = LLWorldMap::getInstance()->simNameFromPosGlobal(global_pos, sim_name);  	if (gotSimName)  	{ -		std::string slurl = LLSLURL::buildSLURLfromPosGlobal(sim_name, global_pos, escaped); +	  std::string slurl = LLSLURL(sim_name, global_pos).getSLURLString();  		cb(slurl);  		return; @@ -351,7 +351,7 @@ void LLLandmarkActions::onRegionResponseSLURL(slurl_callback_t cb,  	bool gotSimName = LLWorldMap::getInstance()->simNameFromPosGlobal(global_pos, sim_name);  	if (gotSimName)  	{ -		slurl = LLSLURL::buildSLURLfromPosGlobal(sim_name, global_pos, escaped); +	  slurl = LLSLURL(sim_name, global_pos).getSLURLString();  	}  	else  	{ diff --git a/indra/newview/lllocationinputctrl.cpp b/indra/newview/lllocationinputctrl.cpp index 5a2332cdd8..936e6ed316 100644 --- a/indra/newview/lllocationinputctrl.cpp +++ b/indra/newview/lllocationinputctrl.cpp @@ -227,7 +227,6 @@ LLLocationInputCtrl::LLLocationInputCtrl(const LLLocationInputCtrl::Params& p)  	params.default_text(LLStringUtil::null);  	params.max_length_bytes(p.max_chars);  	params.keystroke_callback(boost::bind(&LLComboBox::onTextEntry, this, _1)); -	params.handle_edit_keys_directly(true);  	params.commit_on_focus_lost(false);  	params.follows.flags(FOLLOWS_ALL);  	mTextEntry = LLUICtrlFactory::create<LLURLLineEditor>(params); @@ -669,9 +668,8 @@ void LLLocationInputCtrl::onLocationPrearrange(const LLSD& data)  				value["item_type"] = TELEPORT_HISTORY;  				value["global_pos"] = result->mGlobalPos.getValue();  				std::string region_name = result->mTitle.substr(0, result->mTitle.find(',')); -				//TODO*: add slurl to teleportitem or parse region name from title -				value["tooltip"] = LLSLURL::buildSLURLfromPosGlobal(region_name, -						result->mGlobalPos,	false); +				//TODO*: add Surl to teleportitem or parse region name from title +				value["tooltip"] = LLSLURL(region_name, result->mGlobalPos).getSLURLString();  				add(result->getTitle(), value);   			}  			result = std::find_if(result + 1, th_items.end(), boost::bind( @@ -1012,7 +1010,9 @@ void LLLocationInputCtrl::changeLocationPresentation()  	if(!mTextEntry->hasSelection() && text == mHumanReadableLocation)  	{  		//needs unescaped one -		mTextEntry->setText(LLAgentUI::buildSLURL(false)); +		LLSLURL slurl; +		LLAgentUI::buildSLURL(slurl, false); +		mTextEntry->setText(slurl.getSLURLString());  		mTextEntry->selectAll();  		mMaturityIcon->setVisible(FALSE); diff --git a/indra/newview/llloginhandler.cpp b/indra/newview/llloginhandler.cpp index e3817eecc4..4e0a7594ba 100644 --- a/indra/newview/llloginhandler.cpp +++ b/indra/newview/llloginhandler.cpp @@ -35,13 +35,14 @@  #include "llloginhandler.h"  // viewer includes +#include "llsecapi.h"  #include "lllogininstance.h"        // to check if logged in yet  #include "llpanellogin.h"			// save_password_to_disk()  #include "llstartup.h"				// getStartupState() -#include "llurlsimstring.h" +#include "llslurl.h"  #include "llviewercontrol.h"		// gSavedSettings  #include "llviewernetwork.h"		// EGridInfo -#include "llviewerwindow.h"			// getWindow() +#include "llviewerwindow.h"                    // getWindow()  // library includes  #include "llmd5.h" @@ -60,109 +61,33 @@ bool LLLoginHandler::parseDirectLogin(std::string url)  	LLURI uri(url);  	parse(uri.queryMap()); -	if (/*mWebLoginKey.isNull() ||*/ -		mFirstName.empty() || -		mLastName.empty()) -	{ -		return false; -	} -	else -	{ -		return true; -	} +	// NOTE: Need to add direct login as per identity evolution +	return true;  } -  void LLLoginHandler::parse(const LLSD& queryMap)  { -	//mWebLoginKey = queryMap["web_login_key"].asUUID(); -	mFirstName = queryMap["first_name"].asString(); -	mLastName = queryMap["last_name"].asString(); -	EGridInfo grid_choice = GRID_INFO_NONE; -	if (queryMap["grid"].asString() == "aditi") -	{ -		grid_choice = GRID_INFO_ADITI; -	} -	else if (queryMap["grid"].asString() == "agni") -	{ -		grid_choice = GRID_INFO_AGNI; -	} -	else if (queryMap["grid"].asString() == "siva") -	{ -		grid_choice = GRID_INFO_SIVA; -	} -	else if (queryMap["grid"].asString() == "damballah") -	{ -		grid_choice = GRID_INFO_DAMBALLAH; -	} -	else if (queryMap["grid"].asString() == "durga") -	{ -		grid_choice = GRID_INFO_DURGA; -	} -	else if (queryMap["grid"].asString() == "shakti") -	{ -		grid_choice = GRID_INFO_SHAKTI; -	} -	else if (queryMap["grid"].asString() == "soma") -	{ -		grid_choice = GRID_INFO_SOMA; -	} -	else if (queryMap["grid"].asString() == "ganga") -	{ -		grid_choice = GRID_INFO_GANGA; -	} -	else if (queryMap["grid"].asString() == "vaak") -	{ -		grid_choice = GRID_INFO_VAAK; -	} -	else if (queryMap["grid"].asString() == "uma") -	{ -		grid_choice = GRID_INFO_UMA; -	} -	else if (queryMap["grid"].asString() == "mohini") -	{ -		grid_choice = GRID_INFO_MOHINI; -	} -	else if (queryMap["grid"].asString() == "yami") -	{ -		grid_choice = GRID_INFO_YAMI; -	} -	else if (queryMap["grid"].asString() == "nandi") +	if (queryMap.has("grid"))  	{ -		grid_choice = GRID_INFO_NANDI; +	  LLGridManager::getInstance()->setGridChoice(queryMap["grid"].asString());  	} -	else if (queryMap["grid"].asString() == "mitra") -	{ -		grid_choice = GRID_INFO_MITRA; -	} -	else if (queryMap["grid"].asString() == "radha") -	{ -		grid_choice = GRID_INFO_RADHA; -	} -	else if (queryMap["grid"].asString() == "ravi") -	{ -		grid_choice = GRID_INFO_RAVI; -	} -	else if (queryMap["grid"].asString() == "aruna") -	{ -		grid_choice = GRID_INFO_ARUNA; -	} - -	if(grid_choice != GRID_INFO_NONE) -	{ -		LLViewerLogin::getInstance()->setGridChoice(grid_choice); -	} - +	 +	  	std::string startLocation = queryMap["location"].asString(); - +	  	if (startLocation == "specify")  	{ -		LLURLSimString::setString(queryMap["region"].asString()); +	  LLStartUp::setStartSLURL(LLSLURL(LLGridManager::getInstance()->getGridLoginID(), +					   queryMap["region"].asString()));  	} -	else if (!startLocation.empty()) // "last" or "home" or ??? (let LLURLSimString figure it out) +	else if (startLocation == "home")  	{ -		LLURLSimString::setString(startLocation); +	  LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_HOME)); +	} +	else if (startLocation == "last") +	{ +	  LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_LAST));  	}  } @@ -220,40 +145,65 @@ bool LLLoginHandler::handle(const LLSD& tokens,  		return true;  	} -	std::string password = query_map["password"].asString(); - -	if (!password.empty()) +	if  (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP)  //on splash page           	{ -		gSavedSettings.setBOOL("RememberPassword", TRUE); - -		if (password.substr(0,3) != "$1$") -		{ -			LLMD5 pass((unsigned char*)password.c_str()); -			char md5pass[33];		/* Flawfinder: ignore */ -			pass.hex_digest(md5pass); -			std::string hashed_password = ll_safe_string(md5pass, 32); -			LLStartUp::savePasswordToDisk(hashed_password); -		} +	  // as the login page may change from grid to grid, as well as +	  // things like username/password/etc, we simply refresh the +	  // login page to make sure everything is set up correctly +	  LLPanelLogin::loadLoginPage(); +	  LLStartUp::setStartupState( STATE_LOGIN_CLEANUP );  	} -			 +	return true; +} -	if (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP)  //on splash page -	{ -		if (!mFirstName.empty() || !mLastName.empty()) -		{ -			// Fill in the name, and maybe the password -			LLPanelLogin::setFields(mFirstName, mLastName, password); -		} -		//if (mWebLoginKey.isNull()) -		//{ -		//	LLPanelLogin::loadLoginPage(); -		//} -		//else -		//{ -		//	LLStartUp::setStartupState( STATE_LOGIN_CLEANUP ); -		//} -		LLStartUp::setStartupState( STATE_LOGIN_CLEANUP ); -	} -	return true; + +//  Initialize the credentials                                                                                               +// If the passed in URL contains login info, parse                                                                           +// that into a credential and web login key.  Otherwise                                                                      +// check the command line.  If the command line                                                                              +// does not contain any login creds, load the last saved                                                                     +// ones from the protected credential store.                                                                                 +// This always returns with a credential structure set in the                                                                +// login handler                                                                                                             +LLPointer<LLCredential> LLLoginHandler::initializeLoginInfo()                                          +{                                                                                                                            +	LLPointer<LLCredential> result = NULL;                                                                                +	// so try to load it from the UserLoginInfo                                                                           +	result = loadSavedUserLoginInfo();                                                                                    +	if (result.isNull())                                                                                                  +	{                                                                                                                     +		result =  gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGrid());                        +	}                                                                                                                     +	 +	return result;                                                                                                        +}  + + +LLPointer<LLCredential> LLLoginHandler::loadSavedUserLoginInfo() +{ +  // load the saved user login info into a LLCredential. +  // perhaps this should be moved. +	LLSD cmd_line_login = gSavedSettings.getLLSD("UserLoginInfo"); +	if (cmd_line_login.size() == 3)  +	{ +	 +		LLMD5 pass((unsigned char*)cmd_line_login[2].asString().c_str()); +		char md5pass[33];               /* Flawfinder: ignore */ +		pass.hex_digest(md5pass); +		LLSD identifier = LLSD::emptyMap(); +		identifier["type"] = "agent"; +		identifier["first_name"] = cmd_line_login[0]; +		identifier["last_name"] = cmd_line_login[1]; +		 +		LLSD authenticator = LLSD::emptyMap(); +		authenticator["type"] = "hash"; +		authenticator["algorithm"] = "md5"; +		authenticator["secret"] = md5pass; +		// yuck, we'll fix this with mani's changes. +		gSavedSettings.setBOOL("AutoLogin", TRUE); +		return gSecAPIHandler->createCredential(LLGridManager::getInstance()->getGrid(),  +													   identifier, authenticator); +	} +	return NULL;  } diff --git a/indra/newview/llloginhandler.h b/indra/newview/llloginhandler.h index ac4648761b..c15b998c91 100644 --- a/indra/newview/llloginhandler.h +++ b/indra/newview/llloginhandler.h @@ -34,6 +34,7 @@  #define LLLOGINHANDLER_H  #include "llcommandhandler.h" +#include "llsecapi.h"  class LLLoginHandler : public LLCommandHandler  { @@ -46,19 +47,15 @@ class LLLoginHandler : public LLCommandHandler  	// secondlife:///app/login?first=Bob&last=Dobbs  	bool parseDirectLogin(std::string url); -	std::string getFirstName() const { return mFirstName; } -	std::string getLastName() const { return mLastName; } -  	// Web-based login unsupported  	//LLUUID getWebLoginKey() const { return mWebLoginKey; } +	LLPointer<LLCredential> loadSavedUserLoginInfo();   +	LLPointer<LLCredential> initializeLoginInfo(); +  private:  	void parse(const LLSD& queryMap); -private: -	std::string mFirstName; -	std::string mLastName; -	//LLUUID mWebLoginKey;  };  extern LLLoginHandler gLoginHandler; diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index 24c72c65ce..0459c85050 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -48,13 +48,16 @@  // newview  #include "llviewernetwork.h"  #include "llviewercontrol.h" -#include "llurlsimstring.h" +#include "llslurl.h" +#include "llstartup.h"  #include "llfloaterreg.h"  #include "llnotifications.h"  #include "llwindow.h"  #if LL_LINUX || LL_SOLARIS  #include "lltrans.h"  #endif +#include "llsecapi.h" +#include "llstartup.h"  static const char * const TOS_REPLY_PUMP = "lllogininstance_tos_callback";  static const char * const TOS_LISTENER_NAME = "lllogininstance_tos"; @@ -83,14 +86,14 @@ LLLoginInstance::~LLLoginInstance()  {  } -void LLLoginInstance::connect(const LLSD& credentials) +void LLLoginInstance::connect(LLPointer<LLCredential> credentials)  {  	std::vector<std::string> uris; -	LLViewerLogin::getInstance()->getLoginURIs(uris); +	LLGridManager::getInstance()->getLoginURIs(uris);  	connect(uris.front(), credentials);  } -void LLLoginInstance::connect(const std::string& uri, const LLSD& credentials) +void LLLoginInstance::connect(const std::string& uri, LLPointer<LLCredential> credentials)  {  	mAttemptComplete = false; // Reset attempt complete at this point!  	constructAuthParams(credentials); @@ -102,7 +105,7 @@ void LLLoginInstance::reconnect()  	// Sort of like connect, only using the pre-existing  	// request params.  	std::vector<std::string> uris; -	LLViewerLogin::getInstance()->getLoginURIs(uris); +	LLGridManager::getInstance()->getLoginURIs(uris);  	mLoginModule->connect(uris.front(), mRequestData);  } @@ -118,7 +121,7 @@ LLSD LLLoginInstance::getResponse()  	return mResponseData;   } -void LLLoginInstance::constructAuthParams(const LLSD& credentials) +void LLLoginInstance::constructAuthParams(LLPointer<LLCredential> user_credential)  {  	// Set up auth request options.  //#define LL_MINIMIAL_REQUESTED_OPTIONS @@ -145,8 +148,10 @@ void LLLoginInstance::constructAuthParams(const LLSD& credentials)  	requested_options.append("adult_compliant");   	//requested_options.append("inventory-targets");  	requested_options.append("buddy-list"); +	requested_options.append("newuser-config");  	requested_options.append("ui-config");  #endif +	requested_options.append("voice-config");  	requested_options.append("tutorial_setting");  	requested_options.append("login-flags");  	requested_options.append("global-textures"); @@ -155,20 +160,18 @@ void LLLoginInstance::constructAuthParams(const LLSD& credentials)  		gSavedSettings.setBOOL("UseDebugMenus", TRUE);  		requested_options.append("god-connect");  	} +	 +	// (re)initialize the request params with creds. +	LLSD request_params = user_credential->getLoginParams();  	char hashed_mac_string[MD5HEX_STR_SIZE];		/* Flawfinder: ignore */  	LLMD5 hashed_mac; -	hashed_mac.update( gMACAddress, MAC_ADDRESS_BYTES ); +	unsigned char MACAddress[MAC_ADDRESS_BYTES]; +	LLUUID::getNodeID(MACAddress);	 +	hashed_mac.update( MACAddress, MAC_ADDRESS_BYTES );  	hashed_mac.finalize();  	hashed_mac.hex_digest(hashed_mac_string); - -	// prepend "$1$" to the password to indicate its the md5'd version. -	std::string dpasswd("$1$"); -	dpasswd.append(credentials["passwd"].asString()); - -	// (re)initialize the request params with creds. -	LLSD request_params(credentials); -	request_params["passwd"] = dpasswd; +	  	request_params["start"] = construct_start_string();  	request_params["skipoptional"] = mSkipOptionalUpdate;  	request_params["agree_to_tos"] = false; // Always false here. Set true in  @@ -247,6 +250,15 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event)  			LLSD data(LLSD::emptyMap());  			data["message"] = message_response;  			data["reply_pump"] = TOS_REPLY_PUMP; +			if(response.has("error_code")) +			{ +				data["error_code"] = response["error_code"]; +			} +			if(response.has("certificate")) +			{ +				data["certificate"] = response["certificate"]; +			} +			  			LLFloaterReg::showInstance("message_critical", data);  			LLEventPumps::instance().obtain(TOS_REPLY_PUMP)  				.listen(TOS_LISTENER_NAME, @@ -454,20 +466,31 @@ bool LLLoginInstance::updateDialogCallback(const LLSD& notification, const LLSD&  std::string construct_start_string()  {  	std::string start; -	if (LLURLSimString::parse()) +	LLSLURL start_slurl = LLStartUp::getStartSLURL(); +	switch(start_slurl.getType())  	{ -		// a startup URL was specified -		std::string unescaped_start =  +		case LLSLURL::LOCATION: +		{ +			// a startup URL was specified +			LLVector3 position = start_slurl.getPosition(); +			std::string unescaped_start =   			STRINGIZE(  "uri:"  -						<< LLURLSimString::sInstance.mSimName << "&"  -						<< LLURLSimString::sInstance.mX << "&"  -						<< LLURLSimString::sInstance.mY << "&"  -						<< LLURLSimString::sInstance.mZ); -		start = xml_escape_string(unescaped_start); -	} -	else -	{ -		start = gSavedSettings.getString("LoginLocation"); +					  << start_slurl.getRegion() << "&"  +						<< position[VX] << "&"  +						<< position[VY] << "&"  +						<< position[VZ]); +			start = xml_escape_string(unescaped_start); +			break; +		} +		case LLSLURL::HOME_LOCATION: +		{ +			start = "home"; +			break; +		} +		default: +		{ +			start = "last"; +		}  	}  	return start;  } diff --git a/indra/newview/lllogininstance.h b/indra/newview/lllogininstance.h index c8704eddb4..44271bb75e 100644 --- a/indra/newview/lllogininstance.h +++ b/indra/newview/lllogininstance.h @@ -36,6 +36,7 @@  #include "lleventdispatcher.h"  #include <boost/scoped_ptr.hpp>  #include <boost/function.hpp> +#include "llsecapi.h"  class LLLogin;  class LLEventStream;  class LLNotificationsInterface; @@ -48,8 +49,8 @@ public:  	LLLoginInstance();  	~LLLoginInstance(); -	void connect(const LLSD& credential); // Connect to the current grid choice. -	void connect(const std::string& uri, const LLSD& credential);	// Connect to the given uri. +	void connect(LLPointer<LLCredential> credentials); // Connect to the current grid choice. +	void connect(const std::string& uri, LLPointer<LLCredential> credentials);	// Connect to the given uri.  	void reconnect(); // reconnect using the current credentials.  	void disconnect(); @@ -81,7 +82,7 @@ public:  	void setUpdaterLauncher(const UpdaterLauncherCallback& ulc) { mUpdaterLauncher = ulc; }  private: -	void constructAuthParams(const LLSD& credentials);  +	void constructAuthParams(LLPointer<LLCredential> user_credentials);  	void updateApp(bool mandatory, const std::string& message);  	bool updateDialogCallback(const LLSD& notification, const LLSD& response); diff --git a/indra/newview/llnavigationbar.cpp b/indra/newview/llnavigationbar.cpp index e11df06d86..c3d0f1bfc2 100644 --- a/indra/newview/llnavigationbar.cpp +++ b/indra/newview/llnavigationbar.cpp @@ -52,7 +52,6 @@  #include "llsearchcombobox.h"  #include "llsidetray.h"  #include "llslurl.h" -#include "llurlsimstring.h"  #include "llurlregistry.h"  #include "llurldispatcher.h"  #include "llviewerinventory.h" @@ -508,29 +507,34 @@ void LLNavigationBar::onLocationSelection()  	std::string region_name;  	LLVector3 local_coords(128, 128, 0); -	S32 x = 0, y = 0, z = 0;  	// Is the typed location a SLURL? -	if (LLSLURL::isSLURL(typed_location)) +	LLSLURL slurl = LLSLURL(typed_location); +	if (slurl.getType() == LLSLURL::LOCATION)  	{ -		// Yes. Extract region name and local coordinates from it. -		if (LLURLSimString::parse(LLSLURL::stripProtocol(typed_location), ®ion_name, &x, &y, &z)) -				local_coords.set(x, y, z); -		else -			return; +	  region_name = slurl.getRegion(); +	  local_coords = slurl.getPosition();  	} -	// we have to do this check after previous, because LLUrlRegistry contains handlers for slurl too   -	//but we need to know whether typed_location is a simple http url. -	else if (LLUrlRegistry::instance().isUrl(typed_location))  +	else if(!slurl.isValid())  	{ +	  // we have to do this check after previous, because LLUrlRegistry contains handlers for slurl too   +	  // but we need to know whether typed_location is a simple http url. +	  if (LLUrlRegistry::instance().isUrl(typed_location))  +	    {  		// display http:// URLs in the media browser, or  		// anything else is sent to the search floater  		LLWeb::loadURL(typed_location);  		return; +	  } +	  else +	  { +	      // assume that an user has typed the {region name} or possible {region_name, parcel} +	      region_name  = typed_location.substr(0,typed_location.find(',')); +	    }  	}  	else  	{ -		// assume that an user has typed the {region name} or possible {region_name, parcel} -		region_name  = typed_location.substr(0,typed_location.find(',')); +	  // was an app slurl, home, whatever.  Bail +	  return;  	}  	// Resolve the region name to its global coordinates. @@ -562,7 +566,7 @@ void LLNavigationBar::onTeleportFinished(const LLVector3d& global_agent_pos)  	 */  		LLAgentUI::buildLocationString(location, LLAgentUI::LOCATION_FORMAT_NO_MATURITY,  					gAgent.getPosAgentFromGlobal(global_agent_pos)); -	std::string tooltip (LLSLURL::buildSLURLfromPosGlobal(gAgent.getRegion()->getName(), global_agent_pos, false)); +	std::string tooltip (LLSLURL(gAgent.getRegion()->getName(), global_agent_pos).getSLURLString());  	LLLocationHistoryItem item (location,  			global_agent_pos, tooltip,TYPED_REGION_SLURL);// we can add into history only TYPED location @@ -651,7 +655,7 @@ void LLNavigationBar::onRegionNameResponse(  	LLVector3d region_pos = from_region_handle(region_handle);  	LLVector3d global_pos = region_pos + (LLVector3d) local_coords; -	llinfos << "Teleporting to: " << LLSLURL::buildSLURLfromPosGlobal(region_name,	global_pos, false)  << llendl; +	llinfos << "Teleporting to: " << LLSLURL(region_name,	global_pos).getSLURLString()  << llendl;  	gAgent.teleportViaLocation(global_pos);  } diff --git a/indra/newview/lloutputmonitorctrl.cpp b/indra/newview/lloutputmonitorctrl.cpp index d6d48a4ead..197a0ef728 100644 --- a/indra/newview/lloutputmonitorctrl.cpp +++ b/indra/newview/lloutputmonitorctrl.cpp @@ -142,7 +142,7 @@ void LLOutputMonitorCtrl::draw()  	// Copied from llmediaremotectrl.cpp  	// *TODO: Give the LLOutputMonitorCtrl an agent-id to monitor, then -	// call directly into gVoiceClient to ask if that agent-id is muted, is +	// call directly into LLVoiceClient::getInstance() to ask if that agent-id is muted, is  	// speaking, and what power.  This avoids duplicating data, which can get  	// out of sync.  	const F32 LEVEL_0 = LLVoiceClient::OVERDRIVEN_POWER_LEVEL / 3.f; @@ -151,14 +151,14 @@ void LLOutputMonitorCtrl::draw()  	if (getVisible() && mAutoUpdate && !mIsMuted && mSpeakerId.notNull())  	{ -		setPower(gVoiceClient->getCurrentPower(mSpeakerId)); +		setPower(LLVoiceClient::getInstance()->getCurrentPower(mSpeakerId));  		if(mIsAgentControl)  		{ -			setIsTalking(gVoiceClient->getUserPTTState()); +			setIsTalking(LLVoiceClient::getInstance()->getUserPTTState());  		}  		else  		{ -			setIsTalking(gVoiceClient->getIsSpeaking(mSpeakerId)); +			setIsTalking(LLVoiceClient::getInstance()->getIsSpeaking(mSpeakerId));  		}  	} diff --git a/indra/newview/lloutputmonitorctrl.h b/indra/newview/lloutputmonitorctrl.h index b7454a5066..3a83da67e2 100644 --- a/indra/newview/lloutputmonitorctrl.h +++ b/indra/newview/lloutputmonitorctrl.h @@ -143,7 +143,7 @@ private:  	LLPointer<LLUIImage> mImageLevel2;  	LLPointer<LLUIImage> mImageLevel3; -	/** whether to deal with gVoiceClient directly */ +	/** whether to deal with LLVoiceClient::getInstance() directly */  	bool			mAutoUpdate;  	/** uuid of a speaker being monitored */ diff --git a/indra/newview/lloverlaybar.cpp b/indra/newview/lloverlaybar.cpp index 67e048885f..3f1b23ba14 100644 --- a/indra/newview/lloverlaybar.cpp +++ b/indra/newview/lloverlaybar.cpp @@ -258,7 +258,7 @@ void LLOverlayBar::refresh()  	{  		// update "remotes"  		childSetVisible("media_remote_container", TRUE); -		childSetVisible("voice_remote_container", LLVoiceClient::voiceEnabled()); +		childSetVisible("voice_remote_container", LLVoiceClient::getInstance()->voiceEnabled());  		childSetVisible("state_buttons", TRUE);  	} diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index a0ba2f739b..b554af66f0 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -163,7 +163,7 @@ BOOL LLPanelAvatarNotes::postBuild()  	resetControls();  	resetData(); -	gVoiceClient->addObserver((LLVoiceClientStatusObserver*)this); +	LLVoiceClient::getInstance()->addObserver((LLVoiceClientStatusObserver*)this);  	return TRUE;  } @@ -374,7 +374,7 @@ void LLPanelAvatarNotes::onChange(EStatusType status, const std::string &channel  		return;  	} -	childSetEnabled("call", LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking()); +	childSetEnabled("call", LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking());  }  void LLPanelAvatarNotes::setAvatarId(const LLUUID& id) @@ -518,7 +518,7 @@ BOOL LLPanelAvatarProfile::postBuild()  	pic = getChild<LLTextureCtrl>("real_world_pic");  	pic->setFallbackImageName("default_profile_picture.j2c"); -	gVoiceClient->addObserver((LLVoiceClientStatusObserver*)this); +	LLVoiceClient::getInstance()->addObserver((LLVoiceClientStatusObserver*)this);  	resetControls();  	resetData(); @@ -809,7 +809,7 @@ void LLPanelAvatarProfile::onChange(EStatusType status, const std::string &chann  		return;  	} -	childSetEnabled("call", LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking()); +	childSetEnabled("call", LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking());  }  void LLPanelAvatarProfile::setAvatarId(const LLUUID& id) diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp index c00b6a4147..716166a945 100644 --- a/indra/newview/llpanelgroup.cpp +++ b/indra/newview/llpanelgroup.cpp @@ -201,7 +201,7 @@ BOOL LLPanelGroup::postBuild()  		mJoinText = panel_general->getChild<LLUICtrl>("join_cost_text");  	} -	gVoiceClient->addObserver(this); +	LLVoiceClient::getInstance()->addObserver(this);  	return TRUE;  } @@ -322,7 +322,7 @@ void LLPanelGroup::onChange(EStatusType status, const std::string &channelURI, b  		return;  	} -	childSetEnabled("btn_call", LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking()); +	childSetEnabled("btn_call", LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking());  }  void LLPanelGroup::notifyObservers() diff --git a/indra/newview/llpanelimcontrolpanel.cpp b/indra/newview/llpanelimcontrolpanel.cpp index c34f0633b9..709bb83fe4 100644 --- a/indra/newview/llpanelimcontrolpanel.cpp +++ b/indra/newview/llpanelimcontrolpanel.cpp @@ -81,7 +81,8 @@ void LLPanelChatControlPanel::onVoiceChannelStateChanged(const LLVoiceChannel::E  void LLPanelChatControlPanel::updateCallButton()  { -	bool voice_enabled = LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking(); +	// hide/show call button +	bool voice_enabled = LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking();  	LLIMModel::LLIMSession* session = LLIMModel::getInstance()->findIMSession(mSessionId); @@ -124,7 +125,7 @@ BOOL LLPanelChatControlPanel::postBuild()  	childSetAction("end_call_btn", boost::bind(&LLPanelChatControlPanel::onEndCallButtonClicked, this));  	childSetAction("voice_ctrls_btn", boost::bind(&LLPanelChatControlPanel::onOpenVoiceControlsClicked, this)); -	gVoiceClient->addObserver(this); +	LLVoiceClient::getInstance()->addObserver(this);  	return TRUE;  } diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index ee4dcc44fe..42e4b397db 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -51,11 +51,12 @@  #include "llfocusmgr.h"  #include "lllineeditor.h"  #include "llnotificationsutil.h" +#include "llsecapi.h"  #include "llstartup.h"  #include "lltextbox.h"  #include "llui.h"  #include "lluiconstants.h" -#include "llurlsimstring.h" +#include "llslurl.h"  #include "llversioninfo.h"  #include "llviewerhelp.h"  #include "llviewertexturelist.h" @@ -77,6 +78,7 @@  #pragma warning(disable: 4355)      // 'this' used in initializer list  #endif  // LL_WINDOWS +#include "llsdserialize.h"  #define USE_VIEWER_AUTH 0  const S32 BLACK_BORDER_HEIGHT = 160; @@ -104,7 +106,6 @@ public:  LLLoginRefreshHandler gLoginRefreshHandler; -  // helper class that trys to download a URL from a web site and calls a method   // on parent class indicating if the web server is working or not  class LLIamHereLogin : public LLHTTPClient::Responder @@ -153,10 +154,6 @@ namespace {  	boost::intrusive_ptr< LLIamHereLogin > gResponsePtr = 0;  }; -void set_start_location(LLUICtrl* ctrl, void* data) -{ -    LLURLSimString::setString(ctrl->getValue().asString()); -}  //---------------------------------------------------------------------------  // Public methods @@ -187,6 +184,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect,  		delete LLPanelLogin::sInstance;  	} +	mPasswordModified = FALSE;  	LLPanelLogin::sInstance = this;  	// add to front so we are the bottom-most child @@ -213,10 +211,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect,  	}  #if !USE_VIEWER_AUTH -	childSetPrevalidate("first_name_edit", LLTextValidate::validateASCIIPrintableNoSpace); -	childSetPrevalidate("last_name_edit", LLTextValidate::validateASCIIPrintableNoSpace); - -	childSetCommitCallback("password_edit", mungePassword, this); +	childSetPrevalidate("username_edit", LLTextValidate::validateASCIIPrintableNoPipe);  	getChild<LLLineEditor>("password_edit")->setKeystrokeCallback(onPassKey, this);  	// change z sort of clickable text to be behind buttons @@ -228,27 +223,19 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect,  	LLComboBox* combo = getChild<LLComboBox>("start_location_combo"); -	std::string sim_string = LLURLSimString::sInstance.mSimString; -	if(sim_string.empty()) -	{ -		LLURLSimString::setString(gSavedSettings.getString("LoginLocation")); -		sim_string = LLURLSimString::sInstance.mSimString; -	} - -	if (!sim_string.empty()) +	if(LLStartUp::getStartSLURL().getType() != LLSLURL::LOCATION)  	{ -		// Replace "<Type region name>" with this region name -		combo->remove(2); -		combo->add( sim_string ); -		combo->setTextEntry(sim_string); -		combo->setCurrentByIndex( 2 ); +		LLSLURL slurl(gSavedSettings.getString("LoginLocation")); +		LLStartUp::setStartSLURL(slurl);  	} - -	combo->setCommitCallback( &set_start_location, NULL ); +	updateLocationCombo(false); +	 +	combo->setCommitCallback(onSelectLocation, NULL);  	LLComboBox* server_choice_combo = sInstance->getChild<LLComboBox>("server_combo");  	server_choice_combo->setCommitCallback(onSelectServer, NULL);  	server_choice_combo->setFocusLostCallback(boost::bind(onServerComboLostFocus, _1)); +	updateServerCombo();  	childSetAction("connect_btn", onClickConnect, this); @@ -304,17 +291,10 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect,  	// kick off a request to grab the url manually  	gResponsePtr = LLIamHereLogin::build( this ); -	std::string login_page = gSavedSettings.getString("LoginPage"); -	if (login_page.empty()) -	{ -		login_page = getString( "real_url" ); -	} -	LLHTTPClient::head( login_page, gResponsePtr ); -#if !USE_VIEWER_AUTH -	// Initialize visibility (and don't force visibility - use prefs) -	refreshLocation( false ); -#endif +	LLHTTPClient::head( LLGridManager::getInstance()->getLoginPage(), gResponsePtr ); +	 +	updateLocationCombo(false);  } @@ -378,21 +358,6 @@ void LLPanelLogin::setSiteIsAlive( bool alive )  	}  } -void LLPanelLogin::mungePassword(LLUICtrl* caller, void* user_data) -{ -	LLPanelLogin* self = (LLPanelLogin*)user_data; -	LLLineEditor* editor = (LLLineEditor*)caller; -	std::string password = editor->getText(); - -	// Re-md5 if we've changed at all -	if (password != self->mIncomingPassword) -	{ -		LLMD5 pass((unsigned char *)password.c_str()); -		char munged_password[MD5HEX_STR_SIZE]; -		pass.hex_digest(munged_password); -		self->mMungedPassword = munged_password; -	} -}  LLPanelLogin::~LLPanelLogin()  { @@ -499,14 +464,14 @@ void LLPanelLogin::giveFocus()  	if( sInstance )  	{  		// Grab focus and move cursor to first blank input field -		std::string first = sInstance->childGetText("first_name_edit"); +		std::string username = sInstance->childGetText("username_edit");  		std::string pass = sInstance->childGetText("password_edit"); -		BOOL have_first = !first.empty(); +		BOOL have_username = !username.empty();  		BOOL have_pass = !pass.empty();  		LLLineEditor* edit = NULL; -		if (have_first && !have_pass) +		if (have_username && !have_pass)  		{  			// User saved his name but not his password.  Move  			// focus to password field. @@ -515,7 +480,7 @@ void LLPanelLogin::giveFocus()  		else  		{  			// User doesn't have a name, so start there. -			edit = sInstance->getChild<LLLineEditor>("first_name_edit"); +			edit = sInstance->getChild<LLLineEditor>("username_edit");  		}  		if (edit) @@ -537,8 +502,8 @@ void LLPanelLogin::showLoginWidgets()  	// *TODO: Append all the usual login parameters, like first_login=Y etc.  	std::string splash_screen_url = sInstance->getString("real_url");  	web_browser->navigateTo( splash_screen_url, "text/html" ); -	LLUICtrl* first_name_edit = sInstance->getChild<LLUICtrl>("first_name_edit"); -	first_name_edit->setFocus(TRUE); +	LLUICtrl* username_edit = sInstance->getChild<LLUICtrl>("username_edit"); +	username_edit->setFocus(TRUE);  }  // static @@ -560,77 +525,120 @@ void LLPanelLogin::show(const LLRect &rect,  }  // static -void LLPanelLogin::setFields(const std::string& firstname, -			     const std::string& lastname, -			     const std::string& password) +void LLPanelLogin::setFields(LLPointer<LLCredential> credential, +							 BOOL remember)  {  	if (!sInstance)  	{  		llwarns << "Attempted fillFields with no login view shown" << llendl;  		return;  	} +	LL_INFOS("Credentials") << "Setting login fields to " << *credential << LL_ENDL; -	sInstance->childSetText("first_name_edit", firstname); -	sInstance->childSetText("last_name_edit", lastname); - -	// Max "actual" password length is 16 characters. -	// Hex digests are always 32 characters. -	if (password.length() == 32) +	LLSD identifier = credential->getIdentifier(); +	if((std::string)identifier["type"] == "agent")  +	{ +		sInstance->childSetText("username_edit", (std::string)identifier["first_name"] + " " +  +								(std::string)identifier["last_name"]);	 +	} +	else if((std::string)identifier["type"] == "account") +	{ +		sInstance->childSetText("username_edit", (std::string)identifier["account_name"]);		 +	} +	else +	{ +	  sInstance->childSetText("username_edit", std::string());	 +	} +	// if the password exists in the credential, set the password field with +	// a filler to get some stars +	LLSD authenticator = credential->getAuthenticator(); +	LL_INFOS("Credentials") << "Setting authenticator field " << authenticator["type"].asString() << LL_ENDL; +	if(authenticator.isMap() &&  +	   authenticator.has("secret") &&  +	   (authenticator["secret"].asString().size() > 0))  	{ +		  		// This is a MD5 hex digest of a password.  		// We don't actually use the password input field,   		// fill it with MAX_PASSWORD characters so we get a   		// nice row of asterixes.  		const std::string filler("123456789!123456"); -		sInstance->childSetText("password_edit", filler); -		sInstance->mIncomingPassword = filler; -		sInstance->mMungedPassword = password; +		sInstance->childSetText("password_edit", std::string("123456789!123456"));  	}  	else  	{ -		// this is a normal text password -		sInstance->childSetText("password_edit", password); -		sInstance->mIncomingPassword = password; -		LLMD5 pass((unsigned char *)password.c_str()); -		char munged_password[MD5HEX_STR_SIZE]; -		pass.hex_digest(munged_password); -		sInstance->mMungedPassword = munged_password; +		sInstance->childSetText("password_edit", std::string());		  	} +	sInstance->childSetValue("remember_check", remember);  }  // static -void LLPanelLogin::addServer(const std::string& server, S32 domain_name) +void LLPanelLogin::getFields(LLPointer<LLCredential>& credential, +							 BOOL remember)  {  	if (!sInstance)  	{ -		llwarns << "Attempted addServer with no login view shown" << llendl; +		llwarns << "Attempted getFields with no login view shown" << llendl;  		return;  	} +	 +	// load the credential so we can pass back the stored password or hash if the user did +	// not modify the password field. +	 +	credential = gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGrid()); -	LLComboBox* combo = sInstance->getChild<LLComboBox>("server_combo"); -	combo->add(server, LLSD(domain_name) ); -	combo->setCurrentByIndex(0); -} - -// static -void LLPanelLogin::getFields(std::string *firstname, -			     std::string *lastname, -			     std::string *password) -{ -	if (!sInstance) +	LLSD identifier = LLSD::emptyMap(); +	LLSD authenticator = LLSD::emptyMap(); +	 +	if(credential.notNull())  	{ -		llwarns << "Attempted getFields with no login view shown" << llendl; -		return; +		authenticator = credential->getAuthenticator();  	} -	*firstname = sInstance->childGetText("first_name_edit"); -	LLStringUtil::trim(*firstname); - -	*lastname = sInstance->childGetText("last_name_edit"); -	LLStringUtil::trim(*lastname); +	std::string username = sInstance->childGetText("username_edit"); +	LLStringUtil::trim(username); +	std::string password = sInstance->childGetText("password_edit"); -	*password = sInstance->mMungedPassword; +	LL_INFOS2("Credentials", "Authentication") << "retrieving username:" << username << LL_ENDL; +	// determine if the username is a first/last form or not. +	size_t separator_index = username.find_first_of(' '); +	if (separator_index == username.npos) +	{ +		LL_INFOS2("Credentials", "Authentication") << "account: " << username << LL_ENDL; +		// single username, so this is a 'clear' identifier +		identifier["type"] = "account"; +		identifier["account_name"] = username; +		 +		if (LLPanelLogin::sInstance->mPasswordModified) +		{ +			authenticator = LLSD::emptyMap(); +			// password is plaintext +			authenticator["type"] = "clear"; +			authenticator["secret"] = password; +		} +	} +	else if (separator_index == username.find_last_of(' ')) +	{ +		LL_INFOS2("Credentials", "Authentication") << "agent: " << username << LL_ENDL; +		// traditional firstname / lastname +		identifier["type"] = "agent"; +		identifier["first_name"] = username.substr(0, separator_index); +		identifier["last_name"] = username.substr(separator_index+1, username.npos); +		 +		if (LLPanelLogin::sInstance->mPasswordModified) +		{ +			authenticator = LLSD::emptyMap(); +			authenticator["type"] = "hash"; +			authenticator["algorithm"] = "md5"; +			LLMD5 pass((const U8 *)password.c_str()); +			char md5pass[33];               /* Flawfinder: ignore */ +			pass.hex_digest(md5pass); +			authenticator["secret"] = md5pass; +		} +	} +	credential = gSecAPIHandler->createCredential(LLGridManager::getInstance()->getGrid(), identifier, authenticator); +	remember = sInstance->childGetValue("remember_check");  }  // static @@ -650,64 +658,147 @@ BOOL LLPanelLogin::isGridComboDirty()  }  // static -void LLPanelLogin::getLocation(std::string &location) +BOOL LLPanelLogin::areCredentialFieldsDirty()  {  	if (!sInstance)  	{ -		llwarns << "Attempted getLocation with no login view shown" << llendl; -		return; +		llwarns << "Attempted getServer with no login view shown" << llendl;  	} -	 -	LLComboBox* combo = sInstance->getChild<LLComboBox>("start_location_combo"); -	location = combo->getValue().asString(); +	else +	{ +		std::string username = sInstance->childGetText("username_edit"); +		LLStringUtil::trim(username); +		std::string password = sInstance->childGetText("password_edit"); +		LLLineEditor* ctrl = sInstance->getChild<LLLineEditor>("username_edit"); +		if(ctrl && ctrl->isDirty()) +		{ +			return true; +		} +		ctrl = sInstance->getChild<LLLineEditor>("password_edit"); +		if(ctrl && ctrl->isDirty())  +		{ +			return true; +		} +	} +	return false;	  } +  // static -void LLPanelLogin::refreshLocation( bool force_visible ) +void LLPanelLogin::updateLocationCombo( bool force_visible )  { -	if (!sInstance) return; - -#if USE_VIEWER_AUTH -	loadLoginPage(); -#else +	if (!sInstance)  +	{ +		return; +	}	 +	 +	llinfos << "updatelocationcombo " << LLStartUp::getStartSLURL().asString() << llendl; +	LLComboBox* combo = sInstance->getChild<LLComboBox>("start_location_combo"); +	 +	switch(LLStartUp::getStartSLURL().getType()) +	{ +		case LLSLURL::LOCATION: +		{ +			 +			combo->setCurrentByIndex( 2 );	 +			combo->setTextEntry(LLStartUp::getStartSLURL().getLocationString());	 +			break; +		} +		case LLSLURL::HOME_LOCATION: +			combo->setCurrentByIndex(1); +			break; +		default: +			combo->setCurrentByIndex(0); +			break; +	} +	  	BOOL show_start = TRUE; - +	  	if ( ! force_visible ) -	{ -		// Don't show on first run after install -		// Otherwise ShowStartLocation defaults to true.  		show_start = gSavedSettings.getBOOL("ShowStartLocation"); -	} - -	// Update the value of the location combo. -	updateLocationUI();  	sInstance->childSetVisible("start_location_combo", show_start);  	sInstance->childSetVisible("start_location_text", show_start); - -	BOOL show_server = gSavedSettings.getBOOL("ForceShowGrid"); -	sInstance->childSetVisible("server_combo", show_server); - -#endif +	 +	sInstance->childSetVisible("server_combo", TRUE);  }  // static -void LLPanelLogin::updateLocationUI() +void LLPanelLogin::onSelectLocation(LLUICtrl*, void*)  {  	if (!sInstance) return; -	std::string sim_string = LLURLSimString::sInstance.mSimString; -	if (!sim_string.empty()) +	LLComboBox* combo = sInstance->getChild<LLComboBox>("start_location_combo"); +	S32 index = combo->getCurrentIndex(); +	 +	switch (index)  	{ -		// Replace "<Type region name>" with this region name -		LLComboBox* combo = sInstance->getChild<LLComboBox>("start_location_combo"); -		combo->remove(2); -		combo->add( sim_string ); -		combo->setTextEntry(sim_string); -		combo->setCurrentByIndex( 2 ); +		case 2: +		{ +			LLSLURL slurl = LLSLURL(combo->getSelectedValue()); +			if((slurl.getType() == LLSLURL::LOCATION) && +			   (slurl.getGrid() != LLStartUp::getStartSLURL().getGrid())) +			{ +				 + +				// we've changed the grid, so update the grid selection +				try  +				{ +					LLStartUp::setStartSLURL(slurl); +				} +				catch (LLInvalidGridName ex) +				{ +					LLSD args;	 +					args["GRID"] = slurl.getGrid(); +					LLNotificationsUtil::add("InvalidGrid", args); +					return;  +				}	 +				loadLoginPage(); +			} +			break; +		} +		case 1: +		{ +			LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_HOME)); +			break; +		} +		default: +		{ +			LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_LAST)); +			break; +		}  	}  } + +// static +void LLPanelLogin::getLocation(LLSLURL& slurl) +{ +	LLSLURL result; +	if (!sInstance) +	{ +		llwarns << "Attempted getLocation with no login view shown" << llendl; +	} +	 +	LLComboBox* combo = sInstance->getChild<LLComboBox>("start_location_combo"); +	 +	switch(combo->getCurrentIndex()) +	{ +		case 0: +			slurl = LLSLURL(LLSLURL::SIM_LOCATION_HOME); +		case 1: +			slurl =  LLSLURL(LLSLURL::SIM_LOCATION_LAST); +		default: +			slurl = LLSLURL(combo->getValue().asString()); +	} +} + +void LLPanelLogin::setLocation(const LLSLURL& slurl) +{ +	LLStartUp::setStartSLURL(slurl); +	updateServer(); +} +  // static  void LLPanelLogin::closePanel()  { @@ -741,15 +832,13 @@ void LLPanelLogin::loadLoginPage()  	std::ostringstream oStr; -	std::string login_page = gSavedSettings.getString("LoginPage"); -	if (login_page.empty()) -	{ -		login_page = sInstance->getString( "real_url" ); -	} +	std::string login_page = LLGridManager::getInstance()->getLoginPage(); +  	oStr << login_page;  	// Use the right delimeter depending on how LLURI parses the URL  	LLURI login_page_uri = LLURI(login_page); +	  	std::string first_query_delimiter = "&";  	if (login_page_uri.queryMap().size() == 0)  	{ @@ -781,11 +870,10 @@ void LLPanelLogin::loadLoginPage()  	curl_free(curl_version);  	// Grid -	char* curl_grid = curl_escape(LLViewerLogin::getInstance()->getGridLabel().c_str(), 0); +	char* curl_grid = curl_escape(LLGridManager::getInstance()->getGridLoginID().c_str(), 0);  	oStr << "&grid=" << curl_grid;  	curl_free(curl_grid); - -	gViewerWindow->setMenuBackgroundColor(false, !LLViewerLogin::getInstance()->isInProductionGrid()); +	gViewerWindow->setMenuBackgroundColor(false, !LLGridManager::getInstance()->isInProductionGrid());  	gLoginMenuBarView->setBackgroundColor(gMenuBarView->getBackgroundColor()); @@ -810,30 +898,20 @@ void LLPanelLogin::loadLoginPage()  		location = gSavedSettings.getString("LoginLocation");  	} -	std::string firstname, lastname; +	std::string username;      if(gSavedSettings.getLLSD("UserLoginInfo").size() == 3)      {          LLSD cmd_line_login = gSavedSettings.getLLSD("UserLoginInfo"); -		firstname = cmd_line_login[0].asString(); -		lastname = cmd_line_login[1].asString(); +		username = cmd_line_login[0].asString() + " " + cmd_line_login[1];          password = cmd_line_login[2].asString();      } -	if (firstname.empty()) -	{ -		firstname = gSavedSettings.getString("FirstName"); -	} -	 -	if (lastname.empty()) -	{ -		lastname = gSavedSettings.getString("LastName"); -	}  	char* curl_region = curl_escape(region.c_str(), 0); -	oStr <<"firstname=" << firstname << -		"&lastname=" << lastname << "&location=" << location <<	"®ion=" << curl_region; +	oStr <<"username=" << username << +		 "&location=" << location <<	"®ion=" << curl_region;  	curl_free(curl_region); @@ -866,7 +944,7 @@ void LLPanelLogin::loadLoginPage()  #endif  	LLMediaCtrl* web_browser = sInstance->getChild<LLMediaCtrl>("login_html"); -	 +  	// navigate to the "real" page  	if (gSavedSettings.getBOOL("RegInClient"))  	{ @@ -915,34 +993,33 @@ void LLPanelLogin::onClickConnect(void *)  		// JC - Make sure the fields all get committed.  		sInstance->setFocus(FALSE); -		std::string first = sInstance->childGetText("first_name_edit"); -		std::string last  = sInstance->childGetText("last_name_edit"); -		LLComboBox* combo = sInstance->getChild<LLComboBox>("start_location_combo"); -		std::string combo_text = combo->getSimple(); -		 -		bool has_first_and_last = !(first.empty() || last.empty()); -		bool has_location = false; - -		if(combo_text=="<Type region name>" || combo_text =="") +		LLComboBox* combo = sInstance->getChild<LLComboBox>("server_combo"); +		LLSD combo_val = combo->getSelectedValue(); +		if (combo_val.isUndefined())  		{ -			// *NOTE: Mani - Location field is not always committed by this point! -			// This may be duplicate work, but better than not doing the work! -			LLURLSimString::sInstance.setString(""); +			combo_val = combo->getValue();  		} -		else  +		if(combo_val.isUndefined())  		{ -			// *NOTE: Mani - Location field is not always committed by this point! -			LLURLSimString::sInstance.setString(combo_text); -			has_location = true; +			LLNotificationsUtil::add("StartRegionEmpty"); +			return; +		}		 +		try +		{ +			LLGridManager::getInstance()->setGridChoice(combo_val.asString());  		} - -		if(!has_first_and_last) +		catch (LLInvalidGridName ex)  		{ -			LLNotificationsUtil::add("MustHaveAccountToLogIn"); +			LLSD args; +			args["GRID"] = combo_val.asString(); +			LLNotificationsUtil::add("InvalidGrid", args); +			return;  		} -		else if(!has_location) +		 +		std::string username = sInstance->childGetText("username_edit"); +		if(username.empty())  		{ -			LLNotificationsUtil::add("StartRegionEmpty"); +			LLNotificationsUtil::add("MustHaveAccountToLogIn");  		}  		else  		{ @@ -1005,6 +1082,8 @@ void LLPanelLogin::onClickHelp(void*)  // static  void LLPanelLogin::onPassKey(LLLineEditor* caller, void* user_data)  { +	LLPanelLogin *This = (LLPanelLogin *) user_data; +	This->mPasswordModified = TRUE;  	if (gKeyboard->getKeyDown(KEY_CAPSLOCK) && sCapslockDidNotification == FALSE)  	{  		LLNotificationsUtil::add("CapsKeyOn"); @@ -1012,54 +1091,90 @@ void LLPanelLogin::onPassKey(LLLineEditor* caller, void* user_data)  	}  } -// static -void LLPanelLogin::onSelectServer(LLUICtrl*, void*) -{ -	// *NOTE: The paramters for this method are ignored.  -	// LLPanelLogin::onServerComboLostFocus(LLFocusableElement* fe, void*) -	// calls this method. - -	// The user twiddled with the grid choice ui. -	// apply the selection to the grid setting. -	std::string grid_label; -	S32 grid_index; -	LLComboBox* combo = sInstance->getChild<LLComboBox>("server_combo"); -	LLSD combo_val = combo->getValue(); - -	if (LLSD::TypeInteger == combo_val.type()) +void LLPanelLogin::updateServer() +{ +	try   	{ -		grid_index = combo->getValue().asInteger(); -		if ((S32)GRID_INFO_OTHER == grid_index) +		updateServerCombo();	 +		// if they've selected another grid, we should load the credentials +		// for that grid and set them to the UI. +		if(sInstance && !sInstance->areCredentialFieldsDirty())  		{ -			// This happens if the user specifies a custom grid -			// via command line. -			grid_label = combo->getSimple(); +			LLPointer<LLCredential> credential = gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGrid());	 +			bool remember = sInstance->childGetValue("remember_check"); +			sInstance->setFields(credential, remember);  		} +		// grid changed so show new splash screen (possibly) +		loadLoginPage(); +		updateLocationCombo(LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION);  	} -	else +	catch (LLInvalidGridName ex)  	{ -		// no valid selection, return other -		grid_index = (S32)GRID_INFO_OTHER; -		grid_label = combo_val.asString(); +		// do nothing  	} +} -	// This new seelction will override preset uris -	// from the command line. -	LLViewerLogin* vl = LLViewerLogin::getInstance(); -	vl->resetURIs(); -	if(grid_index != GRID_INFO_OTHER) +void LLPanelLogin::updateServerCombo() +{ +	if (!sInstance)   	{ -		vl->setGridChoice((EGridInfo)grid_index); +		return;	  	} -	else +	// We add all of the possible values, sorted, and then add a bar and the current value at the top +	LLComboBox* server_choice_combo = sInstance->getChild<LLComboBox>("server_combo");	 +	server_choice_combo->removeall(); +#ifdef LL_RELEASE_FOR_DOWNLOAD +	std::map<std::string, std::string> known_grids = LLGridManager::getInstance()->getKnownGrids(TRUE); +#else +	std::map<std::string, std::string> known_grids = LLGridManager::getInstance()->getKnownGrids(FALSE);	 +#endif +	for (std::map<std::string, std::string>::iterator grid_choice = known_grids.begin(); +		 grid_choice != known_grids.end(); +		 grid_choice++)  	{ -		vl->setGridChoice(grid_label); +		if (!grid_choice->first.empty()) +		{ +			server_choice_combo->add(grid_choice->second, grid_choice->first, ADD_SORTED); +		}  	} +	 +	server_choice_combo->addSeparator(ADD_TOP); +	 +	server_choice_combo->add(LLGridManager::getInstance()->getGridLabel(),  +		LLGridManager::getInstance()->getGrid(), ADD_TOP);	 +	 +	server_choice_combo->selectFirstItem();	 +} -	// grid changed so show new splash screen (possibly) -	loadLoginPage(); +// static +void LLPanelLogin::onSelectServer(LLUICtrl*, void*) +{ +	// *NOTE: The paramters for this method are ignored.  +	// LLPanelLogin::onServerComboLostFocus(LLFocusableElement* fe, void*) +	// calls this method. +	LL_INFOS("AppInit") << "onSelectServer" << LL_ENDL; +	// The user twiddled with the grid choice ui. +	// apply the selection to the grid setting. +	LLPointer<LLCredential> credential; +	 +	LLComboBox* combo = sInstance->getChild<LLComboBox>("server_combo"); +	LLSD combo_val = combo->getSelectedValue(); +	if (combo_val.isUndefined()) +	{ +		combo_val = combo->getValue(); +	} +	 +	combo = sInstance->getChild<LLComboBox>("start_location_combo");	 +	combo->setCurrentByIndex(1); +	LLStartUp::setStartSLURL(LLSLURL(gSavedSettings.getString("LoginLocation"))); +	LLGridManager::getInstance()->setGridChoice(combo_val.asString()); +	// This new selection will override preset uris +	// from the command line. +	updateServer(); +	updateLocationCombo(false); +	updateLoginPanelLinks();  }  void LLPanelLogin::onServerComboLostFocus(LLFocusableElement* fe) @@ -1072,3 +1187,14 @@ void LLPanelLogin::onServerComboLostFocus(LLFocusableElement* fe)  		onSelectServer(combo, NULL);	  	}  } + +void LLPanelLogin::updateLoginPanelLinks() +{ +	LLSD grid_data = LLGridManager::getInstance()->getGridInfo(); +	bool system_grid = grid_data.has(GRID_IS_SYSTEM_GRID_VALUE); +	 +	// need to call through sInstance, as it's called from onSelectServer, which +	// is static. +	sInstance->childSetVisible("create_new_account_text", system_grid); +	sInstance->childSetVisible("forgot_password_text", system_grid); +} diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h index 1fdc3a9361..9301c263da 100644 --- a/indra/newview/llpanellogin.h +++ b/indra/newview/llpanellogin.h @@ -41,6 +41,8 @@  class LLLineEditor;  class LLUIImage;  class LLPanelLoginListener; +class LLSLURL; +class LLCredential;  class LLPanelLogin:	  	public LLPanel, @@ -65,20 +67,16 @@ public:  		void (*callback)(S32 option, void* user_data),   		void* callback_data); -	// Remember password checkbox is set via gSavedSettings "RememberPassword" -	static void setFields(const std::string& firstname, const std::string& lastname,  -		const std::string& password); +	static void setFields(LLPointer<LLCredential> credential, BOOL remember); -	static void addServer(const std::string& server, S32 domain_name); -	static void refreshLocation( bool force_visible ); -	static void updateLocationUI(); - -	static void getFields(std::string *firstname, std::string *lastname, -						  std::string *password); +	static void getFields(LLPointer<LLCredential>& credential, BOOL remember);  	static BOOL isGridComboDirty(); -	static void getLocation(std::string &location); - +	static BOOL areCredentialFieldsDirty(); +	static void getLocation(LLSLURL& slurl); +	static void setLocation(const LLSLURL& slurl); +	 +	static void updateLocationCombo(bool force_visible);  // simply update the combo box  	static void closePanel();  	void setSiteIsAlive( bool alive ); @@ -86,10 +84,10 @@ public:  	static void loadLoginPage();	  	static void giveFocus();  	static void setAlwaysRefresh(bool refresh);  -	static void mungePassword(LLUICtrl* caller, void* user_data);  	// inherited from LLViewerMediaObserver  	/*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event); +	static void updateServer();  // update the combo box, change the login page to the new server, clear the combo  private:  	friend class LLPanelLoginListener; @@ -103,6 +101,10 @@ private:  	static void onPassKey(LLLineEditor* caller, void* user_data);  	static void onSelectServer(LLUICtrl*, void*);  	static void onServerComboLostFocus(LLFocusableElement*); +	static void updateServerCombo(); +	static void onSelectLocation(LLUICtrl*, void*); +	 +	static void updateLoginPanelLinks();  private:  	LLPointer<LLUIImage> mLogoImage; @@ -111,8 +113,7 @@ private:  	void			(*mCallback)(S32 option, void *userdata);  	void*			mCallbackData; -	std::string mIncomingPassword; -	std::string mMungedPassword; +	BOOL            mPasswordModified;  	static LLPanelLogin* sInstance;  	static BOOL		sCapslockDidNotification; diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp index a67270202e..075b22307d 100644 --- a/indra/newview/llpaneloutfitsinventory.cpp +++ b/indra/newview/llpaneloutfitsinventory.cpp @@ -48,6 +48,7 @@  #include "lllandmark.h"  #include "lllineeditor.h"  #include "llmodaldialog.h" +#include "llnotificationsutil.h"  #include "llsidepanelappearance.h"  #include "llsidetray.h"  #include "lltabcontainer.h" @@ -68,75 +69,13 @@ static const std::string COF_TAB_NAME = "cof_tab";  static LLRegisterPanelClassWrapper<LLPanelOutfitsInventory> t_inventory("panel_outfits_inventory");  bool LLPanelOutfitsInventory::sShowDebugEditor = false; -class LLOutfitSaveAsDialog : public LLModalDialog -{ -private: -	std::string	mItemName; -	std::string mTempItemName; -	 -	boost::signals2::signal<void (const std::string&)> mSaveAsSignal; - -public: -	LLOutfitSaveAsDialog( const LLSD& key ) -		: LLModalDialog( key ), -		  mTempItemName(key.asString()) -	{ -	} -		 -	BOOL postBuild() -	{ -		getChild<LLUICtrl>("Save")->setCommitCallback(boost::bind(&LLOutfitSaveAsDialog::onSave, this )); -		getChild<LLUICtrl>("Cancel")->setCommitCallback(boost::bind(&LLOutfitSaveAsDialog::onCancel, this )); -		 -		childSetTextArg("name ed", "[DESC]", mTempItemName); -		return TRUE; -	} - -	void setSaveAsCommit( const boost::signals2::signal<void (const std::string&)>::slot_type& cb ) -	{ -		mSaveAsSignal.connect(cb); -	} - -	virtual void onOpen(const LLSD& key) -	{ -		LLLineEditor* edit = getChild<LLLineEditor>("name ed"); -		if (edit) -		{ -			edit->setFocus(TRUE); -			edit->selectAll(); -		} -	} -	void onSave() -	{ -		mItemName = childGetValue("name ed").asString(); -		LLStringUtil::trim(mItemName); -		if( !mItemName.empty() ) -		{ -			mSaveAsSignal(mItemName); -			closeFloater(); // destroys this object -		} -	} - -	void onCancel() -	{ -		closeFloater(); // destroys this object -	} -}; -	  LLPanelOutfitsInventory::LLPanelOutfitsInventory() :  	mActivePanel(NULL),  	mParent(NULL)  {  	mSavedFolderState = new LLSaveFolderState();  	mSavedFolderState->setApply(FALSE); - -	static bool registered_dialog = false; -	if (!registered_dialog) -	{ -		LLFloaterReg::add("outfit_save_as", "floater_outfit_save_as.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLOutfitSaveAsDialog>); -		registered_dialog = true; -	}  }  LLPanelOutfitsInventory::~LLPanelOutfitsInventory() @@ -268,6 +207,31 @@ void LLPanelOutfitsInventory::onEdit()  {  } +bool LLPanelOutfitsInventory::onSaveCommit(const LLSD& notification, const LLSD& response) +{ +	S32 option = LLNotificationsUtil::getSelectedOption(notification, response); +	if (0 == option) +	{ +		std::string outfit_name = response["message"].asString(); +		LLStringUtil::trim(outfit_name); +		if( !outfit_name.empty() ) +		{ +			LLUUID outfit_folder = gAgentWearables.makeNewOutfitLinks(outfit_name); +			LLSD key; +			LLSideTray::getInstance()->showPanel("panel_outfits_inventory", key); + +			if (mAppearanceTabs) +			{ +				mAppearanceTabs->selectTabByName(OUTFITS_TAB_NAME); +			}	 +		} +	} + +	return false; +} + + +  void LLPanelOutfitsInventory::onSave()  {  	std::string outfit_name; @@ -277,23 +241,22 @@ void LLPanelOutfitsInventory::onSave()  		outfit_name = LLViewerFolderType::lookupNewCategoryName(LLFolderType::FT_OUTFIT);  	} +	LLSD args; +	args["DESC"] = outfit_name; + +	LLSD payload; +	//payload["ids"].append(*it); +	 +	LLNotificationsUtil::add("SaveOutfitAs", args, payload, boost::bind(&LLPanelOutfitsInventory::onSaveCommit, this, _1, _2)); + +	//) +	 +/*  	LLOutfitSaveAsDialog* save_as_dialog = LLFloaterReg::showTypedInstance<LLOutfitSaveAsDialog>("outfit_save_as", LLSD(outfit_name), TRUE);  	if (save_as_dialog)  	{  		save_as_dialog->setSaveAsCommit(boost::bind(&LLPanelOutfitsInventory::onSaveCommit, this, _1 )); -	} -} - -void LLPanelOutfitsInventory::onSaveCommit(const std::string& outfit_name) -{ -	LLUUID outfit_folder = gAgentWearables.makeNewOutfitLinks(outfit_name); -	LLSD key; -	LLSideTray::getInstance()->showPanel("panel_outfits_inventory", key); - -	if (mAppearanceTabs) -	{ -		mAppearanceTabs->selectTabByName(OUTFITS_TAB_NAME); -	} +	}*/  }  void LLPanelOutfitsInventory::onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action) diff --git a/indra/newview/llpaneloutfitsinventory.h b/indra/newview/llpaneloutfitsinventory.h index 83c4b8f9cd..41afc2f372 100644 --- a/indra/newview/llpaneloutfitsinventory.h +++ b/indra/newview/llpaneloutfitsinventory.h @@ -61,7 +61,7 @@ public:  	void onEdit();  	void onSave(); -	void onSaveCommit(const std::string& item_name); +	bool onSaveCommit(const LLSD& notification, const LLSD& response);  	void onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action);  	void showEditOutfitPanel(); diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index 288edeb031..7f5e63adee 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -619,7 +619,7 @@ BOOL LLPanelPeople::postBuild()  	if(recent_view_sort)  		mRecentViewSortMenuHandle  = recent_view_sort->getHandle(); -	gVoiceClient->addObserver(this); +	LLVoiceClient::getInstance()->addObserver(this);  	// call this method in case some list is empty and buttons can be in inconsistent state  	updateButtons(); @@ -809,7 +809,7 @@ void LLPanelPeople::updateButtons()  		}  	} -	bool enable_calls = gVoiceClient->voiceWorking() && gVoiceClient->voiceEnabled(); +	bool enable_calls = LLVoiceClient::getInstance()->isVoiceWorking() && LLVoiceClient::getInstance()->voiceEnabled();  	buttonSetEnabled("teleport_btn",		friends_tab_active && item_selected && isFriendOnline(selected_uuids.front()));  	buttonSetEnabled("view_profile_btn",	item_selected); diff --git a/indra/newview/llpanelplacestab.cpp b/indra/newview/llpanelplacestab.cpp index 9806b8c64d..6b12796e59 100644 --- a/indra/newview/llpanelplacestab.cpp +++ b/indra/newview/llpanelplacestab.cpp @@ -70,10 +70,7 @@ void LLPanelPlacesTab::onRegionResponse(const LLVector3d& landmark_global_pos,  	std::string sl_url;  	if ( gotSimName )  	{ -		F32 region_x = (F32)fmod( landmark_global_pos.mdV[VX], (F64)REGION_WIDTH_METERS ); -		F32 region_y = (F32)fmod( landmark_global_pos.mdV[VY], (F64)REGION_WIDTH_METERS ); - -		sl_url = LLSLURL::buildSLURL(sim_name, llround(region_x), llround(region_y), llround((F32)landmark_global_pos.mdV[VZ])); +		sl_url = LLSLURL(sim_name, landmark_global_pos).getSLURLString();  	}  	else  	{ diff --git a/indra/newview/llparticipantlist.cpp b/indra/newview/llparticipantlist.cpp index 0a20ff6226..268738d88c 100644 --- a/indra/newview/llparticipantlist.cpp +++ b/indra/newview/llparticipantlist.cpp @@ -346,7 +346,6 @@ void LLParticipantList::addAvatarIDExceptAgent(const LLUUID& avatar_id)  {  	if (mExcludeAgent && gAgent.getID() == avatar_id) return;  	if (mAvatarList->contains(avatar_id)) return; -  	mAvatarList->getIDs().push_back(avatar_id);  	mAvatarList->setDirty();  	adjustParticipant(avatar_id); @@ -632,7 +631,7 @@ bool LLParticipantList::LLParticipantListMenu::enableContextMenuItem(const LLSD&  	else if (item == "can_call")  	{  		bool not_agent = mUUIDs.front() != gAgentID; -		bool can_call = not_agent && LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking(); +		bool can_call = not_agent &&  LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking();  		return can_call;  	} diff --git a/indra/newview/llsecapi.cpp b/indra/newview/llsecapi.cpp new file mode 100644 index 0000000000..ba343f5387 --- /dev/null +++ b/indra/newview/llsecapi.cpp @@ -0,0 +1,161 @@ +/**  + * @file llsecapi.cpp + * @brief Security API for services such as certificate handling + * secure local storage, etc. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + *  + * Copyright (c) 2009, Linden Research, Inc. + *  + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab.  Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + *  + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + *  + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + *  + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + + +#include "llviewerprecompiledheaders.h" +#include "llsecapi.h" +#include "llsechandler_basic.h" +#include <openssl/evp.h> +#include <map> +#include "llhttpclient.h" + + + +std::map<std::string, LLPointer<LLSecAPIHandler> > gHandlerMap; +LLPointer<LLSecAPIHandler> gSecAPIHandler; + +void initializeSecHandler() +{ +	OpenSSL_add_all_algorithms(); +	OpenSSL_add_all_ciphers(); +	OpenSSL_add_all_digests();	 +	gHandlerMap[BASIC_SECHANDLER] = new LLSecAPIBasicHandler(); +	 +	 +	// Currently, we only have the Basic handler, so we can point the main sechandler +	// pointer to the basic handler.  Later, we'll create a wrapper handler that +	// selects the appropriate sechandler as needed, for instance choosing the +	// mac keyring handler, with fallback to the basic sechandler +	gSecAPIHandler = gHandlerMap[BASIC_SECHANDLER]; + +	// initialize all SecAPIHandlers +	LLProtectedDataException ex = LLProtectedDataException(""); +	std::map<std::string, LLPointer<LLSecAPIHandler> >::const_iterator itr; +	for(itr = gHandlerMap.begin(); itr != gHandlerMap.end(); ++itr) +	{ +		LLPointer<LLSecAPIHandler> handler = (*itr).second; +		try  +		{ +			handler->init(); +		} +		catch (LLProtectedDataException e) +		{ +			ex = e; +		} +	} +	if (ex.getMessage().length() > 0 )  // an exception was thrown. +	{ +		throw ex; +	} + +} +// start using a given security api handler.  If the string is empty +// the default is used +LLPointer<LLSecAPIHandler> getSecHandler(const std::string& handler_type) +{ +	if (gHandlerMap.find(handler_type) != gHandlerMap.end()) +	{ +		return gHandlerMap[handler_type]; +	} +	else +	{ +		return LLPointer<LLSecAPIHandler>(NULL); +	} +} +// register a handler +void registerSecHandler(const std::string& handler_type,  +						LLPointer<LLSecAPIHandler>& handler) +{ +	gHandlerMap[handler_type] = handler; +} + +std::ostream& operator <<(std::ostream& s, const LLCredential& cred) +{ +	return s << (std::string)cred; +} + +	 +// secapiSSLCertVerifyCallback +// basic callback called when a cert verification is requested. +// calls SECAPI to validate the context +// not initialized in the above initialization function, due to unit tests +// see llappviewer + +int secapiSSLCertVerifyCallback(X509_STORE_CTX *ctx, void *param) +{ +	LLURLRequest *req = (LLURLRequest *)param; +	LLPointer<LLCertificateStore> store = gSecAPIHandler->getCertificateStore(""); +	LLPointer<LLCertificateChain> chain = gSecAPIHandler->getCertificateChain(ctx); +	LLSD validation_params = LLSD::emptyMap(); +	LLURI uri(req->getURL()); +	validation_params[CERT_HOSTNAME] = uri.hostName(); +	try +	{ +		chain->validate(VALIDATION_POLICY_SSL, store, validation_params); +	} +	catch (LLCertValidationTrustException& cert_exception) +	{ +		LL_WARNS("AppInit") << "Cert not trusted: " << cert_exception.getMessage() << LL_ENDL; +		return 0;		 +	} +	catch (LLCertException& cert_exception) +	{ +		LL_WARNS("AppInit") << "cert error " << cert_exception.getMessage() << LL_ENDL; +		return 0; +	} +	catch (...) +	{ +		LL_WARNS("AppInit") << "cert error " << LL_ENDL; +		return 0; +	} +	return 1; +} + +LLSD LLCredential::getLoginParams() +{ +	LLSD result = LLSD::emptyMap(); +	if (mIdentifier["type"].asString() == "agent") +	{ +		// legacy credential +		result["passwd"] = "$1$" + mAuthenticator["secret"].asString(); +		result["first"] = mIdentifier["first_name"]; +		result["last"] = mIdentifier["last_name"]; +	 +	} +	else if (mIdentifier["type"].asString() == "account") +	{ +		result["username"] = mIdentifier["account_name"]; +		result["passwd"] = mAuthenticator["secret"]; +                                     +	} +	return result; +} diff --git a/indra/newview/llsecapi.h b/indra/newview/llsecapi.h new file mode 100644 index 0000000000..5211dc2699 --- /dev/null +++ b/indra/newview/llsecapi.h @@ -0,0 +1,493 @@ +/**  + * @file llsecapi.h + * @brief Security API for services such as certificate handling + * secure local storage, etc. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + *  + * Copyright (c) 2009, Linden Research, Inc. + *  + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab.  Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + *  + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + *  + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + *  + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LLSECAPI_H +#define LLSECAPI_H +#include <vector> +#include <openssl/x509.h> +#include <ostream> + +#ifdef LL_WINDOWS +#pragma warning(disable:4250) +#endif // LL_WINDOWS + +// All error handling is via exceptions. + + +#define CERT_SUBJECT_NAME "subject_name" +#define CERT_ISSUER_NAME "issuer_name" +#define CERT_NAME_CN "commonName" +		 +#define CERT_SUBJECT_NAME_STRING "subject_name_string" +#define CERT_ISSUER_NAME_STRING "issuer_name_string" +		 +#define CERT_SERIAL_NUMBER "serial_number" +		 +#define CERT_VALID_FROM "valid_from" +#define CERT_VALID_TO "valid_to" +#define CERT_SHA1_DIGEST "sha1_digest" +#define CERT_MD5_DIGEST "md5_digest" +#define CERT_HOSTNAME "hostname" +#define CERT_BASIC_CONSTRAINTS "basicConstraints" +#define CERT_BASIC_CONSTRAINTS_CA "CA" +#define CERT_BASIC_CONSTRAINTS_PATHLEN "pathLen" + +#define CERT_KEY_USAGE "keyUsage" +#define CERT_KU_DIGITAL_SIGNATURE    "digitalSignature" +#define CERT_KU_NON_REPUDIATION      "nonRepudiation" +#define CERT_KU_KEY_ENCIPHERMENT     "keyEncipherment" +#define CERT_KU_DATA_ENCIPHERMENT    "dataEncipherment" +#define CERT_KU_KEY_AGREEMENT        "keyAgreement" +#define CERT_KU_CERT_SIGN        "certSigning" +#define CERT_KU_CRL_SIGN             "crlSigning" +#define CERT_KU_ENCIPHER_ONLY        "encipherOnly" +#define CERT_KU_DECIPHER_ONLY        "decipherOnly" + +#define BASIC_SECHANDLER "BASIC_SECHANDLER" +#define CERT_VALIDATION_DATE "validation_date" + +#define CERT_EXTENDED_KEY_USAGE "extendedKeyUsage" +#define CERT_EKU_SERVER_AUTH SN_server_auth + +#define CERT_SUBJECT_KEY_IDENTFIER "subjectKeyIdentifier" +#define CERT_AUTHORITY_KEY_IDENTIFIER "authorityKeyIdentifier" +#define CERT_AUTHORITY_KEY_IDENTIFIER_ID "authorityKeyIdentifierId" +#define CERT_AUTHORITY_KEY_IDENTIFIER_NAME "authorityKeyIdentifierName" +#define CERT_AUTHORITY_KEY_IDENTIFIER_SERIAL "authorityKeyIdentifierSerial" + +// validate the current time lies within  +// the validation period of the cert +#define VALIDATION_POLICY_TIME 1 + +// validate that the CA, or some cert in the chain +// lies within the certificate store +#define VALIDATION_POLICY_TRUSTED 2 + +// validate that the subject name of +// the cert contains the passed in hostname +// or validates against the hostname +#define VALIDATION_POLICY_HOSTNAME 4 + + +// validate that the cert contains the SSL EKU +#define VALIDATION_POLICY_SSL_KU 8 + +// validate that the cert contains the SSL EKU +#define VALIDATION_POLICY_CA_KU 16 + +#define VALIDATION_POLICY_CA_BASIC_CONSTRAINTS 32 + +// validate that the cert is correct for SSL +#define VALIDATION_POLICY_SSL (VALIDATION_POLICY_TIME | \ +                               VALIDATION_POLICY_HOSTNAME | \ +                               VALIDATION_POLICY_TRUSTED | \ +                               VALIDATION_POLICY_SSL_KU | \ +                               VALIDATION_POLICY_CA_BASIC_CONSTRAINTS | \ +                               VALIDATION_POLICY_CA_KU) + + + + + + +class LLProtectedDataException +{ +public: +	LLProtectedDataException(const char *msg)  +	{ +		LL_WARNS("SECAPI") << "Protected Data Error: " << (std::string)msg << LL_ENDL; +		mMsg = (std::string)msg; +	} +	std::string getMessage() { return mMsg; } +protected: +	std::string mMsg; +}; + +// class LLCertificate +// parent class providing an interface for certifiate. +// LLCertificates are considered unmodifiable +// Certificates are pulled out of stores, or created via +// factory calls +class LLCertificate : public LLRefCount +{ +	LOG_CLASS(LLCertificate); +public: +	LLCertificate() {} +	 +	virtual ~LLCertificate() {} +	 +	// return a PEM encoded certificate.  The encoding +	// includes the -----BEGIN CERTIFICATE----- and end certificate elements +	virtual std::string getPem() const=0;  +	 +	// return a DER encoded certificate +	virtual std::vector<U8> getBinary() const=0;   +	 +	// return an LLSD object containing information about the certificate +	// such as its name, signature, expiry time, serial number +	virtual LLSD getLLSD() const=0;  +	 +	// return an openSSL X509 struct for the certificate +	virtual X509* getOpenSSLX509() const=0; + +}; + +// class LLCertificateVector +// base class for a list of certificates. + + +class LLCertificateVector : public LLRefCount +{ +	 +public: +	 +	LLCertificateVector() {}; +	virtual ~LLCertificateVector() {}; +	 +	// base iterator implementation class, providing +	// the functionality needed for the iterator class. +	class iterator_impl : public LLRefCount +	{ +	public: +		iterator_impl() {}; +		virtual ~iterator_impl() {}; +		virtual void seek(bool incr)=0; +		virtual LLPointer<iterator_impl> clone() const=0; +		virtual bool equals(const LLPointer<iterator_impl>& _iter) const=0; +		virtual LLPointer<LLCertificate> get()=0; +	}; +	 +	// iterator class +	class iterator +	{ +	public: +		iterator(LLPointer<iterator_impl> impl) : mImpl(impl) {} +		iterator() : mImpl(NULL) {} +		iterator(const iterator& _iter) {mImpl = _iter.mImpl->clone(); } +		~iterator() {} +		iterator& operator++() { if(mImpl.notNull()) mImpl->seek(true); return *this;} +		iterator& operator--() { if(mImpl.notNull()) mImpl->seek(false); return *this;} +		 +		iterator operator++(int) { iterator result = *this; if(mImpl.notNull()) mImpl->seek(true); return result;} +		iterator operator--(int) { iterator result = *this; if(mImpl.notNull()) mImpl->seek(false); return result;} +		LLPointer<LLCertificate> operator*() { return mImpl->get(); }		 +		 +		LLPointer<iterator_impl> mImpl; +	protected: +		friend bool operator==(const LLCertificateVector::iterator& _lhs, const LLCertificateVector::iterator& _rhs); +		bool equals(const iterator& _iter) const { return mImpl->equals(_iter.mImpl); } +	}; +	 +	// numeric indexer +	virtual LLPointer<LLCertificate> operator[](int)=0; +	 +	// Iteration +	virtual iterator begin()=0; +	 +	virtual iterator end()=0; +	 +	// find a cert given params +	virtual iterator find(const LLSD& params) =0; +	 +	// return the number of certs in the store +	virtual int size() const = 0;	 +	 +	// append the cert to the store.  if a copy of the cert already exists in the store, it is removed first +	virtual void  add(LLPointer<LLCertificate> cert)=0; +	 +	// insert the cert to the store.  if a copy of the cert already exists in the store, it is removed first +	virtual void  insert(iterator location, LLPointer<LLCertificate> cert)=0;	 +	 +	// remove a certificate from the store +	virtual LLPointer<LLCertificate> erase(iterator cert)=0;	 +}; + + +// class LLCertificateStore +// represents a store of certificates, typically a store of root CA +// certificates.  The store can be persisted, and can be used to validate +// a cert chain +// +class LLCertificateStore : virtual public LLCertificateVector +{ +	 +public: +	 +	LLCertificateStore() {} +	virtual ~LLCertificateStore() {} +	 +	// persist the store +	virtual void save()=0; +	 +	// return the store id +	virtual std::string storeId() const=0; +}; + +// class LLCertificateChain +// Class representing a chain of certificates in order, with the  +// first element being the child cert. +class LLCertificateChain : virtual public LLCertificateVector +{	 + +public: +	LLCertificateChain() {} +	 +	virtual ~LLCertificateChain() {} + +	// validate a certificate chain given the params. +	// Will throw exceptions on error +	 +	virtual void validate(int validation_policy, +						  LLPointer<LLCertificateStore> ca_store, +						  const LLSD& validation_params) =0; +}; + + + + +inline +bool operator==(const LLCertificateVector::iterator& _lhs, const LLCertificateVector::iterator& _rhs) +{ +	return _lhs.equals(_rhs); +} +inline +bool operator!=(const LLCertificateVector::iterator& _lhs, const LLCertificateVector::iterator& _rhs) +{ +	return !(_lhs == _rhs); +} + + +// +// LLCredential - interface for credentials providing the following functionality: +// * persistance of credential information based on grid (for saving username/password) +// * serialization to an OGP identifier/authenticator pair +//  +class LLCredential  : public LLRefCount +{ +public: +	 +	LLCredential() {} +	 +	LLCredential(const std::string& grid) +	{ +		mGrid = grid; +		mIdentifier = LLSD::emptyMap(); +		mAuthenticator = LLSD::emptyMap(); +	} +	 +	virtual ~LLCredential() {} +	 +	virtual void setCredentialData(const LLSD& identifier, const LLSD& authenticator)  +	{  +		mIdentifier = identifier; +		mAuthenticator = authenticator; +	} +	virtual LLSD getIdentifier() { return mIdentifier; } +	virtual LLSD getAuthenticator() { return mAuthenticator; } +	virtual LLSD getLoginParams(); +	virtual std::string getGrid() { return mGrid; } +	 + +	virtual void clearAuthenticator() { mAuthenticator = LLSD(); }  +	virtual std::string userID() const { return std::string("unknown");} +	virtual std::string asString() const { return std::string("unknown");} +	operator std::string() const { return asString(); } +protected: +	LLSD mIdentifier; +	LLSD mAuthenticator; +	std::string mGrid; +}; + +std::ostream& operator <<(std::ostream& s, const LLCredential& cred); + + +// All error handling is via exceptions. + +class LLCertException +{ +public: +	LLCertException(LLPointer<LLCertificate> cert, const char* msg) +	{ + +		mCert = cert; + +		LL_WARNS("SECAPI") << "Certificate Error: " << (std::string)msg << LL_ENDL; +		mMsg = (std::string)msg; +	} +	LLPointer<LLCertificate> getCert() { return mCert; } +	std::string getMessage() { return mMsg; } +protected: +	LLPointer<LLCertificate> mCert; +	std::string mMsg; +}; + +class LLInvalidCertificate : public LLCertException +{ +public: +	LLInvalidCertificate(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertInvalid") +	{ +	} +protected: +}; + +class LLCertValidationTrustException : public LLCertException +{ +public: +	LLCertValidationTrustException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertUntrusted") +	{ +	} +protected: +}; + +class LLCertValidationHostnameException : public LLCertException +{ +public: +	LLCertValidationHostnameException(std::string hostname, +									  LLPointer<LLCertificate> cert) : LLCertException(cert, "CertInvalidHostname") +	{ +		mHostname = hostname; +	} +	 +	std::string getHostname() { return mHostname; } +protected: +	std::string mHostname; +}; + +class LLCertValidationExpirationException : public LLCertException +{ +public: +	LLCertValidationExpirationException(LLPointer<LLCertificate> cert, +										LLDate current_time) : LLCertException(cert, "CertExpired") +	{ +		mTime = current_time; +	} +	LLDate GetTime() { return mTime; } +protected: +	LLDate mTime; +}; + +class LLCertKeyUsageValidationException : public LLCertException +{ +public: +	LLCertKeyUsageValidationException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertKeyUsage") +	{ +	} +protected: +}; + +class LLCertBasicConstraintsValidationException : public LLCertException +{ +public: +	LLCertBasicConstraintsValidationException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertBasicConstraints") +	{ +	} +protected: +}; + +class LLCertValidationInvalidSignatureException : public LLCertException +{ +public: +	LLCertValidationInvalidSignatureException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertInvalidSignature") +	{ +	} +protected: +}; + +// LLSecAPIHandler Class +// Interface handler class for the various security storage handlers. +class LLSecAPIHandler : public LLRefCount +{ +public: +	 +	 +	LLSecAPIHandler() {} +	virtual ~LLSecAPIHandler() {} +	 +	// initialize the SecAPIHandler +	virtual void init() {}; +	 +	// instantiate a certificate from a pem string +	virtual LLPointer<LLCertificate> getCertificate(const std::string& pem_cert)=0; +	 +	 +	 +	// instiate a certificate from an openssl X509 structure +	virtual LLPointer<LLCertificate> getCertificate(X509* openssl_cert)=0; +	 +	// instantiate a chain from an X509_STORE_CTX +	virtual LLPointer<LLCertificateChain> getCertificateChain(const X509_STORE_CTX* chain)=0; +	 +	// instantiate a cert store given it's id.  if a persisted version +	// exists, it'll be loaded.  If not, one will be created (but not +	// persisted) +	virtual LLPointer<LLCertificateStore> getCertificateStore(const std::string& store_id)=0; +	 +	// persist data in a protected store +	virtual void setProtectedData(const std::string& data_type, +								  const std::string& data_id, +								  const LLSD& data)=0; +	 +	// retrieve protected data +	virtual LLSD getProtectedData(const std::string& data_type, +								  const std::string& data_id)=0; +	 +	// delete a protected data item from the store +	virtual void deleteProtectedData(const std::string& data_type, +									 const std::string& data_id)=0; +	 +	virtual LLPointer<LLCredential> createCredential(const std::string& grid, +													 const LLSD& identifier,  +													 const LLSD& authenticator)=0; +	 +	virtual LLPointer<LLCredential> loadCredential(const std::string& grid)=0; +	 +	virtual void saveCredential(LLPointer<LLCredential> cred, bool save_authenticator)=0; +	 +	virtual void deleteCredential(LLPointer<LLCredential> cred)=0; +	 +}; + +void initializeSecHandler(); +				 +// retrieve a security api depending on the api type +LLPointer<LLSecAPIHandler> getSecHandler(const std::string& handler_type); + +void registerSecHandler(const std::string& handler_type,  +						LLPointer<LLSecAPIHandler>& handler); + +extern LLPointer<LLSecAPIHandler> gSecAPIHandler; + + +int secapiSSLCertVerifyCallback(X509_STORE_CTX *ctx, void *param); + + +#endif // LL_SECAPI_H diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp new file mode 100644 index 0000000000..51e250ffc6 --- /dev/null +++ b/indra/newview/llsechandler_basic.cpp @@ -0,0 +1,1586 @@ +/**  + * @file llsechandler_basic.cpp + * @brief Security API for services such as certificate handling + * secure local storage, etc. + * + * $LicenseInfo:firstyear=2003&license=viewergpl$ + *  + * Copyright (c) 2003-2000, Linden Research, Inc. + *  + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab.  Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + *  + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + *  +LLS * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + *  + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + + +#include "llviewerprecompiledheaders.h" +#include "llsecapi.h" +#include "llsechandler_basic.h" +#include "llsdserialize.h" +#include "llviewernetwork.h" +#include "llxorcipher.h" +#include "llfile.h" +#include "lldir.h" +#include "llviewercontrol.h" +#include <vector> +#include <ios> +#include <openssl/ossl_typ.h> +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/pem.h> +#include <openssl/asn1.h> +#include <openssl/rand.h> +#include <openssl/err.h> +#include <iostream> +#include <iomanip> +#include <time.h> + + + +// 128 bits of salt data... +#define STORE_SALT_SIZE 16  +#define BUFFER_READ_SIZE 256 +std::string cert_string_from_asn1_string(ASN1_STRING* value); +std::string cert_string_from_octet_string(ASN1_OCTET_STRING* value); + +LLSD _basic_constraints_ext(X509* cert); +LLSD _key_usage_ext(X509* cert); +LLSD _ext_key_usage_ext(X509* cert); +LLSD _subject_key_identifier_ext(X509 *cert); +LLSD _authority_key_identifier_ext(X509* cert); + +LLBasicCertificate::LLBasicCertificate(const std::string& pem_cert)  +{ +	 +	// BIO_new_mem_buf returns a read only bio, but takes a void* which isn't const +	// so we need to cast it. +	BIO * pem_bio = BIO_new_mem_buf((void*)pem_cert.c_str(), pem_cert.length()); +	if(pem_bio == NULL) +	{ +		LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL; +		throw LLInvalidCertificate(this); +	} +	mCert = NULL; +	PEM_read_bio_X509(pem_bio, &mCert, 0, NULL); +	BIO_free(pem_bio); +	if (!mCert) +	{ +		throw LLInvalidCertificate(this); +	} +	_initLLSD(); +} + + +LLBasicCertificate::LLBasicCertificate(X509* pCert)  +{ +	if (!pCert || !pCert->cert_info) +	{ +		throw LLInvalidCertificate(this); +	}	 +	mCert = X509_dup(pCert); +	_initLLSD(); +} + +LLBasicCertificate::~LLBasicCertificate()  +{ +	if(mCert) +	{ +		X509_free(mCert); +	} +} + +// +// retrieve the pem using the openssl functionality +std::string LLBasicCertificate::getPem() const +{  +	char * pem_bio_chars = NULL; +	// a BIO is the equivalent of a 'std::stream', and +	// can be a file, mem stream, whatever.  Grab a memory based +	// BIO for the result +	BIO *pem_bio = BIO_new(BIO_s_mem()); +	if (!pem_bio) +	{ +		LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL;		 +		return std::string(); +	} +	PEM_write_bio_X509(pem_bio, mCert); +	int length = BIO_get_mem_data(pem_bio, &pem_bio_chars); +	std::string result = std::string(pem_bio_chars, length); +	BIO_free(pem_bio); +	return result; +} + +// get the DER encoding for the cert +// DER is a binary encoding format for certs... +std::vector<U8> LLBasicCertificate::getBinary() const +{  +	U8 * der_bio_data = NULL; +	// get a memory bio  +	BIO *der_bio = BIO_new(BIO_s_mem()); +	if (!der_bio) +	{ +		LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL;			 +		return std::vector<U8>(); +	} +	i2d_X509_bio(der_bio, mCert); +	int length = BIO_get_mem_data(der_bio, &der_bio_data); +	std::vector<U8> result(length); +	// vectors are guranteed to be a contiguous chunk of memory. +	memcpy(&result[0], der_bio_data,  length); +	BIO_free(der_bio); +	return result; +} + + +LLSD LLBasicCertificate::getLLSD() const +{ +	return mLLSDInfo; +} + +// Initialize the LLSD info for the certificate +LLSD& LLBasicCertificate::_initLLSD() +{  + +	// call the various helpers to build the LLSD +	mLLSDInfo[CERT_SUBJECT_NAME] = cert_name_from_X509_NAME(X509_get_subject_name(mCert)); +	mLLSDInfo[CERT_ISSUER_NAME] = cert_name_from_X509_NAME(X509_get_issuer_name(mCert)); +	mLLSDInfo[CERT_SUBJECT_NAME_STRING] = cert_string_name_from_X509_NAME(X509_get_subject_name(mCert)); +	mLLSDInfo[CERT_ISSUER_NAME_STRING] = cert_string_name_from_X509_NAME(X509_get_issuer_name(mCert)); +	ASN1_INTEGER *sn = X509_get_serialNumber(mCert); +	if (sn != NULL) +	{ +		mLLSDInfo[CERT_SERIAL_NUMBER] = cert_string_from_asn1_integer(sn); +	} +	 +	mLLSDInfo[CERT_VALID_TO] = cert_date_from_asn1_time(X509_get_notAfter(mCert)); +	mLLSDInfo[CERT_VALID_FROM] = cert_date_from_asn1_time(X509_get_notBefore(mCert)); +	mLLSDInfo[CERT_SHA1_DIGEST] = cert_get_digest("sha1", mCert); +	mLLSDInfo[CERT_MD5_DIGEST] = cert_get_digest("md5", mCert); +	// add the known extensions +	mLLSDInfo[CERT_BASIC_CONSTRAINTS] = _basic_constraints_ext(mCert); +	mLLSDInfo[CERT_KEY_USAGE] = _key_usage_ext(mCert); +	mLLSDInfo[CERT_EXTENDED_KEY_USAGE] = _ext_key_usage_ext(mCert); +	mLLSDInfo[CERT_SUBJECT_KEY_IDENTFIER] = _subject_key_identifier_ext(mCert); +	mLLSDInfo[CERT_AUTHORITY_KEY_IDENTIFIER] = _authority_key_identifier_ext(mCert); +	return mLLSDInfo;  +} + +// Retrieve the basic constraints info +LLSD _basic_constraints_ext(X509* cert) +{ +	LLSD result; +	BASIC_CONSTRAINTS *bs = (BASIC_CONSTRAINTS *)X509_get_ext_d2i(cert, NID_basic_constraints, NULL, NULL); +	if(bs) +	{ +		result = LLSD::emptyMap(); +		// Determines whether the cert can be used as a CA +		result[CERT_BASIC_CONSTRAINTS_CA] = (bool)bs->ca; +	 +		if(bs->pathlen)  +		{ +			// the pathlen determines how deep a certificate chain can be from +			// this CA +			if((bs->pathlen->type == V_ASN1_NEG_INTEGER) +			   || !bs->ca)  +			{ +				result[CERT_BASIC_CONSTRAINTS_PATHLEN] = 0; +			}  +			else  +			{ +				result[CERT_BASIC_CONSTRAINTS_PATHLEN] = (int)ASN1_INTEGER_get(bs->pathlen); +			} +		} + +	} +	return result; +} + +// retrieve the key usage, which specifies how the cert can be used. +//  +LLSD _key_usage_ext(X509* cert) +{ +	LLSD result; +	ASN1_STRING *usage_str = (ASN1_STRING *)X509_get_ext_d2i(cert, NID_key_usage, NULL, NULL); +	if(usage_str) +	{ +		result = LLSD::emptyArray(); +		long usage = 0; +		if(usage_str->length > 0)  +		{ +			usage = usage_str->data[0]; +			if(usage_str->length > 1) +			{ +				usage |= usage_str->data[1] << 8; +			} +		} +		ASN1_STRING_free(usage_str); +		if(usage) +		{ +			if(usage & KU_DIGITAL_SIGNATURE) result.append(LLSD((std::string)CERT_KU_DIGITAL_SIGNATURE)); +			if(usage & KU_NON_REPUDIATION) result.append(LLSD((std::string)CERT_KU_NON_REPUDIATION)); +			if(usage & KU_KEY_ENCIPHERMENT) result.append(LLSD((std::string)CERT_KU_KEY_ENCIPHERMENT)); +			if(usage & KU_DATA_ENCIPHERMENT) result.append(LLSD((std::string)CERT_KU_DATA_ENCIPHERMENT)); +			if(usage & KU_KEY_AGREEMENT) result.append(LLSD((std::string)CERT_KU_KEY_AGREEMENT)); +			if(usage & KU_KEY_CERT_SIGN) result.append(LLSD((std::string)CERT_KU_CERT_SIGN));			 +			if(usage & KU_CRL_SIGN) result.append(LLSD((std::string)CERT_KU_CRL_SIGN));	 +			if(usage & KU_ENCIPHER_ONLY) result.append(LLSD((std::string)CERT_KU_ENCIPHER_ONLY));				 +			if(usage & KU_DECIPHER_ONLY) result.append(LLSD((std::string)CERT_KU_DECIPHER_ONLY));		 +		} +	} +	return result; +} + +// retrieve the extended key usage for the cert +LLSD _ext_key_usage_ext(X509* cert) +{ +	LLSD result; +	EXTENDED_KEY_USAGE *eku = (EXTENDED_KEY_USAGE *)X509_get_ext_d2i(cert, NID_ext_key_usage, NULL, NULL); +	if(eku) +	{ +		result = LLSD::emptyArray(); +		while(sk_ASN1_OBJECT_num(eku)) +		{ +			ASN1_OBJECT *usage = sk_ASN1_OBJECT_pop(eku); +			if(usage) +			{ +				int nid = OBJ_obj2nid(usage); +				if (nid) +				{ +					std::string sn = OBJ_nid2sn(nid); +					result.append(sn); +				} +				ASN1_OBJECT_free(usage); +			} +		} +	} +	return result; +} + +// retrieve the subject key identifier of the cert +LLSD _subject_key_identifier_ext(X509 *cert) +{ +	LLSD result; +	ASN1_OCTET_STRING *skeyid = (ASN1_OCTET_STRING *)X509_get_ext_d2i(cert, NID_subject_key_identifier, NULL, NULL); +	if(skeyid) +	{ +		result = cert_string_from_octet_string(skeyid); +	} +	return result; +} + +// retrieve the authority key identifier of the cert +LLSD _authority_key_identifier_ext(X509* cert) +{ +	LLSD result; +	AUTHORITY_KEYID *akeyid = (AUTHORITY_KEYID *)X509_get_ext_d2i(cert, NID_authority_key_identifier, NULL, NULL); +	if(akeyid) +	{ +		result = LLSD::emptyMap(); +		if(akeyid->keyid) +		{ +			result[CERT_AUTHORITY_KEY_IDENTIFIER_ID] = cert_string_from_octet_string(akeyid->keyid); +		} +		if(akeyid->serial) +		{ +			result[CERT_AUTHORITY_KEY_IDENTIFIER_SERIAL] = cert_string_from_asn1_integer(akeyid->serial); +		}	 +	} +	 +	// we ignore the issuer name in the authority key identifier, we check the issue name via +	// the the issuer name entry in the cert. +	 + +	return result; +} + +// retrieve an openssl x509 object, +// which must be freed by X509_free +X509* LLBasicCertificate::getOpenSSLX509() const +{  +	return X509_dup(mCert);  +}   + +// generate a single string containing the subject or issuer +// name of the cert. +std::string cert_string_name_from_X509_NAME(X509_NAME* name) +{ +	char * name_bio_chars = NULL; +	// get a memory bio +	BIO *name_bio = BIO_new(BIO_s_mem()); +	// stream the name into the bio.  The name will be in the 'short name' format +	X509_NAME_print_ex(name_bio, name, 0, XN_FLAG_RFC2253); +	int length = BIO_get_mem_data(name_bio, &name_bio_chars); +	std::string result = std::string(name_bio_chars, length); +	BIO_free(name_bio); +	return result; +} + +// generate an LLSD from a certificate name (issuer or subject name).   +// the name will be strings indexed by the 'long form' +LLSD cert_name_from_X509_NAME(X509_NAME* name) +{ +	LLSD result = LLSD::emptyMap(); +	int name_entries = X509_NAME_entry_count(name); +	for (int entry_index=0; entry_index < name_entries; entry_index++)  +	{ +		char buffer[32]; +		X509_NAME_ENTRY *entry = X509_NAME_get_entry(name, entry_index); +		 +		std::string name_value = std::string((const char*)M_ASN1_STRING_data(X509_NAME_ENTRY_get_data(entry)),  +											 M_ASN1_STRING_length(X509_NAME_ENTRY_get_data(entry))); + +		ASN1_OBJECT* name_obj = X509_NAME_ENTRY_get_object(entry);		 +		OBJ_obj2txt(buffer, sizeof(buffer), name_obj, 0); +		std::string obj_buffer_str = std::string(buffer); +		result[obj_buffer_str] = name_value; +	} +	 +	return result; +} + +// Generate a string from an ASN1 integer.  ASN1 Integers are +// bignums, so they can be 'infinitely' long, therefore we +// cannot simply use a conversion to U64 or something. +// We retrieve as a readable string for UI + +std::string cert_string_from_asn1_integer(ASN1_INTEGER* value) +{ +	std::string result; +	BIGNUM *bn = ASN1_INTEGER_to_BN(value, NULL); +	if(bn) +	{ +		char * ascii_bn = BN_bn2hex(bn); + +		if(ascii_bn) +		{ +			result = ascii_bn; +			OPENSSL_free(ascii_bn); +		} +		BN_free(bn); +	} +	return result; +} + +// Generate a string from an OCTET string. +// we retrieve as a  + +std::string cert_string_from_octet_string(ASN1_OCTET_STRING* value) +{ +	 +	std::stringstream result; +	result << std::hex << std::setprecision(2); +	for (int i=0; i < value->length; i++) +	{ +		if (i != 0)  +		{ +			result << ":"; +		} +		result  << std::setfill('0') << std::setw(2) << (int)value->data[i]; +	} +	return result.str(); +} + +// Generate a string from an ASN1 integer.  ASN1 Integers are +// bignums, so they can be 'infinitely' long, therefore we +// cannot simply use a conversion to U64 or something. +// We retrieve as a readable string for UI + +std::string cert_string_from_asn1_string(ASN1_STRING* value) +{ +	char * string_bio_chars = NULL; +	std::string result; +	// get a memory bio +	BIO *string_bio = BIO_new(BIO_s_mem()); +	if(!string_bio) +	{ +		// stream the name into the bio.  The name will be in the 'short name' format +		ASN1_STRING_print_ex(string_bio, value, ASN1_STRFLGS_RFC2253); +		int length = BIO_get_mem_data(string_bio, &string_bio_chars); +		result = std::string(string_bio_chars, length); +		BIO_free(string_bio); +	} +	else +	{ +		LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL; +	} +	 +	return result; +} + +// retrieve a date structure from an ASN1 time, for  +// validity checking. +LLDate cert_date_from_asn1_time(ASN1_TIME* asn1_time) +{ +	 +	struct tm timestruct = {0}; +	int i = asn1_time->length; +	 +	if (i < 10) +	{ +		return LLDate(); +	}	 +	// convert the date from the ASN1 time (which is a string in ZULU time), to +	// a timeval. +	timestruct.tm_year = (asn1_time->data[0]-'0') * 10 + (asn1_time->data[1]-'0'); +	 +	/* Deal with Year 2000 */ +	if (timestruct.tm_year < 70) +		timestruct.tm_year += 100; +	 +	timestruct.tm_mon = (asn1_time->data[2]-'0') * 10 + (asn1_time->data[3]-'0') - 1; +	timestruct.tm_mday = (asn1_time->data[4]-'0') * 10 + (asn1_time->data[5]-'0'); +	timestruct.tm_hour = (asn1_time->data[6]-'0') * 10 + (asn1_time->data[7]-'0'); +	timestruct.tm_min = (asn1_time->data[8]-'0') * 10 + (asn1_time->data[9]-'0'); +	timestruct.tm_sec = (asn1_time->data[10]-'0') * 10 + (asn1_time->data[11]-'0'); + +#if LL_WINDOWS +	return LLDate((F64)_mkgmtime(×truct)); +#else // LL_WINDOWS +	return LLDate((F64)timegm(×truct)); +#endif // LL_WINDOWS +} + +													    +// Generate a string containing a digest.  The digest time is 'ssh1' or +// 'md5', and the resulting string is of the form "aa:12:5c:' and so on +std::string cert_get_digest(const std::string& digest_type, X509 *cert) +{ +	unsigned char digest_data[BUFFER_READ_SIZE]; +	unsigned int len = sizeof(digest_data); +	std::stringstream result; +	const EVP_MD* digest = NULL; +	// we could use EVP_get_digestbyname, but that requires initializer code which +	// would require us to complicate things by plumbing it into the system. +	if (digest_type == "md5") +	{ +		digest = EVP_md5(); +	} +	else if (digest_type == "sha1") +	{ +		digest = EVP_sha1(); +	} +	else +	{ +		return std::string(); +	} + +	X509_digest(cert, digest, digest_data, &len); +	result << std::hex << std::setprecision(2); +	for (unsigned int i=0; i < len; i++) +	{ +		if (i != 0)  +		{ +			result << ":"; +		} +		result  << std::setfill('0') << std::setw(2) << (int)digest_data[i]; +	} +	return result.str(); +} + + +// class LLBasicCertificateVector +// This class represents a list of certificates, implemented by a vector of certificate pointers. +// it contains implementations of the virtual functions for iterators, search, add, remove, etc. +// + +//  Find a certificate in the list. +// It will find a cert that has minimally the params listed, with the values being the same +LLBasicCertificateVector::iterator LLBasicCertificateVector::find(const LLSD& params) +{ +	BOOL found = FALSE; +	// loop through the entire vector comparing the values in the certs +	// against those passed in via the params. +	// params should be a map.  Only the items specified in the map will be +	// checked, but they must match exactly, even if they're maps or arrays. +	 +	for(iterator cert = begin(); +		cert != end(); +		cert++) +	{ + +			found= TRUE; +		LLSD cert_info = (*cert)->getLLSD(); +			for (LLSD::map_const_iterator param = params.beginMap(); +			 param != params.endMap(); +			 param++) +		{ + +			if (!cert_info.has((std::string)param->first) ||  +				(!valueCompareLLSD(cert_info[(std::string)param->first], param->second))) +			{ +				found = FALSE; +				break; +			} +		} +		if (found) +		{ +			return (cert); +		} +	} +	return end(); +} + +// Insert a certificate into the store.  If the certificate already  +// exists in the store, nothing is done. +void  LLBasicCertificateVector::insert(iterator _iter,  +									   LLPointer<LLCertificate> cert) +{ +	LLSD cert_info = cert->getLLSD(); +	if (cert_info.isMap() && cert_info.has(CERT_SHA1_DIGEST)) +	{ +		LLSD existing_cert_info = LLSD::emptyMap(); +		existing_cert_info[CERT_MD5_DIGEST] = cert_info[CERT_MD5_DIGEST]; +		if(find(existing_cert_info) == end()) +		{ +			BasicIteratorImpl *basic_iter = dynamic_cast<BasicIteratorImpl*>(_iter.mImpl.get()); +			mCerts.insert(basic_iter->mIter, cert); +		} +	} +} + +// remove a certificate from the store +LLPointer<LLCertificate> LLBasicCertificateVector::erase(iterator _iter) +{ +	 +	if (_iter != end()) +	{ +		BasicIteratorImpl *basic_iter = dynamic_cast<BasicIteratorImpl*>(_iter.mImpl.get()); +		LLPointer<LLCertificate> result = (*_iter); +		mCerts.erase(basic_iter->mIter); +		return result; +	} +	return NULL; +} + + +// +// LLBasicCertificateStore +// This class represents a store of CA certificates.  The basic implementation +// uses a pem file such as the legacy CA.pem stored in the existing  +// SL implementation. +LLBasicCertificateStore::LLBasicCertificateStore(const std::string& filename) +{ +	mFilename = filename; +	load_from_file(filename); +} + +void LLBasicCertificateStore::load_from_file(const std::string& filename) +{ +	// scan the PEM file extracting each certificate +	BIO* file_bio = BIO_new(BIO_s_file()); +	if(file_bio) +	{ +		if (BIO_read_filename(file_bio, filename.c_str()) > 0) +		{	 +			X509 *cert_x509 = NULL; +			while((PEM_read_bio_X509(file_bio, &cert_x509, 0, NULL)) &&  +				  (cert_x509 != NULL)) +			{ +				try +				{ +					add(new LLBasicCertificate(cert_x509)); +				} +				catch (...) +				{ +					LL_WARNS("SECAPI") << "Failure creating certificate from the certificate store file." << LL_ENDL; +				} +				X509_free(cert_x509); +				cert_x509 = NULL; +			} +			BIO_free(file_bio); +		} +	} +	else +	{ +		LL_WARNS("SECAPI") << "Could not allocate a file BIO" << LL_ENDL; +	} +} + + +LLBasicCertificateStore::~LLBasicCertificateStore() +{ +} + + +// persist the store +void LLBasicCertificateStore::save() +{ +	llofstream file_store(mFilename, llofstream::binary); +	if(!file_store.fail()) +	{ +		for(iterator cert = begin(); +			cert != end(); +			cert++) +		{ +			std::string pem = (*cert)->getPem(); +			if(!pem.empty()) +			{ +				file_store << (*cert)->getPem() << std::endl; +			} +		} +		file_store.close(); +	} +	else +	{ +		LL_WARNS("SECAPI") << "Could not open certificate store " << mFilename << "for save" << LL_ENDL; +	} +} + +// return the store id +std::string LLBasicCertificateStore::storeId() const +{ +	// this is the basic handler which uses the CA.pem store, +	// so we ignore this. +	return std::string(""); +} + + +// +// LLBasicCertificateChain +// This class represents a chain of certs, each cert being signed by the next cert +// in the chain.  Certs must be properly signed by the parent +LLBasicCertificateChain::LLBasicCertificateChain(const X509_STORE_CTX* store) +{ + +	// we're passed in a context, which contains a cert, and a blob of untrusted +	// certificates which compose the chain. +	if((store == NULL) || (store->cert == NULL)) +	{ +		LL_WARNS("SECAPI") << "An invalid store context was passed in when trying to create a certificate chain" << LL_ENDL; +		return; +	} +	// grab the child cert +	LLPointer<LLCertificate> current = new LLBasicCertificate(store->cert); + +	add(current); +	if(store->untrusted != NULL) +	{ +		// if there are other certs in the chain, we build up a vector +		// of untrusted certs so we can search for the parents of each +		// consecutive cert. +		LLBasicCertificateVector untrusted_certs; +		for(int i = 0; i < sk_X509_num(store->untrusted); i++) +		{ +			LLPointer<LLCertificate> cert = new LLBasicCertificate(sk_X509_value(store->untrusted, i)); +			untrusted_certs.add(cert); + +		}		 +		while(untrusted_certs.size() > 0) +		{ +			LLSD find_data = LLSD::emptyMap(); +			LLSD cert_data = current->getLLSD(); +			// we simply build the chain via subject/issuer name as the +			// client should not have passed in multiple CA's with the same  +			// subject name.  If they did, it'll come out in the wash during +			// validation. +			find_data[CERT_SUBJECT_NAME_STRING] = cert_data[CERT_ISSUER_NAME_STRING];  +			LLBasicCertificateVector::iterator issuer = untrusted_certs.find(find_data); +			if (issuer != untrusted_certs.end()) +			{ +				current = untrusted_certs.erase(issuer); +				add(current); +			} +			else +			{ +				break; +			} +		} +	} +} + + +// subdomain wildcard specifiers can be divided into 3 parts +// the part before the first *, the part after the first * but before +// the second *, and the part after the second *. +// It then iterates over the second for each place in the string +// that it matches.  ie if the subdomain was testfoofoobar, and +// the wildcard was test*foo*bar, it would match test, then +// recursively match foofoobar and foobar + +bool _cert_subdomain_wildcard_match(const std::string& subdomain, +									const std::string& wildcard) +{ +	// split wildcard into the portion before the *, and the portion after + +	int wildcard_pos = wildcard.find_first_of('*');	 +	// check the case where there is no wildcard. +	if(wildcard_pos == wildcard.npos) +	{ +		return (subdomain == wildcard); +	} +	 +	// we need to match the first part of the subdomain string up to the wildcard +	// position +	if(subdomain.substr(0, wildcard_pos) != wildcard.substr(0, wildcard_pos)) +	{ +		// the first portions of the strings didn't match +		return FALSE; +	} +	 +	// as the portion of the wildcard string before the * matched, we need to check the +	// portion afterwards.  Grab that portion. +	std::string new_wildcard_string = wildcard.substr( wildcard_pos+1, wildcard.npos); +	if(new_wildcard_string.empty()) +	{ +		// we had nothing after the *, so it's an automatic match +		return TRUE; +	} +	 +	// grab the portion of the remaining wildcard string before the next '*'.  We need to find this +	// within the remaining subdomain string. and then recursively check. +	std::string new_wildcard_match_string = new_wildcard_string.substr(0, new_wildcard_string.find_first_of('*')); +	 +	// grab the portion of the subdomain after the part that matched the initial wildcard portion +	std::string new_subdomain = subdomain.substr(wildcard_pos, subdomain.npos); +	 +	// iterate through the current subdomain, finding instances of the match string. +	int sub_pos = new_subdomain.find_first_of(new_wildcard_match_string); +	while(sub_pos != std::string::npos) +	{ +		new_subdomain = new_subdomain.substr(sub_pos, std::string::npos); +		if(_cert_subdomain_wildcard_match(new_subdomain, new_wildcard_string)) +		{ +			return TRUE; +		} +		sub_pos = new_subdomain.find_first_of(new_wildcard_match_string, 1); + + +	} +	// didn't find any instances of the match string that worked in the subdomain, so fail. +	return FALSE; +} + + +// RFC2459 does not address wildcards as part of it's name matching +// specification, and there is no RFC specifying wildcard matching, +// RFC2818 does a few statements about wildcard matching, but is very  +// general.  Generally, wildcard matching is per implementation, although +// it's pretty similar. +// in our case, we use the '*' wildcard character only, within each +// subdomain.  The hostname and the CN specification should have the +// same number of subdomains. +// We then iterate that algorithm over each subdomain. +bool _cert_hostname_wildcard_match(const std::string& hostname, const std::string& common_name) +{ +	std::string new_hostname = hostname; +	std::string new_cn = common_name; +	int subdomain_pos = new_hostname.find_first_of('.'); +	int subcn_pos = new_cn.find_first_of('.'); +	 +	while((subcn_pos != std::string::npos) && (subdomain_pos != std::string::npos)) +	{ +		// snip out the first subdomain and cn element + +		if(!_cert_subdomain_wildcard_match(new_hostname.substr(0, subdomain_pos), +										   new_cn.substr(0, subcn_pos))) +		{ +			return FALSE; +		} +		new_hostname = new_hostname.substr(subdomain_pos+1, std::string::npos); +		new_cn = new_cn.substr(subcn_pos+1, std::string::npos); +		subdomain_pos = new_hostname.find_first_of('.'); +		subcn_pos = new_cn.find_first_of('.'); +	} +	return _cert_subdomain_wildcard_match(new_hostname, new_cn); + +} + +// validate that the LLSD array in llsd_set contains the llsd_value  +bool _LLSDArrayIncludesValue(const LLSD& llsd_set, LLSD llsd_value) +{ +	for(LLSD::array_const_iterator set_value = llsd_set.beginArray(); +		set_value != llsd_set.endArray(); +		set_value++) +	{ +		if(valueCompareLLSD((*set_value), llsd_value)) +		{ +			return TRUE; +		} +	} +	return FALSE; +} + +void _validateCert(int validation_policy, +				  const LLPointer<LLCertificate> cert, +				  const LLSD& validation_params, +				  int depth) +{ + +	LLSD current_cert_info = cert->getLLSD();		 +	// check basic properties exist in the cert +	if(!current_cert_info.has(CERT_SUBJECT_NAME) || !current_cert_info.has(CERT_SUBJECT_NAME_STRING)) +	{ +		throw LLCertException(cert, "Cert doesn't have a Subject Name");				 +	} +	 +	if(!current_cert_info.has(CERT_ISSUER_NAME_STRING)) +	{ +		throw LLCertException(cert, "Cert doesn't have an Issuer Name");				 +	} +	 +	// check basic properties exist in the cert +	if(!current_cert_info.has(CERT_VALID_FROM) || !current_cert_info.has(CERT_VALID_TO)) +	{ +		throw LLCertException(cert, "Cert doesn't have an expiration period");				 +	} +	if (!current_cert_info.has(CERT_SHA1_DIGEST)) +	{ +		throw LLCertException(cert, "No SHA1 digest"); +	} + +	if (validation_policy & VALIDATION_POLICY_TIME) +	{ + +		LLDate validation_date(time(NULL)); +		if(validation_params.has(CERT_VALIDATION_DATE)) +		{ +			validation_date = validation_params[CERT_VALIDATION_DATE]; +		} +		 +		if((validation_date < current_cert_info[CERT_VALID_FROM].asDate()) || +		   (validation_date > current_cert_info[CERT_VALID_TO].asDate())) +		{ +			throw LLCertValidationExpirationException(cert, validation_date); +		} +	} +	if (validation_policy & VALIDATION_POLICY_SSL_KU) +	{ +		if (current_cert_info.has(CERT_KEY_USAGE) && current_cert_info[CERT_KEY_USAGE].isArray() && +			(!(_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE],  +									   LLSD((std::string)CERT_KU_DIGITAL_SIGNATURE))) || +			!(_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE],  +									  LLSD((std::string)CERT_KU_KEY_ENCIPHERMENT))))) +		{ +			throw LLCertKeyUsageValidationException(cert); +		} +		// only validate EKU if the cert has it +		if(current_cert_info.has(CERT_EXTENDED_KEY_USAGE) && current_cert_info[CERT_EXTENDED_KEY_USAGE].isArray() &&	    +		   (!_LLSDArrayIncludesValue(current_cert_info[CERT_EXTENDED_KEY_USAGE],  +									LLSD((std::string)CERT_EKU_SERVER_AUTH)))) +		{ +			throw LLCertKeyUsageValidationException(cert);			 +		} +	} +	if (validation_policy & VALIDATION_POLICY_CA_KU) +	{ +		if (current_cert_info.has(CERT_KEY_USAGE) && current_cert_info[CERT_KEY_USAGE].isArray() && +			(!_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE],  +									   (std::string)CERT_KU_CERT_SIGN))) +			{ +				throw LLCertKeyUsageValidationException(cert);						 +			} +	} +	 +	// validate basic constraints +	if ((validation_policy & VALIDATION_POLICY_CA_BASIC_CONSTRAINTS) && +		current_cert_info.has(CERT_BASIC_CONSTRAINTS) &&  +		current_cert_info[CERT_BASIC_CONSTRAINTS].isMap()) +	{ +		if(!current_cert_info[CERT_BASIC_CONSTRAINTS].has(CERT_BASIC_CONSTRAINTS_CA) || +		   !current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_CA]) +		{ +				throw LLCertBasicConstraintsValidationException(cert); +		} +		if (current_cert_info[CERT_BASIC_CONSTRAINTS].has(CERT_BASIC_CONSTRAINTS_PATHLEN) && +			((current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_PATHLEN].asInteger() != 0) && +			 (depth > current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_PATHLEN].asInteger()))) +		{ +			throw LLCertBasicConstraintsValidationException(cert);					 +		} +	} +} + +bool _verify_signature(LLPointer<LLCertificate> parent,  +					   LLPointer<LLCertificate> child) +{ +	bool verify_result = FALSE;  +	LLSD cert1 = parent->getLLSD(); +	LLSD cert2 = child->getLLSD(); +	X509 *signing_cert = parent->getOpenSSLX509(); +	X509 *child_cert = child->getOpenSSLX509(); +	if((signing_cert != NULL) && (child_cert != NULL)) +	{ +		EVP_PKEY *pkey = X509_get_pubkey(signing_cert); +		 +		 +		if(pkey) +		{ +			int verify_code = X509_verify(child_cert, pkey); +			verify_result = ( verify_code > 0); +			EVP_PKEY_free(pkey); +		} +		else +		{ +			LL_WARNS("SECAPI") << "Could not validate the cert chain signature, as the public key of the signing cert could not be retrieved" << LL_ENDL; +		} + +	} +	else +	{ +		LL_WARNS("SECAPI") << "Signature verification failed as there are no certs in the chain" << LL_ENDL; +	} +	if(child_cert) +	{ +		X509_free(child_cert); +	} +	if(signing_cert) +	{ +		X509_free(signing_cert); +	} +	return verify_result; +} + +// validate the certificate chain against a store. +// There are many aspects of cert validatioin policy involved in +// trust validation.  The policies in this validation algorithm include +// * Hostname matching for SSL certs +// * Expiration time matching +// * Signature validation +// * Chain trust (is the cert chain trusted against the store) +// * Basic constraints +// * key usage and extended key usage +// TODO: We should add 'authority key identifier' for chaining. +// This algorithm doesn't simply validate the chain by itself +// and verify the last cert is in the certificate store, or points +// to a cert in the store.  It validates whether any cert in the chain +// is trusted in the store, even if it's not the last one. +void LLBasicCertificateChain::validate(int validation_policy, +									   LLPointer<LLCertificateStore> ca_store, +									   const LLSD& validation_params) +{ + +	if(size() < 1) +	{ +		throw LLCertException(NULL, "No certs in chain"); +	} +	iterator current_cert = begin(); +	LLSD 	current_cert_info = (*current_cert)->getLLSD(); +	LLSD validation_date; +	if (validation_params.has(CERT_VALIDATION_DATE)) +	{ +		validation_date = validation_params[CERT_VALIDATION_DATE]; +	} + +	if (validation_policy & VALIDATION_POLICY_HOSTNAME) +	{ +		if(!validation_params.has(CERT_HOSTNAME)) +		{ +			throw LLCertException((*current_cert), "No hostname passed in for validation");			 +		} +		if(!current_cert_info.has(CERT_SUBJECT_NAME) || !current_cert_info[CERT_SUBJECT_NAME].has(CERT_NAME_CN)) +		{ +			throw LLInvalidCertificate((*current_cert));				 +		} +		 +		LL_INFOS("SECAPI") << "Validating the hostname " << validation_params[CERT_HOSTNAME].asString() <<  +		     "against the cert CN " << current_cert_info[CERT_SUBJECT_NAME][CERT_NAME_CN].asString() << LL_ENDL; +		if(!_cert_hostname_wildcard_match(validation_params[CERT_HOSTNAME].asString(), +										  current_cert_info[CERT_SUBJECT_NAME][CERT_NAME_CN].asString())) +		{ +			throw LLCertValidationHostnameException(validation_params[CERT_HOSTNAME].asString(), +													(*current_cert)); +		} +	} +	 + +	int depth = 0; +	LLPointer<LLCertificate> previous_cert; +	// loop through the cert chain, validating the current cert against the next one. +	while(current_cert != end()) +	{ +		 +		int local_validation_policy = validation_policy; +		if(current_cert == begin()) +		{ +			// for the child cert, we don't validate CA stuff +			local_validation_policy &= ~(VALIDATION_POLICY_CA_KU |  +										 VALIDATION_POLICY_CA_BASIC_CONSTRAINTS); +		} +		else +		{ +			// for non-child certs, we don't validate SSL Key usage +			local_validation_policy &= ~VALIDATION_POLICY_SSL_KU;				 +			if(!_verify_signature((*current_cert), +								  previous_cert)) +			{ +			   throw LLCertValidationInvalidSignatureException(previous_cert); +			} +		} +		_validateCert(local_validation_policy, +					  (*current_cert), +					  validation_params, +					  depth); +		 +		// look for a CA in the CA store that may belong to this chain. +		LLSD cert_llsd = (*current_cert)->getLLSD(); +		LLSD cert_search_params = LLSD::emptyMap();		 +		// is the cert itself in the store? +		cert_search_params[CERT_SHA1_DIGEST] = cert_llsd[CERT_SHA1_DIGEST]; +		LLCertificateStore::iterator found_store_cert = ca_store->find(cert_search_params); +		if(found_store_cert != ca_store->end()) +		{ +			return; +		} +		 +		// is the parent in the cert store? +			 +		cert_search_params = LLSD::emptyMap(); +		cert_search_params[CERT_SUBJECT_NAME_STRING] = cert_llsd[CERT_ISSUER_NAME_STRING]; +		if (cert_llsd.has(CERT_AUTHORITY_KEY_IDENTIFIER)) +		{ +			LLSD cert_aki = cert_llsd[CERT_AUTHORITY_KEY_IDENTIFIER]; +			if(cert_aki.has(CERT_AUTHORITY_KEY_IDENTIFIER_ID)) +			{ +				cert_search_params[CERT_SUBJECT_KEY_IDENTFIER] = cert_aki[CERT_AUTHORITY_KEY_IDENTIFIER_ID]; +			} +			if(cert_aki.has(CERT_AUTHORITY_KEY_IDENTIFIER_SERIAL)) +			{ +				cert_search_params[CERT_SERIAL_NUMBER] = cert_aki[CERT_AUTHORITY_KEY_IDENTIFIER_SERIAL]; +			} +		} +		found_store_cert = ca_store->find(cert_search_params); +		 +		if(found_store_cert != ca_store->end()) +		{ +			LLSD foo = (*found_store_cert)->getLLSD(); +			// validate the store cert against the depth +			_validateCert(validation_policy & VALIDATION_POLICY_CA_BASIC_CONSTRAINTS, +						  (*found_store_cert), +						  LLSD(), +						  depth); +			 +			// verify the signature of the CA +			if(!_verify_signature((*found_store_cert), +								  (*current_cert))) +			{ +				throw LLCertValidationInvalidSignatureException(*current_cert); +			}			 +			// successfully validated. +			return; +		} +		previous_cert = (*current_cert); +		current_cert++; +			   depth++; +	} +	if (validation_policy & VALIDATION_POLICY_TRUSTED) +	{ +		LLPointer<LLCertificate> untrusted_ca_cert = (*this)[size()-1]; +		// we reached the end without finding a trusted cert. +		throw LLCertValidationTrustException((*this)[size()-1]); + +	} +} + + +// LLSecAPIBasicHandler Class +// Interface handler class for the various security storage handlers. + +// We read the file on construction, and write it on destruction.  This +// means multiple processes cannot modify the datastore. +LLSecAPIBasicHandler::LLSecAPIBasicHandler(const std::string& protected_data_file, +										   const std::string& legacy_password_path) +{ +	mProtectedDataFilename = protected_data_file; +	mProtectedDataMap = LLSD::emptyMap(); +	mLegacyPasswordPath = legacy_password_path; + +} + +LLSecAPIBasicHandler::LLSecAPIBasicHandler() +{ +} + + +void LLSecAPIBasicHandler::init() +{ +	mProtectedDataMap = LLSD::emptyMap(); +	if (mProtectedDataFilename.length() == 0) +	{ +		mProtectedDataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, +															"bin_conf.dat"); +		mLegacyPasswordPath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "password.dat"); +	 +		mProtectedDataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, +															"bin_conf.dat");	 +		std::string store_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, +														"CA.pem"); +		// copy the CA file to a user writable location so we can manipulate it. +		// for this provider, by using a user writable file, there is a risk that +		// an attacking program can modify the file, but OS dependent providers +		// will reduce that risk. +		// by using a user file, modifications will be limited to one user if +		// we read-only the main file +		if (!LLFile::isfile(store_file)) +		{ + +			std::string ca_file_path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "CA.pem"); +			llifstream ca_file(ca_file_path.c_str(), llifstream::binary | llifstream::in); +			llofstream copied_store_file(store_file.c_str(), llofstream::binary | llofstream::out); + +			while(!ca_file.fail()) +			{ +				char buffer[BUFFER_READ_SIZE]; +				ca_file.read(buffer, sizeof(buffer)); +				copied_store_file.write(buffer, ca_file.gcount()); +			} +			ca_file.close(); +			copied_store_file.close(); +		} +		LL_INFOS("SECAPI") << "Loading certificate store from " << store_file << LL_ENDL; +		mStore = new LLBasicCertificateStore(store_file); +	} +	_readProtectedData(); // initialize mProtectedDataMap +						  // may throw LLProtectedDataException if saved datamap is not decryptable +} +LLSecAPIBasicHandler::~LLSecAPIBasicHandler() +{ +	_writeProtectedData(); +} + +void LLSecAPIBasicHandler::_readProtectedData() +{	 +	// attempt to load the file into our map +	LLPointer<LLSDParser> parser = new LLSDXMLParser(); +	llifstream protected_data_stream(mProtectedDataFilename.c_str(),  +									llifstream::binary); + +	if (!protected_data_stream.fail()) { +		int offset; +		U8 salt[STORE_SALT_SIZE]; +		U8 buffer[BUFFER_READ_SIZE]; +		U8 decrypted_buffer[BUFFER_READ_SIZE]; +		int decrypted_length;	 +		unsigned char MACAddress[MAC_ADDRESS_BYTES]; +		LLUUID::getNodeID(MACAddress); +		LLXORCipher cipher(MACAddress, MAC_ADDRESS_BYTES); + +		// read in the salt and key +		protected_data_stream.read((char *)salt, STORE_SALT_SIZE); +		offset = 0; +		if (protected_data_stream.gcount() < STORE_SALT_SIZE) +		{ +			throw LLProtectedDataException("Config file too short."); +		} + +		cipher.decrypt(salt, STORE_SALT_SIZE);		 + +		// totally lame.  As we're not using the OS level protected data, we need to +		// at least obfuscate the data.  We do this by using a salt stored at the head of the file +		// to encrypt the data, therefore obfuscating it from someone using simple existing tools. +		// We do include the MAC address as part of the obfuscation, which would require an +		// attacker to get the MAC address as well as the protected store, which improves things +		// somewhat.  It would be better to use the password, but as this store +		// will be used to store the SL password when the user decides to have SL remember it,  +		// so we can't use that.  OS-dependent store implementations will use the OS password/storage  +		// mechanisms and are considered to be more secure. +		// We've a strong intent to move to OS dependent protected data stores. +		 + +		// read in the rest of the file. +		EVP_CIPHER_CTX ctx; +		EVP_CIPHER_CTX_init(&ctx); +		EVP_DecryptInit(&ctx, EVP_rc4(), salt, NULL); +		// allocate memory: +		std::string decrypted_data;	 +		 +		while(protected_data_stream.good()) { +			// read data as a block: +			protected_data_stream.read((char *)buffer, BUFFER_READ_SIZE); +			 +			EVP_DecryptUpdate(&ctx, decrypted_buffer, &decrypted_length,  +							  buffer, protected_data_stream.gcount()); +			decrypted_data.append((const char *)decrypted_buffer, protected_data_stream.gcount()); +		} +		 +		// RC4 is a stream cipher, so we don't bother to EVP_DecryptFinal, as there is +		// no block padding. +		EVP_CIPHER_CTX_cleanup(&ctx); +		std::istringstream parse_stream(decrypted_data); +		if (parser->parse(parse_stream, mProtectedDataMap,  +						  LLSDSerialize::SIZE_UNLIMITED) == LLSDParser::PARSE_FAILURE) +		{ +			throw LLProtectedDataException("Config file cannot be decrypted."); +		} +	} +} + +void LLSecAPIBasicHandler::_writeProtectedData() +{	 +	std::ostringstream formatted_data_ostream; +	U8 salt[STORE_SALT_SIZE]; +	U8 buffer[BUFFER_READ_SIZE]; +	U8 encrypted_buffer[BUFFER_READ_SIZE]; + +	 +	if(mProtectedDataMap.isUndefined()) +	{ +		LLFile::remove(mProtectedDataFilename); +		return; +	} +	// create a string with the formatted data. +	LLSDSerialize::toXML(mProtectedDataMap, formatted_data_ostream); +	std::istringstream formatted_data_istream(formatted_data_ostream.str()); +	// generate the seed +	RAND_bytes(salt, STORE_SALT_SIZE); + +	 +	// write to a temp file so we don't clobber the initial file if there is +	// an error. +	std::string tmp_filename = mProtectedDataFilename + ".tmp"; +	 +	llofstream protected_data_stream(tmp_filename.c_str(),  +										llofstream::binary); +	try +	{ +		 +		EVP_CIPHER_CTX ctx; +		EVP_CIPHER_CTX_init(&ctx); +		EVP_EncryptInit(&ctx, EVP_rc4(), salt, NULL); +		unsigned char MACAddress[MAC_ADDRESS_BYTES]; +		LLUUID::getNodeID(MACAddress); +		LLXORCipher cipher(MACAddress, MAC_ADDRESS_BYTES); +		cipher.encrypt(salt, STORE_SALT_SIZE); +		protected_data_stream.write((const char *)salt, STORE_SALT_SIZE); + +		while (formatted_data_istream.good()) +		{ +			formatted_data_istream.read((char *)buffer, BUFFER_READ_SIZE); +			if(formatted_data_istream.gcount() == 0) +			{ +				break; +			} +			int encrypted_length; +			EVP_EncryptUpdate(&ctx, encrypted_buffer, &encrypted_length,  +						  buffer, formatted_data_istream.gcount()); +			protected_data_stream.write((const char *)encrypted_buffer, encrypted_length); +		} +		 +		// no EVP_EncrypteFinal, as this is a stream cipher +		EVP_CIPHER_CTX_cleanup(&ctx); + +		protected_data_stream.close(); +	} +	catch (...) +	{ +		// it's good practice to clean up any secure information on error +		// (even though this file isn't really secure.  Perhaps in the future +		// it may be, however. +		LLFile::remove(tmp_filename); +		throw LLProtectedDataException("Error writing Protected Data Store"); +	} + +	// move the temporary file to the specified file location. +	if((((LLFile::isfile(mProtectedDataFilename) != 0) &&  +		 (LLFile::remove(mProtectedDataFilename) != 0))) ||  +	   (LLFile::rename(tmp_filename, mProtectedDataFilename))) +	{ +		LLFile::remove(tmp_filename); +		throw LLProtectedDataException("Could not overwrite protected data store"); +	} +} +		 +// instantiate a certificate from a pem string +LLPointer<LLCertificate> LLSecAPIBasicHandler::getCertificate(const std::string& pem_cert) +{ +	LLPointer<LLCertificate> result = new LLBasicCertificate(pem_cert); +	return result; +} +		 + +		 +// instiate a certificate from an openssl X509 structure +LLPointer<LLCertificate> LLSecAPIBasicHandler::getCertificate(X509* openssl_cert) +{ +	LLPointer<LLCertificate> result = new LLBasicCertificate(openssl_cert); +	return result;		 +} +		 +// instantiate a chain from an X509_STORE_CTX +LLPointer<LLCertificateChain> LLSecAPIBasicHandler::getCertificateChain(const X509_STORE_CTX* chain) +{ +	LLPointer<LLCertificateChain> result = new LLBasicCertificateChain(chain); +	return result; +} +		 +// instantiate a cert store given it's id.  if a persisted version +// exists, it'll be loaded.  If not, one will be created (but not +// persisted) +LLPointer<LLCertificateStore> LLSecAPIBasicHandler::getCertificateStore(const std::string& store_id) +{ +	return mStore; +} +		 +// retrieve protected data +LLSD LLSecAPIBasicHandler::getProtectedData(const std::string& data_type, +											const std::string& data_id) +{ + +	if (mProtectedDataMap.has(data_type) &&  +		mProtectedDataMap[data_type].isMap() &&  +		mProtectedDataMap[data_type].has(data_id)) +	{ +		return mProtectedDataMap[data_type][data_id]; +	} +																				 +	return LLSD(); +} + +void LLSecAPIBasicHandler::deleteProtectedData(const std::string& data_type, +											   const std::string& data_id) +{ +	if (mProtectedDataMap.has(data_type) && +		mProtectedDataMap[data_type].isMap() && +		mProtectedDataMap[data_type].has(data_id)) +		{ +			mProtectedDataMap[data_type].erase(data_id); +		} +} + + +// +// persist data in a protected store +// +void LLSecAPIBasicHandler::setProtectedData(const std::string& data_type, +											const std::string& data_id, +											const LLSD& data) +{ +	if (!mProtectedDataMap.has(data_type) || !mProtectedDataMap[data_type].isMap()) { +		mProtectedDataMap[data_type] = LLSD::emptyMap(); +	} +	 +	mProtectedDataMap[data_type][data_id] = data;  +} + +// +// Create a credential object from an identifier and authenticator.  credentials are +// per grid. +LLPointer<LLCredential> LLSecAPIBasicHandler::createCredential(const std::string& grid, +															   const LLSD& identifier,  +															   const LLSD& authenticator) +{ +	LLPointer<LLSecAPIBasicCredential> result = new LLSecAPIBasicCredential(grid); +	result->setCredentialData(identifier, authenticator); +	return result; +} + +// Load a credential from the credential store, given the grid +LLPointer<LLCredential> LLSecAPIBasicHandler::loadCredential(const std::string& grid) +{ +	LLSD credential = getProtectedData("credential", grid); +	LLPointer<LLSecAPIBasicCredential> result = new LLSecAPIBasicCredential(grid); +	if(credential.isMap() &&  +	   credential.has("identifier")) +	{ + +		LLSD identifier = credential["identifier"]; +		LLSD authenticator; +		if (credential.has("authenticator")) +		{ +			authenticator = credential["authenticator"]; +		} +		result->setCredentialData(identifier, authenticator); +	} +	else +	{ +		// credential was not in protected storage, so pull the credential +		// from the legacy store. +		std::string first_name = gSavedSettings.getString("FirstName"); +		std::string last_name = gSavedSettings.getString("LastName"); +		 +		if ((first_name != "") && +			(last_name != "")) +		{ +			LLSD identifier = LLSD::emptyMap(); +			LLSD authenticator; +			identifier["type"] = "agent"; +			identifier["first_name"] = first_name; +			identifier["last_name"] = last_name; +			 +			std::string legacy_password = _legacyLoadPassword(); +			if (legacy_password.length() > 0) +			{ +				authenticator = LLSD::emptyMap(); +				authenticator["type"] = "hash"; +				authenticator["algorithm"] = "md5"; +				authenticator["secret"] = legacy_password; +			} +			result->setCredentialData(identifier, authenticator); +		}		 +	} +	return result; +} + +// Save the credential to the credential store.  Save the authenticator also if requested. +// That feature is used to implement the 'remember password' functionality. +void LLSecAPIBasicHandler::saveCredential(LLPointer<LLCredential> cred, bool save_authenticator) +{ +	LLSD credential = LLSD::emptyMap(); +	credential["identifier"] = cred->getIdentifier();  +	if (save_authenticator)  +	{ +		credential["authenticator"] = cred->getAuthenticator(); +	} +	LL_INFOS("SECAPI") << "Saving Credential " << cred->getGrid() << ":" << cred->userID() << " " << save_authenticator << LL_ENDL; +	setProtectedData("credential", cred->getGrid(), credential); +	//*TODO: If we're saving Agni credentials, should we write the +	// credentials to the legacy password.dat/etc? +	_writeProtectedData(); +} + +// Remove a credential from the credential store. +void LLSecAPIBasicHandler::deleteCredential(LLPointer<LLCredential> cred) +{ +	LLSD undefVal; +	deleteProtectedData("credential", cred->getGrid()); +	cred->setCredentialData(undefVal, undefVal); +	_writeProtectedData(); +} + +// load the legacy hash for agni, and decrypt it given the  +// mac address +std::string LLSecAPIBasicHandler::_legacyLoadPassword() +{ +	const S32 HASHED_LENGTH = 32;	 +	std::vector<U8> buffer(HASHED_LENGTH); +	llifstream password_file(mLegacyPasswordPath, llifstream::binary); +	 +	if(password_file.fail()) +	{ +		return std::string(""); +	} +	 +	password_file.read((char*)&buffer[0], buffer.size()); +	if(password_file.gcount() != buffer.size()) +	{ +		return std::string(""); +	} +	 +	// Decipher with MAC address +	unsigned char MACAddress[MAC_ADDRESS_BYTES]; +	LLUUID::getNodeID(MACAddress); +	LLXORCipher cipher(MACAddress, 6); +	cipher.decrypt(&buffer[0], buffer.size()); +	 +	return std::string((const char*)&buffer[0], buffer.size()); +} + + +// return an identifier for the user +std::string LLSecAPIBasicCredential::userID() const +{ +	if (!mIdentifier.isMap()) +	{ +		return mGrid + "(null)"; +	} +	else if ((std::string)mIdentifier["type"] == "agent") +	{ +		return  (std::string)mIdentifier["first_name"] + "_" + (std::string)mIdentifier["last_name"]; +	} +	else if ((std::string)mIdentifier["type"] == "account") +	{ +		return (std::string)mIdentifier["account_name"]; +	} + +	return "unknown"; + +} + +// return a printable user identifier +std::string LLSecAPIBasicCredential::asString() const +{ +	if (!mIdentifier.isMap()) +	{ +		return mGrid + ":(null)"; +	} +	else if ((std::string)mIdentifier["type"] == "agent") +	{ +		return mGrid + ":" + (std::string)mIdentifier["first_name"] + " " + (std::string)mIdentifier["last_name"]; +	} +	else if ((std::string)mIdentifier["type"] == "account") +	{ +		return mGrid + ":" + (std::string)mIdentifier["account_name"]; +	} + +	return mGrid + ":(unknown type)"; +} + + +bool valueCompareLLSD(const LLSD& lhs, const LLSD& rhs) +{ +	if (lhs.type() != rhs.type()) +	{ +		return FALSE; +	} +    if (lhs.isMap()) +	{ +		// iterate through the map, verifying the right hand side has all of the +		// values that the left hand side has. +		for (LLSD::map_const_iterator litt = lhs.beginMap(); +			 litt != lhs.endMap(); +			 litt++) +		{ +			if (!rhs.has(litt->first)) +			{ +				return FALSE; +			} +		} +		 +		// Now validate that the left hand side has everything the +		// right hand side has, and that the values are equal. +		for (LLSD::map_const_iterator ritt = rhs.beginMap(); +			 ritt != rhs.endMap(); +			 ritt++) +		{ +			if (!lhs.has(ritt->first)) +			{ +				return FALSE; +			} +			if (!valueCompareLLSD(lhs[ritt->first], ritt->second)) +			{ +				return FALSE; +			} +		} +		return TRUE; +	} +    else if (lhs.isArray()) +	{ +		LLSD::array_const_iterator ritt = rhs.beginArray(); +		// iterate through the array, comparing +		for (LLSD::array_const_iterator litt = lhs.beginArray(); +			 litt != lhs.endArray(); +			 litt++) +		{ +			if (!valueCompareLLSD(*ritt, *litt)) +			{ +				return FALSE; +			} +			ritt++; +		} +		 +		return (ritt == rhs.endArray()); +	} +    else +	{ +		// simple type, compare as string +		return (lhs.asString() == rhs.asString()); +	} +	 +} diff --git a/indra/newview/llsechandler_basic.h b/indra/newview/llsechandler_basic.h new file mode 100644 index 0000000000..4bbb73f062 --- /dev/null +++ b/indra/newview/llsechandler_basic.h @@ -0,0 +1,285 @@ +/**  + * @file llsechandler_basic.h + * @brief Security API for services such as certificate handling + * secure local storage, etc. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + *  + * Copyright (c) 2009, Linden Research, Inc. + *  + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab.  Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + *  + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + *  + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + *  + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#ifndef LLSECHANDLER_BASIC +#define LLSECHANDLER_BASIC + +#include "llsecapi.h" +#include <vector> +#include <openssl/x509.h> + +// helpers +extern LLSD cert_name_from_X509_NAME(X509_NAME* name); +extern std::string cert_string_name_from_X509_NAME(X509_NAME* name); +extern std::string cert_string_from_asn1_integer(ASN1_INTEGER* value); +extern LLDate cert_date_from_asn1_time(ASN1_TIME* asn1_time); +extern std::string cert_get_digest(const std::string& digest_type, X509 *cert); + + +// class LLCertificate +//  +class LLBasicCertificate : public LLCertificate +{ +public:		 +	LOG_CLASS(LLBasicCertificate); + +	LLBasicCertificate(const std::string& pem_cert); +	LLBasicCertificate(X509* openSSLX509); +	 +	virtual ~LLBasicCertificate(); +	 +	virtual std::string getPem() const; +	virtual std::vector<U8> getBinary() const; +	virtual LLSD getLLSD() const; + +	virtual X509* getOpenSSLX509() const; +	 +	// set llsd elements for testing +	void setLLSD(const std::string name, const LLSD& value) { mLLSDInfo[name] = value; } +protected: + +	// certificates are stored as X509 objects, as validation and +	// other functionality is via openssl +	X509* mCert; +	 +	LLSD& _initLLSD(); +	LLSD mLLSDInfo; +}; + + +// class LLBasicCertificateVector +// Class representing a list of certificates +// This implementation uses a stl vector of certificates. +class LLBasicCertificateVector : virtual public LLCertificateVector +{ +	 +public: +	LLBasicCertificateVector() {} +	 +	virtual ~LLBasicCertificateVector() {} +	 +	// Implementation of the basic iterator implementation. +	// The implementation uses a vector iterator derived from  +	// the vector in the LLBasicCertificateVector class +	class BasicIteratorImpl : public iterator_impl +	{ +	public: +		BasicIteratorImpl(std::vector<LLPointer<LLCertificate> >::iterator _iter) { mIter = _iter;} +		virtual ~BasicIteratorImpl() {}; +		// seek forward or back.  Used by the operator++/operator-- implementations +		virtual void seek(bool incr) +		{ +			if(incr) +			{ +				mIter++; +			} +			else +			{ +				mIter--; +			} +		} +		// create a copy of the iterator implementation class, used by the iterator copy constructor +		virtual LLPointer<iterator_impl> clone() const +		{ +			return new BasicIteratorImpl(mIter); +		} +		 +		virtual bool equals(const LLPointer<iterator_impl>& _iter) const +		{ +			const BasicIteratorImpl *rhs_iter = dynamic_cast<const BasicIteratorImpl *>(_iter.get()); +			return (mIter == rhs_iter->mIter); +		} +		virtual LLPointer<LLCertificate> get() +		{ +			return *mIter; +		} +	protected: +		friend class LLBasicCertificateVector; +		std::vector<LLPointer<LLCertificate> >::iterator mIter; +	}; +	 +	// numeric index of the vector +	virtual LLPointer<LLCertificate> operator[](int _index) { return mCerts[_index];} +	 +	// Iteration +	virtual iterator begin() { return iterator(new BasicIteratorImpl(mCerts.begin())); } +	 +	virtual iterator end() {  return iterator(new BasicIteratorImpl(mCerts.end())); } +	 +	// find a cert given params +	virtual iterator find(const LLSD& params); +	 +	// return the number of certs in the store +	virtual int size() const { return mCerts.size(); }	 +	 +	// insert the cert to the store.  if a copy of the cert already exists in the store, it is removed first +	virtual void  add(LLPointer<LLCertificate> cert) { insert(end(), cert); } +	 +	// insert the cert to the store.  if a copy of the cert already exists in the store, it is removed first +	virtual void  insert(iterator _iter, LLPointer<LLCertificate> cert);	 +	 +	// remove a certificate from the store +	virtual LLPointer<LLCertificate> erase(iterator _iter); +	 +protected: +	std::vector<LLPointer<LLCertificate> >mCerts;	 +}; + +// class LLCertificateStore +// represents a store of certificates, typically a store of root CA +// certificates.  The store can be persisted, and can be used to validate +// a cert chain +// +class LLBasicCertificateStore : virtual public LLBasicCertificateVector, public LLCertificateStore +{ +public: +	LLBasicCertificateStore(const std::string& filename); +	void load_from_file(const std::string& filename); +	 +	virtual ~LLBasicCertificateStore(); +	 +	// persist the store +	virtual void save(); +	 +	// return the store id +	virtual std::string storeId() const; +	 +protected: +	std::vector<LLPointer<LLCertificate> >mCerts; +	std::string mFilename; +}; + +// class LLCertificateChain +// Class representing a chain of certificates in order, with the  +// first element being the child cert. +class LLBasicCertificateChain : virtual public LLBasicCertificateVector, public LLCertificateChain +{ +	 +public: +	LLBasicCertificateChain(const X509_STORE_CTX * store); +	 +	virtual ~LLBasicCertificateChain() {} +	 +	// validate a certificate chain against a certificate store, using the +	// given validation policy. +	virtual void validate(int validation_policy, +						  LLPointer<LLCertificateStore> ca_store, +						  const LLSD& validation_params); +}; + + + +// LLSecAPIBasicCredential class +class LLSecAPIBasicCredential : public LLCredential +{ +public: +	LLSecAPIBasicCredential(const std::string& grid) : LLCredential(grid) {}  +	virtual ~LLSecAPIBasicCredential() {} +	// return a value representing the user id, (could be guid, name, whatever) +	virtual std::string userID() const;	 +	 +	// printible string identifying the credential. +	virtual std::string asString() const; +}; + +// LLSecAPIBasicHandler Class +// Interface handler class for the various security storage handlers. +class LLSecAPIBasicHandler : public LLSecAPIHandler +{ +public: +	 +	LLSecAPIBasicHandler(const std::string& protected_data_filename, +						 const std::string& legacy_password_path); +	LLSecAPIBasicHandler(); +	 +	void init(); +	 +	virtual ~LLSecAPIBasicHandler(); +	 +	// instantiate a certificate from a pem string +	virtual LLPointer<LLCertificate> getCertificate(const std::string& pem_cert); +	 +	 +	// instiate a certificate from an openssl X509 structure +	virtual LLPointer<LLCertificate> getCertificate(X509* openssl_cert); +	 +	// instantiate a chain from an X509_STORE_CTX +	virtual LLPointer<LLCertificateChain> getCertificateChain(const X509_STORE_CTX* chain); +	 +	// instantiate a cert store given it's id.  if a persisted version +	// exists, it'll be loaded.  If not, one will be created (but not +	// persisted) +	virtual LLPointer<LLCertificateStore> getCertificateStore(const std::string& store_id); +	 +	// persist data in a protected store +	virtual void setProtectedData(const std::string& data_type, +								  const std::string& data_id, +								  const LLSD& data); +	 +	// retrieve protected data +	virtual LLSD getProtectedData(const std::string& data_type, +								  const std::string& data_id); +	 +	// delete a protected data item from the store +	virtual void deleteProtectedData(const std::string& data_type, +									 const std::string& data_id); +	 +	// credential management routines +	 +	virtual LLPointer<LLCredential> createCredential(const std::string& grid, +													 const LLSD& identifier,  +													 const LLSD& authenticator); +	 +	virtual LLPointer<LLCredential> loadCredential(const std::string& grid); + +	virtual void saveCredential(LLPointer<LLCredential> cred, bool save_authenticator); +	 +	virtual void deleteCredential(LLPointer<LLCredential> cred); +	 +protected: +	void _readProtectedData(); +	void _writeProtectedData(); +	std::string _legacyLoadPassword(); + +	std::string mProtectedDataFilename; +	LLSD mProtectedDataMap; +	LLPointer<LLBasicCertificateStore> mStore; +	 +	std::string mLegacyPasswordPath; +}; + +bool valueCompareLLSD(const LLSD& lhs, const LLSD& rhs); + +#endif // LLSECHANDLER_BASIC + + + diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index d03a492cd1..3ef810c3e9 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -2435,7 +2435,7 @@ BOOL LLSelectMgr::selectGetCreator(LLUUID& result_id, std::string& name)  	if (identical)  	{ -		name = LLSLURL::buildCommand("agent", first_id, "inspect"); +		name = LLSLURL("agent", first_id, "inspect").getSLURLString();  	}  	else  	{ @@ -2494,11 +2494,11 @@ BOOL LLSelectMgr::selectGetOwner(LLUUID& result_id, std::string& name)  		BOOL public_owner = (first_id.isNull() && !first_group_owned);  		if (first_group_owned)  		{ -			name = LLSLURL::buildCommand("group", first_id, "inspect"); +			name = LLSLURL("group", first_id, "inspect").getSLURLString();  		}  		else if(!public_owner)  		{ -			name = LLSLURL::buildCommand("agent", first_id, "inspect"); +			name = LLSLURL("agent", first_id, "inspect").getSLURLString();  		}  		else  		{ @@ -2558,7 +2558,7 @@ BOOL LLSelectMgr::selectGetLastOwner(LLUUID& result_id, std::string& name)  		BOOL public_owner = (first_id.isNull());  		if(!public_owner)  		{ -			name = LLSLURL::buildCommand("agent", first_id, "inspect"); +			name = LLSLURL("agent", first_id, "inspect").getSLURLString();  		}  		else  		{ diff --git a/indra/newview/llslurl.cpp b/indra/newview/llslurl.cpp index 5d20e280b5..ff7e479368 100644 --- a/indra/newview/llslurl.cpp +++ b/indra/newview/llslurl.cpp @@ -1,10 +1,11 @@  /**  - * @file llslurl.cpp - * @brief SLURL manipulation + * @file llurlsimstring.cpp (was llsimurlstring.cpp) + * @brief Handles "SLURL fragments" like Ahern/123/45 for + * startup processing, login screen, prefs, etc.   * - * $LicenseInfo:firstyear=2009&license=viewergpl$ + * $LicenseInfo:firstyear=2010&license=viewergpl$   *  - * Copyright (c) 2009, Linden Research, Inc. + * Copyright (c) 2006-2010, Linden Research, Inc.   *    * Second Life Viewer Source Code   * The source code in this file ("Source Code") is provided by Linden Lab @@ -12,13 +13,12 @@   * ("GPL"), unless you have obtained a separate licensing agreement   * ("Other License"), formally executed by you and Linden Lab.  Terms of   * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2   *    * There are special exceptions to the terms and conditions of the GPL as   * it is applied to this Source Code. View the full text of the exception   * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * online at http://secondlife.com/developers/opensource/flossexception   *    * By copying, modifying or distributing this software, you acknowledge   * that you have read and understood your obligations described above, @@ -34,155 +34,443 @@  #include "llslurl.h" -#include "llweb.h" - -#include "llurlregistry.h" - -const std::string LLSLURL::PREFIX_SL_HELP		= "secondlife://app."; -const std::string LLSLURL::PREFIX_SL			= "sl://"; -const std::string LLSLURL::PREFIX_SECONDLIFE	= "secondlife://"; -const std::string LLSLURL::PREFIX_SLURL_OLD		= "http://slurl.com/secondlife/"; - -// For DnD - even though www.slurl.com redirects to slurl.com in a browser, you can copy and drag +#include "llpanellogin.h" +#include "llviewercontrol.h" +#include "llviewernetwork.h" +#include "llfiltersd2xmlrpc.h" +#include "curl/curl.h" +const char* LLSLURL::SLURL_HTTP_SCHEME		 = "http"; +const char* LLSLURL::SLURL_HTTPS_SCHEME		 = "https"; +const char* LLSLURL::SLURL_SECONDLIFE_SCHEME	 = "secondlife"; +const char* LLSLURL::SLURL_SECONDLIFE_PATH	 = "secondlife"; +const char* LLSLURL::SLURL_COM		         = "slurl.com"; +// For DnD - even though www.slurl.com redirects to slurl.com in a browser, you  can copy and drag  // text with www.slurl.com or a link explicitly pointing at www.slurl.com so testing for this  // version is required also. -const std::string LLSLURL::PREFIX_SLURL_WWW		= "http://www.slurl.com/secondlife/"; -const std::string LLSLURL::PREFIX_SLURL			= "http://maps.secondlife.com/secondlife/"; +const char* LLSLURL::WWW_SLURL_COM		 = "www.slurl.com"; +const char* LLSLURL::MAPS_SECONDLIFE_COM	 = "maps.secondlife.com";	 +const char* LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME = "x-grid-location-info"; +const char* LLSLURL::SLURL_APP_PATH              = "app"; +const char* LLSLURL::SLURL_REGION_PATH           = "region"; +const char* LLSLURL::SIM_LOCATION_HOME           = "home"; +const char* LLSLURL::SIM_LOCATION_LAST           = "last"; -const std::string LLSLURL::APP_TOKEN = "app/"; - -// static -std::string LLSLURL::stripProtocol(const std::string& url) +// resolve a simstring from a slurl +LLSLURL::LLSLURL(const std::string& slurl)  { -	std::string stripped = url; -	if (matchPrefix(stripped, PREFIX_SL_HELP)) -	{ -		stripped.erase(0, PREFIX_SL_HELP.length()); -	} -	else if (matchPrefix(stripped, PREFIX_SL)) -	{ -		stripped.erase(0, PREFIX_SL.length()); -	} -	else if (matchPrefix(stripped, PREFIX_SECONDLIFE)) -	{ -		stripped.erase(0, PREFIX_SECONDLIFE.length()); -	} -	else if (matchPrefix(stripped, PREFIX_SLURL)) +	// by default we go to agni. +	mType = INVALID; +	LL_INFOS("AppInit") << "SLURL: " << slurl << LL_ENDL; +	if(slurl == SIM_LOCATION_HOME)  	{ -		stripped.erase(0, PREFIX_SLURL.length()); +		mType = HOME_LOCATION;  	} -	else if (matchPrefix(stripped, PREFIX_SLURL_OLD)) +	else if(slurl.empty() || (slurl == SIM_LOCATION_LAST))  	{ -		stripped.erase(0, PREFIX_SLURL_OLD.length()); + +		mType = LAST_LOCATION;  	} -	else if (matchPrefix(stripped, PREFIX_SLURL_WWW)) +	else  	{ -		stripped.erase(0, PREFIX_SLURL_WWW.length()); +		LLURI slurl_uri; +		// parse the slurl as a uri +		if(slurl.find(':') == std::string::npos) +		{ +			// There may be no scheme ('secondlife:' etc.) passed in.  In that case +			// we want to normalize the slurl by putting the appropriate scheme +			// in front of the slurl.  So, we grab the appropriate slurl base +			// from the grid manager which may be http://slurl.com/secondlife/ for maingrid, or +			// https://<hostname>/region/ for Standalone grid (the word region, not the region name) +			// these slurls are typically passed in from the 'starting location' box on the login panel, +			// where the user can type in <regionname>/<x>/<y>/<z> +			std::string fixed_slurl = LLGridManager::getInstance()->getSLURLBase(); +			// the slurl that was passed in might have a prepended /, or not.  So, +			// we strip off the prepended '/' so we don't end up with http://slurl.com/secondlife/<region>/<x>/<y>/<z> +			// or some such. +			 +			if(slurl[0] == '/') +		    { +				fixed_slurl += slurl.substr(1); +		    } +			else +		    { +				fixed_slurl += slurl; +		    } +			// We then load the slurl into a LLURI form +			slurl_uri = LLURI(fixed_slurl); +		} +		else +		{ +		    // as we did have a scheme, implying a URI style slurl, we +		    // simply parse it as a URI +		    slurl_uri = LLURI(slurl); +		} +		 +		LLSD path_array = slurl_uri.pathArray(); +		 +		// determine whether it's a maingrid URI or an Standalone/open style URI +		// by looking at the scheme.  If it's a 'secondlife:' slurl scheme or +		// 'sl:' scheme, we know it's maingrid +		 +		// At the end of this if/else block, we'll have determined the grid, +		// and the slurl type (APP or LOCATION) +		if(slurl_uri.scheme() == LLSLURL::SLURL_SECONDLIFE_SCHEME) +		{ +			// parse a maingrid style slurl.  We know the grid is maingrid +			// so grab it. +			// A location slurl for maingrid (with the special schemes) can be in the form +			// secondlife://<regionname>/<x>/<y>/<z> +			// or +			// secondlife://<Grid>/secondlife/<region>/<x>/<y>/<z> +			// where if grid is empty, it specifies Agni +			 +			// An app style slurl for maingrid can be +			// secondlife://<Grid>/app/<app parameters> +			// where an empty grid implies Agni +			 +			// we'll start by checking the top of the 'path' which will be  +			// either 'app', 'secondlife', or <x>. +			 +			// default to maingrid +			 +			mGrid = MAINGRID; +			 +			if ((path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH) || +				(path_array[0].asString() == LLSLURL::SLURL_APP_PATH)) +		    { +				// it's in the form secondlife://<grid>/(app|secondlife) +				// so parse the grid name to derive the grid ID +				if (!slurl_uri.hostName().empty()) +				{ +					mGrid = LLGridManager::getInstance()->getGridByLabel(slurl_uri.hostName()); +				} +				else if(path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH) +				{ +					// If the slurl is in the form secondlife:///secondlife/<region> form,  +					// then we are in fact on maingrid.   +					mGrid = MAINGRID; +				} +				else if(path_array[0].asString() == LLSLURL::SLURL_APP_PATH) +				{ +					// for app style slurls, where no grid name is specified, assume the currently +					// selected or logged in grid. +					mGrid =  LLGridManager::getInstance()->getGrid(); +				} + +				if(mGrid.empty()) +				{ +					// we couldn't find the grid in the grid manager, so bail +					return; +				} +				// set the type as appropriate. +				if(path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH) +				{ +					mType = LOCATION; +				} +				else +				{ +					mType = APP; +				} +				path_array.erase(0); +		    } +			else +		    { +				// it wasn't a /secondlife/<region> or /app/<params>, so it must be secondlife://<region> +				// therefore the hostname will be the region name, and it's a location type +				mType = LOCATION; +				// 'normalize' it so the region name is in fact the head of the path_array +				path_array.insert(0, slurl_uri.hostName()); +		    } +		} +		else if((slurl_uri.scheme() == LLSLURL::SLURL_HTTP_SCHEME) || +		   (slurl_uri.scheme() == LLSLURL::SLURL_HTTPS_SCHEME) ||  +		   (slurl_uri.scheme() == LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME)) +		{ +		    // We're dealing with either a Standalone style slurl or slurl.com slurl +		  if ((slurl_uri.hostName() == LLSLURL::SLURL_COM) || +		      (slurl_uri.hostName() == LLSLURL::WWW_SLURL_COM) ||  +		      (slurl_uri.hostName() == LLSLURL::MAPS_SECONDLIFE_COM)) +			{ +				// slurl.com implies maingrid +				mGrid = MAINGRID; +			} +		    else +			{ +				// As it's a Standalone grid/open, we will always have a hostname, as Standalone/open  style +				// urls are properly formed, unlike the stinky maingrid style +				mGrid = slurl_uri.hostName(); +			} +		    if (path_array.size() == 0) +			{ +				// um, we need a path... +				return; +			} +			 +			// we need to normalize the urls so +			// the path portion starts with the 'command' that we want to do +			// it can either be region or app.   +		    if ((path_array[0].asString() == LLSLURL::SLURL_REGION_PATH) || +				(path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH)) +			{ +				// strip off 'region' or 'secondlife' +				path_array.erase(0); +				// it's a location +				mType = LOCATION; +			} +			else if (path_array[0].asString() == LLSLURL::SLURL_APP_PATH) +			{ +				mType = APP; +				path_array.erase(0); +				// leave app appended.   +			} +			else +			{ +				// not a valid https/http/x-grid-location-info slurl, so it'll likely just be a URL +				return; +			} +		} +		else +		{ +		    // invalid scheme, so bail +		    return; +		} +		 +		 +		if(path_array.size() == 0) +		{ +			// we gotta have some stuff after the specifier as to whether it's a region or command +			return; +		} +		 +		// now that we know whether it's an app slurl or a location slurl, +		// parse the slurl into the proper data structures. +		if(mType == APP) +		{		 +			// grab the app command type and strip it (could be a command to jump somewhere,  +			// or whatever ) +			mAppCmd = path_array[0].asString(); +			path_array.erase(0); +			 +			// Grab the parameters +			mAppPath = path_array; +			// and the query +			mAppQuery = slurl_uri.query(); +			mAppQueryMap = slurl_uri.queryMap(); +			return; +		} +		else if(mType == LOCATION) +		{ +			// at this point, head of the path array should be [ <region>, <x>, <y>, <z> ] where x, y and z  +			// are collectively optional +			// are optional +			mRegion = LLURI::unescape(path_array[0].asString()); +			path_array.erase(0); +			 +			// parse the x, y, z +			if(path_array.size() >= 3) +			{	 +			   +			  mPosition = LLVector3(path_array); +			  if((F32(mPosition[VX]) < 0.f) ||  +                             (mPosition[VX] > REGION_WIDTH_METERS) || +			     (F32(mPosition[VY]) < 0.f) ||  +                             (mPosition[VY] > REGION_WIDTH_METERS) || +			     (F32(mPosition[VZ]) < 0.f) ||  +                             (mPosition[VZ] > REGION_HEIGHT_METERS)) +			    { +			      mType = INVALID; +			      return; +			    } +  +			} +			else +			{ +				// if x, y and z were not fully passed in, go to the middle of the region. +				// teleport will adjust the actual location to make sure you're on the ground +				// and such +				mPosition = LLVector3(REGION_WIDTH_METERS/2, REGION_WIDTH_METERS/2, 0); +			} +		}  	} -	 -	return stripped;  } -// static -bool LLSLURL::isSLURL(const std::string& url) + +// Create a slurl for the middle of the region +LLSLURL::LLSLURL(const std::string& grid,  +				 const std::string& region)  { -	if (matchPrefix(url, PREFIX_SL_HELP))		return true; -	if (matchPrefix(url, PREFIX_SL))			return true; -	if (matchPrefix(url, PREFIX_SECONDLIFE))	return true; -	if (matchPrefix(url, PREFIX_SLURL))			return true; -	if (matchPrefix(url, PREFIX_SLURL_OLD))		return true; -	if (matchPrefix(url, PREFIX_SLURL_WWW))		return true; -	 -	return false; +	mGrid = grid; +	mRegion = region; +	mType = LOCATION; +	mPosition = LLVector3((F64)REGION_WIDTH_METERS/2, (F64)REGION_WIDTH_METERS/2, 0);  } -bool LLSLURL::isValidSLURL(const std::string& url) + + +// create a slurl given the position.  The position will be modded with the region +// width handling global positions as well +LLSLURL::LLSLURL(const std::string& grid,  +		 const std::string& region,  +		 const LLVector3& position)  { -	std::string temp_url(url); -	//"www." may appear in DnD- see description of PREFIX_SLURL_WWW. -	// If it is found, we remove it because it isn't expected in regexp. -	if (matchPrefix(url, PREFIX_SLURL_WWW)) -	{ -		size_t position = url.find("www."); -		temp_url.erase(position,4); -	} -	 -	return LLUrlRegistry::getInstance()->isUrl(temp_url); +	mGrid = grid; +	mRegion = region; +	S32 x = llround( (F32)fmod( position[VX], (F32)REGION_WIDTH_METERS ) ); +	S32 y = llround( (F32)fmod( position[VY], (F32)REGION_WIDTH_METERS ) ); +	S32 z = llround( (F32)position[VZ] ); +	mType = LOCATION; +	mPosition = LLVector3(x, y, z);  } -// static -bool LLSLURL::isSLURLCommand(const std::string& url) -{  -	if (matchPrefix(url, PREFIX_SL + APP_TOKEN) || -		matchPrefix(url, PREFIX_SECONDLIFE + "/" + APP_TOKEN) || -		matchPrefix(url, PREFIX_SLURL + APP_TOKEN) || -		matchPrefix(url, PREFIX_SLURL_WWW + APP_TOKEN) || -		matchPrefix(url, PREFIX_SLURL_OLD + APP_TOKEN) ) -	{ -		return true; -	} -	return false; +// create a simstring +LLSLURL::LLSLURL(const std::string& region,  +		 const LLVector3& position) +{ +  *this = LLSLURL(LLGridManager::getInstance()->getGrid(), +		  region, position);  } -// static -bool LLSLURL::isSLURLHelp(const std::string& url) +// create a slurl from a global position +LLSLURL::LLSLURL(const std::string& grid,  +		 const std::string& region,  +		 const LLVector3d& global_position)  { -	return matchPrefix(url, PREFIX_SL_HELP); +  *this = LLSLURL(grid, +		  region, LLVector3(global_position.mdV[VX], +				    global_position.mdV[VY], +				    global_position.mdV[VZ]));  } -// static -std::string LLSLURL::buildSLURL(const std::string& regionname, S32 x, S32 y, S32 z) +// create a slurl from a global position +LLSLURL::LLSLURL(const std::string& region,  +		 const LLVector3d& global_position)  { -	std::string slurl = PREFIX_SLURL + regionname + llformat("/%d/%d/%d",x,y,z);  -	slurl = LLWeb::escapeURL( slurl ); -	return slurl; +  *this = LLSLURL(LLGridManager::getInstance()->getGrid(), +		  region, global_position);  } -// static -std::string LLSLURL::buildCommand(const char* noun, const LLUUID& id, const char* verb) +LLSLURL::LLSLURL(const std::string& command, const LLUUID&id, const std::string& verb)  { -	std::string slurl = llformat("secondlife:///app/%s/%s/%s", -		noun, id.asString().c_str(), verb); -	return slurl; +  mType = APP; +  mAppCmd = command; +  mAppPath = LLSD::emptyArray(); +  mAppPath.append(LLSD(id)); +  mAppPath.append(LLSD(verb));  } -// static -std::string LLSLURL::buildUnescapedSLURL(const std::string& regionname, S32 x, S32 y, S32 z) + +std::string LLSLURL::getSLURLString() const  { -	std::string unescapedslurl = PREFIX_SLURL + regionname + llformat("/%d/%d/%d",x,y,z); -	return unescapedslurl; +	switch(mType) +	{ +		case HOME_LOCATION: +			return SIM_LOCATION_HOME; +		case LAST_LOCATION: +			return SIM_LOCATION_LAST; +		case LOCATION: +			{ +				// lookup the grid +				S32 x = llround( (F32)mPosition[VX] ); +				S32 y = llround( (F32)mPosition[VY] ); +				S32 z = llround( (F32)mPosition[VZ] );	 +				return LLGridManager::getInstance()->getSLURLBase(mGrid) +  +				LLURI::escape(mRegion) + llformat("/%d/%d/%d",x,y,z);  +			} +		case APP: +		{ +			std::ostringstream app_url; +			app_url << LLGridManager::getInstance()->getAppSLURLBase() << "/" << mAppCmd; +			for(LLSD::array_const_iterator i = mAppPath.beginArray(); +				i != mAppPath.endArray(); +				i++) +			{ +				app_url << "/" << i->asString(); +			} +			if(mAppQuery.length() > 0) +			{ +				app_url << "?" << mAppQuery; +			} +			return app_url.str(); +		}	 +		default: +			LL_WARNS("AppInit") << "Unexpected SLURL type for SLURL string" << (int)mType << LL_ENDL;			 +			return std::string(); +	}  } -// static -std::string LLSLURL::buildSLURLfromPosGlobal(const std::string& regionname, -											 const LLVector3d& global_pos, -											 bool escaped /*= true*/) +std::string LLSLURL::getLoginString() const  { -	S32 x, y, z; -	globalPosToXYZ(global_pos, x, y, z); -	if(escaped) +	 +	std::stringstream unescaped_start; +	switch(mType)  	{ -		return buildSLURL(regionname, x, y, z); +		case LOCATION: +			unescaped_start << "uri:"  +			<< mRegion << "&"  +			<< llround(mPosition[0]) << "&"  +			<< llround(mPosition[1]) << "&"  +			<< llround(mPosition[2]); +			break; +		case HOME_LOCATION: +			unescaped_start << "home"; +			break; +		case LAST_LOCATION: +			unescaped_start << "last"; +			break; +		default: +			LL_WARNS("AppInit") << "Unexpected SLURL type for login string" << (int)mType << LL_ENDL; +			break;  	} -	else +	return  xml_escape_string(unescaped_start.str()); +} + +bool LLSLURL::operator==(const LLSLURL& rhs) +{ +	if(rhs.mType != mType) return false; +	switch(mType)  	{ -		return buildUnescapedSLURL(regionname, x, y, z); +		case LOCATION: +			return ((mGrid == rhs.mGrid) && +					(mRegion == rhs.mRegion) && +					(mPosition == rhs.mPosition)); +		case APP: +			return getSLURLString() == rhs.getSLURLString(); +			 +		case HOME_LOCATION: +		case LAST_LOCATION: +			return true; +		default: +			return false;  	}  } -// static -bool LLSLURL::matchPrefix(const std::string& url, const std::string& prefix) +bool LLSLURL::operator !=(const LLSLURL& rhs)  { -	std::string test_prefix = url.substr(0, prefix.length()); -	LLStringUtil::toLower(test_prefix); -	return test_prefix == prefix; +	return !(*this == rhs);  } -void LLSLURL::globalPosToXYZ(const LLVector3d& pos, S32& x, S32& y, S32& z) +std::string LLSLURL::getLocationString() const +{ +	return llformat("%s/%d/%d/%d", +					mRegion.c_str(), +					(int)llround(mPosition[0]), +					(int)llround(mPosition[1]), +					(int)llround(mPosition[2]));						  +} +std::string LLSLURL::asString() const  { -	x = llround((F32)fmod(pos.mdV[VX], (F64)REGION_WIDTH_METERS)); -	y = llround((F32)fmod(pos.mdV[VY], (F64)REGION_WIDTH_METERS)); -	z = llround((F32)pos.mdV[VZ]); +    std::ostringstream result; +    result << "   mAppCmd:"  << getAppCmd() << +              "   mAppPath:" + getAppPath().asString() << +              "   mAppQueryMap:" + getAppQueryMap().asString() << +              "   mAppQuery: " + getAppQuery() << +              "   mGrid: " + getGrid() << +              "   mRegion: " + getRegion() << +              "   mPosition: "  << +              "   mType: " << mType << +              "   mPosition: " << mPosition; +    return result.str();  } + diff --git a/indra/newview/llslurl.h b/indra/newview/llslurl.h index a79a8fc97c..28c23561cf 100644 --- a/indra/newview/llslurl.h +++ b/indra/newview/llslurl.h @@ -1,10 +1,11 @@ -/**  +/**   * @file llslurl.h - * @brief SLURL manipulation + * @brief Handles "SLURL fragments" like Ahern/123/45 for + * startup processing, login screen, prefs, etc.   * - * $LicenseInfo:firstyear=2009&license=viewergpl$ + * $LicenseInfo:firstyear=2010&license=viewergpl$   *  - * Copyright (c) 2009, Linden Research, Inc. + * Copyright (c) 2006-2010, Linden Research, Inc.   *    * Second Life Viewer Source Code   * The source code in this file ("Source Code") is provided by Linden Lab @@ -12,13 +13,12 @@   * ("GPL"), unless you have obtained a separate licensing agreement   * ("Other License"), formally executed by you and Linden Lab.  Terms of   * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2   *    * There are special exceptions to the terms and conditions of the GPL as   * it is applied to this Source Code. View the full text of the exception   * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * online at http://secondlife.com/developers/opensource/flossexception   *    * By copying, modifying or distributing this software, you acknowledge   * that you have read and understood your obligations described above, @@ -29,85 +29,84 @@   * COMPLETENESS OR PERFORMANCE.   * $/LicenseInfo$   */ +#ifndef LLSLURL_H +#define LLSLURL_H -#ifndef LL_SLURL_H -#define LL_SLURL_H +#include "llstring.h" -#include <string> -// IAN BUG: where should this live? -// IAN BUG: are static utility functions right?  See LLUUID. -// question of whether to have a LLSLURL object or a  -// some of this was moved from LLURLDispatcher +// represents a location in a grid -/** - * SLURL manipulation - */  class LLSLURL  {  public: -	static const std::string PREFIX_SL_HELP; -	static const std::string PREFIX_SL; -	static const std::string PREFIX_SECONDLIFE; -	static const std::string PREFIX_SLURL; -	static const std::string PREFIX_SLURL_OLD; -	static const std::string PREFIX_SLURL_WWW; - -	static const std::string APP_TOKEN; - -	/** -	 * Is this any sort of secondlife:// or sl:// URL? -	 */ -	static bool isSLURL(const std::string& url); - -	/** -	 * Returns true if url is proven valid by regexp check from LLUrlRegistry -	 */ -	static bool isValidSLURL(const std::string& url); - -	/** -	 * Is this a special secondlife://app/ URL? -	 */ -	static bool isSLURLCommand(const std::string& url); - -	/** -	 * Not sure what it is. -	 */ -	static bool isSLURLHelp(const std::string& url); - -	/** -	 * builds: http://slurl.com/secondlife/Region%20Name/x/y/z/ escaping result url. -	 */ -	static std::string buildSLURL(const std::string& regionname, S32 x, S32 y, S32 z); - -	/// Build a SLURL like secondlife:///app/agent/<uuid>/inspect -	static std::string buildCommand(const char* noun, const LLUUID& id, const char* verb); - -	/** -	 * builds: http://slurl.com/secondlife/Region Name/x/y/z/ without escaping result url. -	 */ -	static std::string buildUnescapedSLURL(const std::string& regionname, S32 x, S32 y, S32 z); - -	/** -	 * builds SLURL from global position. Returns escaped or unescaped url. -	 * Returns escaped url by default. -	 */ -	static std::string buildSLURLfromPosGlobal(const std::string& regionname, -											   const LLVector3d& global_pos, -											   bool escaped = true); -	/** -	 * Strip protocol part from the URL. -	 */ -	static std::string stripProtocol(const std::string& url); - -	/** -	 * Convert global position to X, Y Z -	 */ -	static void globalPosToXYZ(const LLVector3d& pos, S32& x, S32& y, S32& z); - -private: -	static bool matchPrefix(const std::string& url, const std::string& prefix); - +	static const char* SLURL_HTTPS_SCHEME; +	static const char* SLURL_HTTP_SCHEME; +	static const char* SLURL_SL_SCHEME; +	static const char* SLURL_SECONDLIFE_SCHEME; +	static const char* SLURL_SECONDLIFE_PATH; +	static const char* SLURL_COM; +	static const char* WWW_SLURL_COM; +	static const char* MAPS_SECONDLIFE_COM; +	static const char* SLURL_X_GRID_LOCATION_INFO_SCHEME; +	static LLSLURL START_LOCATION; +	static const char* SIM_LOCATION_HOME; +	static const char* SIM_LOCATION_LAST; +	static const char* SLURL_APP_PATH; +	static const char* SLURL_REGION_PATH;	 +	 +	enum SLURL_TYPE {  +		INVALID,  +		LOCATION, +		HOME_LOCATION, +		LAST_LOCATION, +		APP, +		HELP  +	}; +		 +	 +	LLSLURL(): mType(LAST_LOCATION)  { } +	LLSLURL(const std::string& slurl); +	LLSLURL(const std::string& grid, const std::string& region); +	LLSLURL(const std::string& region, const LLVector3& position); +	LLSLURL(const std::string& grid, const std::string& region, const LLVector3& position); +	LLSLURL(const std::string& grid, const std::string& region, const LLVector3d& global_position); +	LLSLURL(const std::string& region, const LLVector3d& global_position); +	LLSLURL(const std::string& command, const LLUUID&id, const std::string& verb); +	 +	SLURL_TYPE getType() const { return mType; } +	 +	std::string getSLURLString() const; +	std::string getLoginString() const; +	std::string getLocationString() const;  +	std::string getGrid() const { return mGrid; } +	std::string getRegion() const { return mRegion; } +	LLVector3   getPosition() const { return mPosition; } +	std::string getAppCmd() const { return mAppCmd; } +	std::string getAppQuery() const { return mAppQuery; } +	LLSD        getAppQueryMap() const { return mAppQueryMap; } +	LLSD        getAppPath() const { return mAppPath; } +	 +	bool        isValid() const { return mType != INVALID; } +	bool        isSpatial() const { return (mType == LAST_LOCATION) || (mType == HOME_LOCATION) || (mType == LOCATION); } +	 +	bool operator==(const LLSLURL& rhs); +	bool operator!=(const LLSLURL&rhs); + +    std::string asString() const ; + +protected: +	SLURL_TYPE mType; +	 +	// used for Apps and Help +	std::string mAppCmd; +	LLSD        mAppPath; +	LLSD        mAppQueryMap; +	std::string mAppQuery; +	 +	std::string mGrid;  // reference to grid manager grid +	std::string mRegion; +	LLVector3  mPosition;  }; -#endif +#endif // LLSLURL_H diff --git a/indra/newview/llspeakbutton.cpp b/indra/newview/llspeakbutton.cpp index c5c311ed33..d7de050636 100644 --- a/indra/newview/llspeakbutton.cpp +++ b/indra/newview/llspeakbutton.cpp @@ -59,9 +59,9 @@ LLSpeakButton::Params::Params()  void LLSpeakButton::draw()  { -	// gVoiceClient is the authoritative global source of info regarding our open-mic state, we merely reflect that state. -	bool openmic = gVoiceClient->getUserPTTState(); -	bool voiceenabled = gVoiceClient->voiceEnabled(); +	// LLVoiceClient::getInstance() is the authoritative global source of info regarding our open-mic state, we merely reflect that state. +	bool openmic = LLVoiceClient::getInstance()->getUserPTTState(); +	bool voiceenabled = LLVoiceClient::getInstance()->voiceEnabled();  	mSpeakBtn->setToggleState(openmic && voiceenabled);  	mOutputMonitor->setIsMuted(!voiceenabled);  	LLUICtrl::draw(); @@ -176,11 +176,11 @@ void LLSpeakButton::setLabelVisible(bool visible)  void LLSpeakButton::onMouseDown_SpeakBtn()  {  	bool down = true; -	gVoiceClient->inputUserControlState(down); // this method knows/care about whether this translates into a toggle-to-talk or down-to-talk +	LLVoiceClient::getInstance()->inputUserControlState(down); // this method knows/care about whether this translates into a toggle-to-talk or down-to-talk  }  void LLSpeakButton::onMouseUp_SpeakBtn()  {  	bool down = false; -	gVoiceClient->inputUserControlState(down); +	LLVoiceClient::getInstance()->inputUserControlState(down);  } diff --git a/indra/newview/llspeakers.cpp b/indra/newview/llspeakers.cpp index 4573520647..b9534fac9a 100644 --- a/indra/newview/llspeakers.cpp +++ b/indra/newview/llspeakers.cpp @@ -299,7 +299,7 @@ LLPointer<LLSpeaker> LLSpeakerMgr::setSpeaker(const LLUUID& id, const std::strin  void LLSpeakerMgr::update(BOOL resort_ok)  { -	if (!gVoiceClient) +	if (!LLVoiceClient::getInstance())  	{  		return;  	} @@ -313,7 +313,7 @@ void LLSpeakerMgr::update(BOOL resort_ok)  	}  	// update status of all current speakers -	BOOL voice_channel_active = (!mVoiceChannel && gVoiceClient->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive()); +	BOOL voice_channel_active = (!mVoiceChannel && LLVoiceClient::getInstance()->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive());  	for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end();)  	{  		LLUUID speaker_id = speaker_it->first; @@ -321,21 +321,21 @@ void LLSpeakerMgr::update(BOOL resort_ok)  		speaker_map_t::iterator  cur_speaker_it = speaker_it++; -		if (voice_channel_active && gVoiceClient->getVoiceEnabled(speaker_id)) +		if (voice_channel_active && LLVoiceClient::getInstance()->getVoiceEnabled(speaker_id))  		{ -			speakerp->mSpeechVolume = gVoiceClient->getCurrentPower(speaker_id); -			BOOL moderator_muted_voice = gVoiceClient->getIsModeratorMuted(speaker_id); +			speakerp->mSpeechVolume = LLVoiceClient::getInstance()->getCurrentPower(speaker_id); +			BOOL moderator_muted_voice = LLVoiceClient::getInstance()->getIsModeratorMuted(speaker_id);  			if (moderator_muted_voice != speakerp->mModeratorMutedVoice)  			{  				speakerp->mModeratorMutedVoice = moderator_muted_voice;  				speakerp->fireEvent(new LLSpeakerVoiceModerationEvent(speakerp));  			} -			if (gVoiceClient->getOnMuteList(speaker_id) || speakerp->mModeratorMutedVoice) +			if (LLVoiceClient::getInstance()->getOnMuteList(speaker_id) || speakerp->mModeratorMutedVoice)  			{  				speakerp->mStatus = LLSpeaker::STATUS_MUTED;  			} -			else if (gVoiceClient->getIsSpeaking(speaker_id)) +			else if (LLVoiceClient::getInstance()->getIsSpeaking(speaker_id))  			{  				// reset inactivity expiration  				if (speakerp->mStatus != LLSpeaker::STATUS_SPEAKING) @@ -417,19 +417,21 @@ void LLSpeakerMgr::update(BOOL resort_ok)  void LLSpeakerMgr::updateSpeakerList()  {  	// are we bound to the currently active voice channel? -	if ((!mVoiceChannel && gVoiceClient->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive())) -	{ -		LLVoiceClient::participantMap* participants = gVoiceClient->getParticipantList(); -		if(participants) +	if ((!mVoiceChannel && LLVoiceClient::getInstance()->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive())) +	{ +	        std::set<LLUUID> participants; +	        LLVoiceClient::getInstance()->getParticipantList(participants); +		// add new participants to our list of known speakers +		for (std::set<LLUUID>::iterator participant_it = participants.begin(); +			 participant_it != participants.end();  +			 ++participant_it)  		{ -			LLVoiceClient::participantMap::iterator participant_it; +				setSpeaker(*participant_it,  +						   LLVoiceClient::getInstance()->getDisplayName(*participant_it), +						   LLSpeaker::STATUS_VOICE_ACTIVE,  +						   (LLVoiceClient::getInstance()->isParticipantAvatar(*participant_it)?LLSpeaker::SPEAKER_AGENT:LLSpeaker::SPEAKER_EXTERNAL)); + -			// add new participants to our list of known speakers -			for (participant_it = participants->begin(); participant_it != participants->end(); ++participant_it) -			{ -				LLVoiceClient::participantState* participantp = participant_it->second; -				setSpeaker(participantp->mAvatarID, participantp->mDisplayName, LLSpeaker::STATUS_VOICE_ACTIVE, (participantp->isAvatar()?LLSpeaker::SPEAKER_AGENT:LLSpeaker::SPEAKER_EXTERNAL)); -			}  		}  	}  } @@ -519,7 +521,7 @@ void LLSpeakerMgr::speakerChatted(const LLUUID& speaker_id)  BOOL LLSpeakerMgr::isVoiceActive()  {  	// mVoiceChannel = NULL means current voice channel, whatever it is -	return LLVoiceClient::voiceEnabled() && mVoiceChannel && mVoiceChannel->isActive(); +	return LLVoiceClient::getInstance()->voiceEnabled() && mVoiceChannel && mVoiceChannel->isActive();  } diff --git a/indra/newview/llspeakingindicatormanager.cpp b/indra/newview/llspeakingindicatormanager.cpp index cc06179481..29237946d2 100644 --- a/indra/newview/llspeakingindicatormanager.cpp +++ b/indra/newview/llspeakingindicatormanager.cpp @@ -158,7 +158,7 @@ void SpeakingIndicatorManager::registerSpeakingIndicator(const LLUUID& speaker_i  	mSpeakingIndicators.insert(value_type);  	speaker_ids_t speakers_uuids; -	BOOL is_in_same_voice = LLVoiceClient::getInstance()->findParticipantByID(speaker_id) != NULL; +	BOOL is_in_same_voice = LLVoiceClient::getInstance()->isParticipant(speaker_id);  	speakers_uuids.insert(speaker_id);  	switchSpeakerIndicators(speakers_uuids, is_in_same_voice); @@ -210,7 +210,7 @@ void SpeakingIndicatorManager::onChange()  	LL_DEBUGS("SpeakingIndicator") << "Voice participant list was changed, updating indicators" << LL_ENDL;  	speaker_ids_t speakers_uuids; -	LLVoiceClient::getInstance()->getParticipantsUUIDSet(speakers_uuids); +	LLVoiceClient::getInstance()->getParticipantList(speakers_uuids);  	LL_DEBUGS("SpeakingIndicator") << "Switching all OFF, count: " << mSwitchedIndicatorsOn.size() << LL_ENDL;  	// switch all indicators off diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index b5a73a3143..4f1bcde302 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -147,7 +147,7 @@  #include "lltrans.h"  #include "llui.h"  #include "llurldispatcher.h" -#include "llurlsimstring.h" +#include "llslurl.h"  #include "llurlhistory.h"  #include "llurlwhitelist.h"  #include "llvieweraudio.h" @@ -192,6 +192,7 @@  #include "llinventorybridge.h"  #include "llappearancemgr.h"  #include "llavatariconctrl.h" +#include "llvoicechannel.h"  #include "lllogin.h"  #include "llevents.h" @@ -228,11 +229,11 @@ static std::string sInitialOutfitGender;	// "male" or "female"  static bool gUseCircuitCallbackCalled = false;  EStartupState LLStartUp::gStartupState = STATE_FIRST; +LLSLURL LLStartUp::sStartSLURL; -// *NOTE:Mani - to reconcile with giab changes... -static std::string gFirstname; -static std::string gLastname; -static std::string gPassword; +static LLPointer<LLCredential> gUserCredential; +static std::string gDisplayName; +static BOOL gRememberPassword = TRUE;       static U64 gFirstSimHandle = 0;  static LLHost gFirstSim; @@ -249,7 +250,6 @@ boost::scoped_ptr<LLStartupListener> LLStartUp::sListener(new LLStartupListener(  void login_show();  void login_callback(S32 option, void* userdata); -bool is_hex_string(U8* str, S32 len);  void show_first_run_dialog();  bool first_run_dialog_callback(const LLSD& notification, const LLSD& response);  void set_startup_status(const F32 frac, const std::string& string, const std::string& msg); @@ -262,6 +262,9 @@ bool callback_choose_gender(const LLSD& notification, const LLSD& response);  void init_start_screen(S32 location_id);  void release_start_screen();  void reset_login(); +LLSD transform_cert_args(LLPointer<LLCertificate> cert); +void general_cert_done(const LLSD& notification, const LLSD& response); +void trust_cert_done(const LLSD& notification, const LLSD& response);  void apply_udp_blacklist(const std::string& csv);  bool process_login_success_response();  void transition_back_to_login_panel(const std::string& emsg); @@ -364,7 +367,7 @@ bool idle_startup()  	if ( STATE_FIRST == LLStartUp::getStartupState() )  	{ -		gViewerWindow->showCursor(); +		gViewerWindow->showCursor();   		gViewerWindow->getWindow()->setCursor(UI_CURSOR_WAIT);  		///////////////////////////////////////////////// @@ -662,69 +665,25 @@ bool idle_startup()  		//  		// Log on to system  		// -		if (!LLStartUp::sSLURLCommand.empty()) -		{ -			// this might be a secondlife:///app/login URL -			gLoginHandler.parseDirectLogin(LLStartUp::sSLURLCommand); -		} -		if (!gLoginHandler.getFirstName().empty() -			|| !gLoginHandler.getLastName().empty() -			/*|| !gLoginHandler.getWebLoginKey().isNull()*/ ) -		{ -			// We have at least some login information on a SLURL -			gFirstname = gLoginHandler.getFirstName(); -			gLastname = gLoginHandler.getLastName(); -			LL_DEBUGS("LLStartup") << "STATE_FIRST: setting gFirstname, gLastname from gLoginHandler: '" << gFirstname << "' '" << gLastname << "'" << LL_ENDL; - -			// Show the login screen if we don't have everything -			show_connect_box =  -				gFirstname.empty() || gLastname.empty(); -		} -        else if(gSavedSettings.getLLSD("UserLoginInfo").size() == 3) -        { -            LLSD cmd_line_login = gSavedSettings.getLLSD("UserLoginInfo"); -			gFirstname = cmd_line_login[0].asString(); -			gLastname = cmd_line_login[1].asString(); -			LL_DEBUGS("LLStartup") << "Setting gFirstname, gLastname from gSavedSettings(\"UserLoginInfo\"): '" << gFirstname << "' '" << gLastname << "'" << LL_ENDL; - -			LLMD5 pass((unsigned char*)cmd_line_login[2].asString().c_str()); -			char md5pass[33];               /* Flawfinder: ignore */ -			pass.hex_digest(md5pass); -			gPassword = md5pass; -			 -#ifdef USE_VIEWER_AUTH -			show_connect_box = true; -#else -			show_connect_box = false; -#endif -			gSavedSettings.setBOOL("AutoLogin", TRUE); -        } -		else if (gSavedSettings.getBOOL("AutoLogin")) -		{ -			gFirstname = gSavedSettings.getString("FirstName"); -			gLastname = gSavedSettings.getString("LastName"); -			LL_DEBUGS("LLStartup") << "AutoLogin: setting gFirstname, gLastname from gSavedSettings(\"First|LastName\"): '" << gFirstname << "' '" << gLastname << "'" << LL_ENDL; -			gPassword = LLStartUp::loadPasswordFromDisk(); -			gSavedSettings.setBOOL("RememberPassword", TRUE); -			 -#ifdef USE_VIEWER_AUTH -			show_connect_box = true; -#else -			show_connect_box = false; -#endif +		if (gUserCredential.isNull()) +		{ +			gUserCredential = gLoginHandler.initializeLoginInfo();  		} -		else +		if (gUserCredential.isNull())  		{ -			// if not automatically logging in, display login dialog -			// a valid grid is selected -			gFirstname = gSavedSettings.getString("FirstName"); -			gLastname = gSavedSettings.getString("LastName"); -			LL_DEBUGS("LLStartup") << "normal login: setting gFirstname, gLastname from gSavedSettings(\"First|LastName\"): '" << gFirstname << "' '" << gLastname << "'" << LL_ENDL; -			gPassword = LLStartUp::loadPasswordFromDisk(); -			show_connect_box = true; +			show_connect_box = TRUE; +		} +		else if (gSavedSettings.getBOOL("AutoLogin"))   +		{ +			gRememberPassword = TRUE; +			gSavedSettings.setBOOL("RememberPassword", TRUE);                                                       +			show_connect_box = false;    			 +		} +		else  +		{ +			gRememberPassword = gSavedSettings.getBOOL("RememberPassword"); +			show_connect_box = TRUE;  		} - -  		// Go to the next startup state  		LLStartUp::setStartupState( STATE_BROWSER_INIT );  		return FALSE; @@ -756,8 +715,10 @@ bool idle_startup()  			// Load all the name information out of the login view  			// NOTE: Hits "Attempted getFields with no login view shown" warning, since we don't  			// show the login view until login_show() is called below.   -			// LLPanelLogin::getFields(gFirstname, gLastname, gPassword); - +			if (gUserCredential.isNull())                                                                           +			{                                                                                                       +				gUserCredential = gLoginHandler.initializeLoginInfo();                  +			}       			if (gNoRender)  			{  				LL_ERRS("AppInit") << "Need to autologin or use command line with norender!" << LL_ENDL; @@ -768,8 +729,10 @@ bool idle_startup()  			// Show the login dialog  			login_show();  			// connect dialog is already shown, so fill in the names -			LLPanelLogin::setFields( gFirstname, gLastname, gPassword); - +			if (gUserCredential.notNull())                                                                          +			{                                                                                                       +				LLPanelLogin::setFields( gUserCredential, gRememberPassword);                                   +			}       			LLPanelLogin::giveFocus();  			gSavedSettings.setBOOL("FirstRunThisInstall", FALSE); @@ -839,39 +802,36 @@ bool idle_startup()  		// DEV-42215: Make sure they're not empty -- gFirstname and gLastname  		// might already have been set from gSavedSettings, and it's too bad  		// to overwrite valid values with empty strings. -		if (! gLoginHandler.getFirstName().empty() && ! gLoginHandler.getLastName().empty()) -		{ -			gFirstname = gLoginHandler.getFirstName(); -			gLastname = gLoginHandler.getLastName(); -			LL_DEBUGS("LLStartup") << "STATE_LOGIN_CLEANUP: setting gFirstname, gLastname from gLoginHandler: '" << gFirstname << "' '" << gLastname << "'" << LL_ENDL; -		}  		if (show_connect_box)  		{  			// TODO if not use viewer auth  			// Load all the name information out of the login view -			LLPanelLogin::getFields(&gFirstname, &gLastname, &gPassword); +			LLPanelLogin::getFields(gUserCredential, gRememberPassword);   			// end TODO  			// HACK: Try to make not jump on login  			gKeyboard->resetKeys();  		} -		if (!gFirstname.empty() && !gLastname.empty()) -		{ -			gSavedSettings.setString("FirstName", gFirstname); -			gSavedSettings.setString("LastName", gLastname); - -			LL_INFOS("AppInit") << "Attempting login as: " << gFirstname << " " << gLastname << LL_ENDL; -			gDebugInfo["LoginName"] = gFirstname + " " + gLastname;	 +		// save the credentials                                                                                         +		std::string userid = "unknown";                                                                                 +		if(gUserCredential.notNull())                                                                                   +		{   +			userid = gUserCredential->userID();                                                                     +			gSecAPIHandler->saveCredential(gUserCredential, gRememberPassword);    		} - +		gSavedSettings.setBOOL("RememberPassword", gRememberPassword);                                                  +		LL_INFOS("AppInit") << "Attempting login as: " << userid << LL_ENDL;                                            +		gDebugInfo["LoginName"] = userid;                                                                               +           		// create necessary directories  		// *FIX: these mkdir's should error check -		gDirUtilp->setLindenUserDir(gFirstname, gLastname); +		gDirUtilp->setLindenUserDir(userid);  		LLFile::mkdir(gDirUtilp->getLindenUserDir()); -		 +  		// Set PerAccountSettingsFile to the default value. +		std::string per_account_settings_file = LLAppViewer::instance()->getSettingsFilename("Default", "PerAccount");  		gSavedSettings.setString("PerAccountSettingsFile",  			gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT,   				LLAppViewer::instance()->getSettingsFilename("Default", "PerAccount"))); @@ -901,9 +861,8 @@ bool idle_startup()  		{  			gDirUtilp->setChatLogsDir(gSavedPerAccountSettings.getString("InstantMessageLogPath"));		  		} +		gDirUtilp->setPerAccountChatLogsDir(userid);   -		gDirUtilp->setPerAccountChatLogsDir(gFirstname, gLastname); -  		LLFile::mkdir(gDirUtilp->getChatLogsDir());  		LLFile::mkdir(gDirUtilp->getPerAccountChatLogsDir()); @@ -924,11 +883,7 @@ bool idle_startup()  		if (show_connect_box)  		{ -			std::string location; -			LLPanelLogin::getLocation( location ); -			LLURLSimString::setString( location ); - -			// END TODO +			LLSLURL slurl;  			LLPanelLogin::closePanel();  		} @@ -952,26 +907,21 @@ bool idle_startup()  		// their last location, or some URL "-url //sim/x/y[/z]"  		// All accounts have both a home and a last location, and we don't support  		// more locations than that.  Choose the appropriate one.  JC -		if (LLURLSimString::parse()) -		{ -			// a startup URL was specified -			agent_location_id = START_LOCATION_ID_URL; - -			// doesn't really matter what location_which is, since -			// gAgentStartLookAt will be overwritten when the -			// UserLoginLocationReply arrives -			location_which = START_LOCATION_ID_LAST; -		} -		else if (gSavedSettings.getString("LoginLocation") == "last" ) -		{ -			agent_location_id = START_LOCATION_ID_LAST;	// last location -			location_which = START_LOCATION_ID_LAST; -		} -		else -		{ -			agent_location_id = START_LOCATION_ID_HOME;	// home -			location_which = START_LOCATION_ID_HOME; -		} +		switch (LLStartUp::getStartSLURL().getType()) +		  { +		  case LLSLURL::LOCATION: +		    agent_location_id = START_LOCATION_ID_URL; +		    location_which = START_LOCATION_ID_LAST; +		    break; +		  case LLSLURL::LAST_LOCATION: +		    agent_location_id = START_LOCATION_ID_LAST; +		    location_which = START_LOCATION_ID_LAST; +		    break; +		  default: +		    agent_location_id = START_LOCATION_ID_HOME; +		    location_which = START_LOCATION_ID_HOME; +		    break; +		  }  		gViewerWindow->getWindow()->setCursor(UI_CURSOR_WAIT); @@ -998,7 +948,7 @@ bool idle_startup()  	if(STATE_LOGIN_AUTH_INIT == LLStartUp::getStartupState())  	{ -		gDebugInfo["GridName"] = LLViewerLogin::getInstance()->getGridLabel(); +		gDebugInfo["GridName"] = LLGridManager::getInstance()->getGridLabel();  		// Update progress status and the display loop.  		auth_desc = LLTrans::getString("LoginInProgress"); @@ -1022,11 +972,7 @@ bool idle_startup()  		// This call to LLLoginInstance::connect() starts the   		// authentication process. -		LLSD credentials; -		credentials["first"] = gFirstname; -		credentials["last"] = gLastname; -		credentials["passwd"] = gPassword; -		login->connect(credentials); +		login->connect(gUserCredential);  		LLStartUp::setStartupState( STATE_LOGIN_CURL_UNSTUCK );  		return FALSE; @@ -1051,10 +997,11 @@ bool idle_startup()  		{  			LL_INFOS("LLStartup") << "Login failed, LLLoginInstance::getResponse(): "  			                      << LLLoginInstance::getInstance()->getResponse() << LL_ENDL; +			LLSD response = LLLoginInstance::getInstance()->getResponse();  			// Still have error conditions that may need some   			// sort of handling. -			std::string reason_response = LLLoginInstance::getInstance()->getResponse("reason"); -			std::string message_response = LLLoginInstance::getInstance()->getResponse("message"); +			std::string reason_response = response["reason"]; +			std::string message_response = response["message"];  			if(!message_response.empty())  			{ @@ -1074,8 +1021,8 @@ bool idle_startup()  			if(reason_response == "key")  			{  				// Couldn't login because user/password is wrong -				// Clear the password -				gPassword = ""; +				// Clear the credential +				gUserCredential->clearAuthenticator();  			}  			if(reason_response == "update"  @@ -1088,18 +1035,65 @@ bool idle_startup()  				LLLoginInstance::getInstance()->disconnect();  				LLAppViewer::instance()->forceQuit();  			} -			else +			else   			{ -				// Don't pop up a notification in the TOS case because -				// LLFloaterTOS::onCancel() already scolded the user. -				if (reason_response != "tos") +				if (reason_response != "tos")   				{ -					LLSD args; -					args["ERROR_MESSAGE"] = emsg.str(); -					LL_INFOS("LLStartup") << "Notification: " << args << LL_ENDL; -					LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done); +					// Don't pop up a notification in the TOS case because +					// LLFloaterTOS::onCancel() already scolded the user. +					std::string error_code; +					if(response.has("errorcode")) +					{ +						error_code = response["errorcode"].asString(); +					} +					if ((reason_response == "CURLError") &&  +						(error_code == "SSL_CACERT" || error_code == "SSL_PEER_CERTIFICATE") &&  +						response.has("certificate")) +					{ +						// This was a certificate error, so grab the certificate +						// and throw up the appropriate dialog. +						LLPointer<LLCertificate> certificate = gSecAPIHandler->getCertificate(response["certificate"]); +						if(certificate) +						{ +							LLSD args = transform_cert_args(certificate); + +							if(error_code == "SSL_CACERT") +							{ +								// if we are handling an untrusted CA, throw up the dialog                              +								// with the 'trust this CA' button.                                                     +								LLNotificationsUtil::add("TrustCertificateError", args, response, +														trust_cert_done); +								 +								show_connect_box = true; +							} +							else +							{ +								// the certificate exception returns a unique string for each type of exception.        +								// we grab this string via the LLUserAuth object, and use that to grab the localized    +								// string.                                                                              +								args["REASON"] = LLTrans::getString(message_response); +								 +								LLNotificationsUtil::add("GeneralCertificateError", args, response, +														 general_cert_done); +								 +								reset_login(); +								gSavedSettings.setBOOL("AutoLogin", FALSE); +								show_connect_box = true; +								 +							} + +						} +					} +					else  +					{ +						// This wasn't a certificate error, so throw up the normal +						// notificatioin message. +						LLSD args; +						args["ERROR_MESSAGE"] = emsg.str(); +						LL_INFOS("LLStartup") << "Notification: " << args << LL_ENDL; +						LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done); +					}  				} -  				//setup map of datetime strings to codes and slt & local time offset from utc  				// *TODO: Does this need to be here?  				LLStringOps::setupDatetimeInfo (false); @@ -1112,7 +1106,12 @@ bool idle_startup()  			if(process_login_success_response())  			{  				// Pass the user information to the voice chat server interface. -				gVoiceClient->userAuthorized(gFirstname, gLastname, gAgentID); +				LLVoiceClient::getInstance()->userAuthorized(gUserCredential->userID(), gAgentID); +				// create the default proximal channel +				LLVoiceChannel::initClass(); +				// update the voice settings +				LLVoiceClient::getInstance()->updateSettings(); +				LLGridManager::getInstance()->setFavorite();   				LLStartUp::setStartupState( STATE_WORLD_INIT);  			}  			else @@ -1123,6 +1122,7 @@ bool idle_startup()  				LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done);  				transition_back_to_login_panel(emsg.str());  				show_connect_box = true; +				return FALSE;  			}  		}  		return FALSE; @@ -1807,9 +1807,12 @@ bool idle_startup()  		// thus, do not show this alert.  		if (!gAgent.isFirstLogin())  		{ -			bool url_ok = LLURLSimString::sInstance.parse(); -			if ((url_ok && gAgentStartLocation == "url") || -				(!url_ok && ((gAgentStartLocation == gSavedSettings.getString("LoginLocation"))))) +			llinfos << "gAgentStartLocation : " << gAgentStartLocation << llendl; +			LLSLURL start_slurl = LLStartUp::getStartSLURL(); +			 +			if (((start_slurl.getType() == LLSLURL::LOCATION) && (gAgentStartLocation == "url")) || +				((start_slurl.getType() == LLSLURL::LAST_LOCATION) && (gAgentStartLocation == "last")) || +				((start_slurl.getType() == LLSLURL::HOME_LOCATION) && (gAgentStartLocation == "home")))  			{  				// Start location is OK  				// Disabled code to restore camera location and focus if logging in to default location @@ -1831,17 +1834,23 @@ bool idle_startup()  			else  			{  				std::string msg; -				if (url_ok) -				{ -					msg = "AvatarMovedDesired"; -				} -				else if (gSavedSettings.getString("LoginLocation") == "home") -				{ -					msg = "AvatarMovedHome"; -				} -				else +				switch(start_slurl.getType())  				{ -					msg = "AvatarMovedLast"; +					case LLSLURL::LOCATION: +					{ +						 +						msg = "AvatarMovedDesired"; +						break; +					} +					case LLSLURL::HOME_LOCATION: +					{ +						msg = "AvatarMovedHome"; +						break; +					} +					default: +					{ +						msg = "AvatarMovedLast"; +					}  				}  				LLNotificationsUtil::add(msg);  			} @@ -2057,20 +2066,9 @@ void login_show()  #endif  	LLPanelLogin::show(	gViewerWindow->getWindowRectScaled(), -						bUseDebugLogin, +						bUseDebugLogin || gSavedSettings.getBOOL("SecondLifeEnterprise"),  						login_callback, NULL ); -	// UI textures have been previously loaded in doPreloadImages() -	 -	LL_DEBUGS("AppInit") << "Setting Servers" << LL_ENDL; - -	LLPanelLogin::addServer(LLViewerLogin::getInstance()->getGridLabel(), LLViewerLogin::getInstance()->getGridChoice()); - -	LLViewerLogin* vl = LLViewerLogin::getInstance(); -	for(int grid_index = GRID_INFO_ADITI; grid_index < GRID_INFO_OTHER; ++grid_index) -	{ -		LLPanelLogin::addServer(vl->getKnownGridLabel((EGridInfo)grid_index), grid_index); -	}  }  // Callback for when login screen is closed.  Option 0 = connect, option 1 = quit. @@ -2086,9 +2084,6 @@ void login_callback(S32 option, void *userdata)  	}  	else if (QUIT_OPTION == option) // *TODO: THIS CODE SEEMS TO BE UNREACHABLE!!!!! login_callback is never called with option equal to QUIT_OPTION  	{ -		// Make sure we don't save the password if the user is trying to clear it. -		std::string first, last, password; -		LLPanelLogin::getFields(&first, &last, &password);  		if (!gSavedSettings.getBOOL("RememberPassword"))  		{  			// turn off the setting and write out to disk @@ -2111,142 +2106,6 @@ void login_callback(S32 option, void *userdata)  	}  } - -// static -std::string LLStartUp::loadPasswordFromDisk() -{ -	// Only load password if we also intend to save it (otherwise the user -	// wonders what we're doing behind his back).  JC -	BOOL remember_password = gSavedSettings.getBOOL("RememberPassword"); -	if (!remember_password) -	{ -		return std::string(""); -	} - -	std::string hashed_password(""); - -	// Look for legacy "marker" password from settings.ini -	hashed_password = gSavedSettings.getString("Marker"); -	if (!hashed_password.empty()) -	{ -		// Stomp the Marker entry. -		gSavedSettings.setString("Marker", ""); - -		// Return that password. -		return hashed_password; -	} - -	std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, -													   "password.dat"); -	LLFILE* fp = LLFile::fopen(filepath, "rb");		/* Flawfinder: ignore */ -	if (!fp) -	{ -		return hashed_password; -	} - -	// UUID is 16 bytes, written into ASCII is 32 characters -	// without trailing \0 -	const S32 HASHED_LENGTH = 32; -	U8 buffer[HASHED_LENGTH+1]; - -	if (1 != fread(buffer, HASHED_LENGTH, 1, fp)) -	{ -		return hashed_password; -	} - -	fclose(fp); - -	// Decipher with MAC address -	LLXORCipher cipher(gMACAddress, 6); -	cipher.decrypt(buffer, HASHED_LENGTH); - -	buffer[HASHED_LENGTH] = '\0'; - -	// Check to see if the mac address generated a bad hashed -	// password. It should be a hex-string or else the mac adress has -	// changed. This is a security feature to make sure that if you -	// get someone's password.dat file, you cannot hack their account. -	if(is_hex_string(buffer, HASHED_LENGTH)) -	{ -		hashed_password.assign((char*)buffer); -	} - -	return hashed_password; -} - - -// static -void LLStartUp::savePasswordToDisk(const std::string& hashed_password) -{ -	std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, -													   "password.dat"); -	LLFILE* fp = LLFile::fopen(filepath, "wb");		/* Flawfinder: ignore */ -	if (!fp) -	{ -		return; -	} - -	// Encipher with MAC address -	const S32 HASHED_LENGTH = 32; -	U8 buffer[HASHED_LENGTH+1]; - -	LLStringUtil::copy((char*)buffer, hashed_password.c_str(), HASHED_LENGTH+1); - -	LLXORCipher cipher(gMACAddress, 6); -	cipher.encrypt(buffer, HASHED_LENGTH); - -	if (fwrite(buffer, HASHED_LENGTH, 1, fp) != 1) -	{ -		LL_WARNS("AppInit") << "Short write" << LL_ENDL; -	} - -	fclose(fp); -} - - -// static -void LLStartUp::deletePasswordFromDisk() -{ -	std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, -														  "password.dat"); -	LLFile::remove(filepath); -} - - -bool is_hex_string(U8* str, S32 len) -{ -	bool rv = true; -	U8* c = str; -	while(rv && len--) -	{ -		switch(*c) -		{ -		case '0': -		case '1': -		case '2': -		case '3': -		case '4': -		case '5': -		case '6': -		case '7': -		case '8': -		case '9': -		case 'a': -		case 'b': -		case 'c': -		case 'd': -		case 'e': -		case 'f': -			++c; -			break; -		default: -			rv = false; -			break; -		} -	} -	return rv; -} -  void show_first_run_dialog()  {  	LLNotificationsUtil::add("FirstRun", LLSD(), LLSD(), first_run_dialog_callback); @@ -2288,7 +2147,7 @@ bool login_alert_status(const LLSD& notification, const LLSD& response)        //      break;          case 2:     // Teleport              // Restart the login process, starting at our home locaton -            LLURLSimString::setString("home"); +	  LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_HOME));              LLStartUp::setStartupState( STATE_LOGIN_CLEANUP );              break;          default: @@ -2508,30 +2367,35 @@ void asset_callback_nothing(LLVFS*, const LLUUID&, LLAssetType::EType, void*, S3  const std::string COMMON_GESTURES_FOLDER = "Common Gestures";  const std::string MALE_GESTURES_FOLDER = "Male Gestures";  const std::string FEMALE_GESTURES_FOLDER = "Female Gestures"; -const std::string MALE_OUTFIT_FOLDER = "Male Shape & Outfit"; -const std::string FEMALE_OUTFIT_FOLDER = "Female Shape & Outfit";  const S32 OPT_CLOSED_WINDOW = -1;  const S32 OPT_MALE = 0;  const S32 OPT_FEMALE = 1; - +const S32 OPT_TRUST_CERT = 0; +const S32 OPT_CANCEL_TRUST = 1; +	  bool callback_choose_gender(const LLSD& notification, const LLSD& response) -{	 -	S32 option = LLNotificationsUtil::getSelectedOption(notification, response); +{ +	 +    // These defaults are returned from the server on login.  They are set in login.xml.                   +    // If no default is returned from the server, they are retrieved from settings.xml.                    +	 +	S32 option = LLNotification::getSelectedOption(notification, response);  	switch(option)  	{ -	case OPT_MALE: -		LLStartUp::loadInitialOutfit( MALE_OUTFIT_FOLDER, "male" ); -		break; - -	case OPT_FEMALE: -	case OPT_CLOSED_WINDOW: -	default: -		LLStartUp::loadInitialOutfit( FEMALE_OUTFIT_FOLDER, "female" ); -		break; +		case OPT_MALE: +			LLStartUp::loadInitialOutfit( gSavedSettings.getString("DefaultMaleAvatar"), "male" ); +			break; +			 +        case OPT_FEMALE: +        case OPT_CLOSED_WINDOW: +        default: +			LLStartUp::loadInitialOutfit( gSavedSettings.getString("DefaultFemaleAvatar"), "female" ); +			break;  	}  	return false;  } +  void LLStartUp::loadInitialOutfit( const std::string& outfit_folder_name,  								   const std::string& gender_name )  { @@ -2746,7 +2610,6 @@ void reset_login()  //--------------------------------------------------------------------------- -std::string LLStartUp::sSLURLCommand;  bool LLStartUp::canGoFullscreen()  { @@ -2779,41 +2642,145 @@ void LLStartUp::fontInit()  bool LLStartUp::dispatchURL()  {  	// ok, if we've gotten this far and have a startup URL -	if (!sSLURLCommand.empty()) +        if (!getStartSLURL().isValid())  	{ -		LLMediaCtrl* web = NULL; -		const bool trusted_browser = false; -		LLURLDispatcher::dispatch(sSLURLCommand, web, trusted_browser); +	  return false;  	} -	else if (LLURLSimString::parse()) -	{ +        if(getStartSLURL().getType() != LLSLURL::APP) +	  { +	      		// If we started with a location, but we're already  		// at that location, don't pop dialogs open.  		LLVector3 pos = gAgent.getPositionAgent(); -		F32 dx = pos.mV[VX] - (F32)LLURLSimString::sInstance.mX; -		F32 dy = pos.mV[VY] - (F32)LLURLSimString::sInstance.mY; +		LLVector3 slurlpos = getStartSLURL().getPosition(); +		F32 dx = pos.mV[VX] - slurlpos.mV[VX]; +		F32 dy = pos.mV[VY] - slurlpos.mV[VY];  		const F32 SLOP = 2.f;	// meters -		if( LLURLSimString::sInstance.mSimName != gAgent.getRegion()->getName() +		if( getStartSLURL().getRegion() != gAgent.getRegion()->getName()  			|| (dx*dx > SLOP*SLOP)  			|| (dy*dy > SLOP*SLOP) )  		{ -			std::string url = LLURLSimString::getURL(); -			LLMediaCtrl* web = NULL; -			const bool trusted_browser = false; -			LLURLDispatcher::dispatch(url, web, trusted_browser); +			LLURLDispatcher::dispatch(getStartSLURL().getSLURLString(),  +						  NULL, false);  		}  		return true;  	}  	return false;  } +void LLStartUp::setStartSLURL(const LLSLURL& slurl)  +{ +  sStartSLURL = slurl; +  switch(slurl.getType()) +    { +    case LLSLURL::HOME_LOCATION: +      { +		  gSavedSettings.setString("LoginLocation", LLSLURL::SIM_LOCATION_HOME); +	break; +      } +    case LLSLURL::LAST_LOCATION: +      { +	gSavedSettings.setString("LoginLocation", LLSLURL::SIM_LOCATION_LAST); +	break; +      } +    default: +			LLGridManager::getInstance()->setGridChoice(slurl.getGrid()); +			break; +    } +} +  bool login_alert_done(const LLSD& notification, const LLSD& response)  {  	LLPanelLogin::giveFocus();  	return false;  } +// parse the certificate information into args for the  +// certificate notifications +LLSD transform_cert_args(LLPointer<LLCertificate> cert) +{ +	LLSD args = LLSD::emptyMap(); +	std::string value; +	LLSD cert_info = cert->getLLSD(); +	// convert all of the elements in the cert into                                         +	// args for the xml dialog, so we have flexability to                                   +	// display various parts of the cert by only modifying                                  +	// the cert alert dialog xml.                                                           +	for(LLSD::map_iterator iter = cert_info.beginMap(); +		iter != cert_info.endMap(); +		iter++) +	{ +		// key usage and extended key usage                                             +		// are actually arrays, and we want to format them as comma separated           +		// strings, so special case those.                                              +		LLSDSerialize::toXML(cert_info[iter->first], std::cout); +		if((iter->first== std::string(CERT_KEY_USAGE)) | +		   (iter->first == std::string(CERT_EXTENDED_KEY_USAGE))) +		{ +			value = ""; +			LLSD usage = cert_info[iter->first]; +			for (LLSD::array_iterator usage_iter = usage.beginArray(); +				 usage_iter != usage.endArray(); +				 usage_iter++) +			{ +				 +				if(usage_iter != usage.beginArray()) +				{ +					value += ", "; +				} +				 +				value += (*usage_iter).asString(); +			} +			 +		} +		else +		{ +			value = iter->second.asString(); +		} +		 +		std::string name = iter->first; +		std::transform(name.begin(), name.end(), name.begin(), +					   (int(*)(int))toupper); +		args[name.c_str()] = value; +	} +	return args; +} + + +// when we handle a cert error, give focus back to the login panel +void general_cert_done(const LLSD& notification, const LLSD& response) +{ +	LLStartUp::setStartupState( STATE_LOGIN_SHOW );			 +	LLPanelLogin::giveFocus(); +} + +// check to see if the user wants to trust the cert. +// if they do, add it to the cert store and  +void trust_cert_done(const LLSD& notification, const LLSD& response) +{ +	S32 option = LLNotification::getSelectedOption(notification, response);	 +	switch(option) +	{ +		case OPT_TRUST_CERT: +		{ +			LLPointer<LLCertificate> cert = gSecAPIHandler->getCertificate(notification["payload"]["certificate"]); +			LLPointer<LLCertificateStore> store = gSecAPIHandler->getCertificateStore(gSavedSettings.getString("CertStore"));			 +			store->add(cert); +			store->save(); +			LLStartUp::setStartupState( STATE_LOGIN_CLEANUP );	 +			break; +		} +		case OPT_CANCEL_TRUST: +			reset_login(); +			gSavedSettings.setBOOL("AutoLogin", FALSE);			 +			LLStartUp::setStartupState( STATE_LOGIN_SHOW );				 +		default: +			LLPanelLogin::giveFocus(); +			break; +	} + +}  void apply_udp_blacklist(const std::string& csv)  { @@ -2861,33 +2828,45 @@ bool process_login_success_response()  	text = response["secure_session_id"].asString();  	if(!text.empty()) gAgent.mSecureSessionID.set(text); -	text = response["first_name"].asString(); -	if(!text.empty())  -	{ -		// Remove quotes from string.  Login.cgi sends these to force -		// names that look like numbers into strings. -		gFirstname.assign(text); -		LLStringUtil::replaceChar(gFirstname, '"', ' '); -		LLStringUtil::trim(gFirstname); -	} -	text = response["last_name"].asString(); -	if(!text.empty())  +	// if the response contains a display name, use that, +	// otherwise if the response contains a first and/or last name, +	// use those.  Otherwise use the credential identifier + +	gDisplayName = ""; +	if (response.has("display_name"))  	{ -		gLastname.assign(text); +		gDisplayName.assign(response["display_name"].asString()); +		if(!gDisplayName.empty()) +		{ +			// Remove quotes from string.  Login.cgi sends these to force +			// names that look like numbers into strings. +			LLStringUtil::replaceChar(gDisplayName, '"', ' '); +			LLStringUtil::trim(gDisplayName); +		}  	} -	gSavedSettings.setString("FirstName", gFirstname); -	gSavedSettings.setString("LastName", gLastname); - -	if (gSavedSettings.getBOOL("RememberPassword")) +	if(gDisplayName.empty())  	{ -		// Successful login means the password is valid, so save it. -		LLStartUp::savePasswordToDisk(gPassword); +		if(response.has("first_name")) +		{ +			gDisplayName.assign(response["first_name"].asString()); +			LLStringUtil::replaceChar(gDisplayName, '"', ' '); +			LLStringUtil::trim(gDisplayName); +		} +		if(response.has("last_name")) +		{ +			text.assign(response["last_name"].asString()); +			LLStringUtil::replaceChar(text, '"', ' '); +			LLStringUtil::trim(text); +			if(!gDisplayName.empty()) +			{ +				gDisplayName += " "; +			} +			gDisplayName += text; +		}  	} -	else +	if(gDisplayName.empty())  	{ -		// Don't leave password from previous session sitting around -		// during this login session. -		LLStartUp::deletePasswordFromDisk(); +		gDisplayName.assign(gUserCredential->asString());  	}  	// this is their actual ability to access content @@ -2981,7 +2960,7 @@ bool process_login_success_response()  		// replace the default help URL format  		gSavedSettings.setString("HelpURLFormat",text); -		// don't fall back to Nebraska's pre-connection static help +		// don't fall back to Standalone's pre-connection static help  		gSavedSettings.setBOOL("HelpUseLocal", false);  	} @@ -3043,7 +3022,44 @@ bool process_login_success_response()  		//setup map of datetime strings to codes and slt & local time offset from utc  		LLStringOps::setupDatetimeInfo(pacific_daylight_time);  	} - +	 +	static const char* CONFIG_OPTIONS[] = {"voice-config", "newuser-config"}; +	for (int i = 0; i < sizeof(CONFIG_OPTIONS)/sizeof(CONFIG_OPTIONS[0]); i++) +	{ +		LLSD options = response[CONFIG_OPTIONS[i]]; +		if (!options.isArray() && (options.size() < 1) && !options[0].isMap()) +		{ +			continue; +		} +		llinfos << "config option " << CONFIG_OPTIONS[i][0] << "response " << options << llendl; +		for(LLSD::map_iterator option_it = options[0].beginMap(); +			option_it != options[0].endMap(); +			option_it++) +		{ +			llinfos << "trying option " << option_it->first << llendl; +			LLPointer<LLControlVariable> control = gSavedSettings.getControl(option_it->first); +			if(control.notNull()) +			{ +				if(control->isType(TYPE_BOOLEAN)) +				{ +					llinfos << "Setting BOOL from login " << option_it->first << " " << option_it->second << llendl; +					 +					gSavedSettings.setBOOL(option_it->first, !((option_it->second == "F") || +															   (option_it->second == "false") || +															   (!option_it->second))); +				} +				else if (control->isType(TYPE_STRING)) +				{ +					llinfos << "Setting String from login " << option_it->first << " " << option_it->second << llendl; +					gSavedSettings.setString(option_it->first, option_it->second); +				} +				// we don't support other types now                                                                                                             +				 +			} +			 +		} +	} +	  	LLSD initial_outfit = response["initial-outfit"][0];  	if(initial_outfit.size())  	{ @@ -3097,7 +3113,7 @@ bool process_login_success_response()  	bool success = false;  	// JC: gesture loading done below, when we have an asset system -	// in place.  Don't delete/clear user_credentials until then. +	// in place.  Don't delete/clear gUserCredentials until then.  	if(gAgentID.notNull()  	   && gAgentSessionID.notNull()  	   && gMessageSystem->mOurCircuitCode diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h index 92fe9521d3..16cc74504f 100644 --- a/indra/newview/llstartup.h +++ b/indra/newview/llstartup.h @@ -38,6 +38,7 @@  class LLViewerTexture ;  class LLEventPump;  class LLStartupListener; +class LLSLURL;  // functions  bool idle_startup(); @@ -101,26 +102,18 @@ public:  	static void loadInitialOutfit( const std::string& outfit_folder_name,  								   const std::string& gender_name ); -	// Load MD5 of user's password from local disk file. -	static std::string loadPasswordFromDisk(); -	 -	// Record MD5 of user's password for subsequent login. -	static void savePasswordToDisk(const std::string& hashed_password); -	 -	// Delete the saved password local disk file. -	static void deletePasswordFromDisk();  	static bool dispatchURL();  		// if we have a SLURL or sim string ("Ahern/123/45") that started  		// the viewer, dispatch it -	static std::string sSLURLCommand; -		// *HACK: On startup, if we were passed a secondlife://app/do/foo -		// command URL, store it for later processing. -  	static void postStartupState(); +	static void setStartSLURL(const LLSLURL& slurl);  +	static LLSLURL& getStartSLURL() { return sStartSLURL; }   private: +	static LLSLURL sStartSLURL; +  	static std::string startupStateToString(EStartupState state);  	static EStartupState gStartupState; // Do not set directly, use LLStartup::setStartupState  	static boost::scoped_ptr<LLEventPump> sStateWatcher; diff --git a/indra/newview/llstylemap.cpp b/indra/newview/llstylemap.cpp index 61705c4eb3..8fab3bb361 100644 --- a/indra/newview/llstylemap.cpp +++ b/indra/newview/llstylemap.cpp @@ -51,7 +51,7 @@ const LLStyle::Params &LLStyleMap::lookupAgent(const LLUUID &source)  			style_params.color.control = "HTMLLinkColor";  			style_params.readonly_color.control = "HTMLLinkColor";  			style_params.link_href =  -					LLSLURL::buildCommand("agent", source, "inspect"); +					LLSLURL("agent", source, "inspect").getSLURLString();  		}  		else  		{ diff --git a/indra/newview/lltoastalertpanel.cpp b/indra/newview/lltoastalertpanel.cpp index c3ccb9380b..986ccdf19b 100644 --- a/indra/newview/lltoastalertpanel.cpp +++ b/indra/newview/lltoastalertpanel.cpp @@ -281,9 +281,6 @@ LLToastAlertPanel::LLToastAlertPanel( LLNotificationPtr notification, bool modal  			mLineEditor->setText(edit_text_contents);  			mLineEditor->setMaxTextLength(STD_STRING_STR_LEN - 1); -			// make sure all edit keys get handled properly (DEV-22396) -			mLineEditor->setHandleEditKeysDirectly(TRUE); -  			LLToastPanel::addChild(mLineEditor);  			mLineEditor->setDrawAsterixes(is_password); diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index 394f550f2e..f37efd778f 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -412,9 +412,12 @@ void LLToolDragAndDrop::setDragStart(S32 x, S32 y)  BOOL LLToolDragAndDrop::isOverThreshold(S32 x,S32 y)  { -	const S32 MIN_MANHATTAN_DIST = 3; -	S32 manhattan_dist = llabs( x - mDragStartX ) + llabs( y - mDragStartY ); -	return manhattan_dist >= MIN_MANHATTAN_DIST; +	static LLCachedControl<S32> drag_and_drop_threshold(gSavedSettings,"DragAndDropDistanceThreshold"); +	 +	S32 mouse_delta_x = x - mDragStartX; +	S32 mouse_delta_y = y - mDragStartY; +	 +	return (mouse_delta_x * mouse_delta_x) + (mouse_delta_y * mouse_delta_y) > drag_and_drop_threshold * drag_and_drop_threshold;  }  void LLToolDragAndDrop::beginDrag(EDragAndDropType type, diff --git a/indra/newview/llurl.cpp b/indra/newview/llurl.cpp index ab65ead4c5..83a5839a93 100644 --- a/indra/newview/llurl.cpp +++ b/indra/newview/llurl.cpp @@ -286,5 +286,11 @@ const char * LLURL::getFullPath()  	return(sReturnString);  } +const char * LLURL::getAuthority() +{ +	strncpy(LLURL::sReturnString,mAuthority, LL_MAX_PATH -1);               /* Flawfinder: ignore */ +	LLURL::sReturnString[LL_MAX_PATH -1] = '\0'; +	return(sReturnString); +}  char LLURL::sReturnString[LL_MAX_PATH] = ""; diff --git a/indra/newview/llurl.h b/indra/newview/llurl.h index 9a089dd835..e41b83d29f 100644 --- a/indra/newview/llurl.h +++ b/indra/newview/llurl.h @@ -79,6 +79,7 @@ public:  	virtual const char *getFQURL() const;  	virtual const char *getFullPath(); +	virtual const char *getAuthority();  	virtual const char *updateRelativePath(const LLURL &url); diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp index b88069cd48..a31c3a0f1b 100644 --- a/indra/newview/llurldispatcher.cpp +++ b/indra/newview/llurldispatcher.cpp @@ -4,7 +4,7 @@   *   * $LicenseInfo:firstyear=2007&license=viewergpl$   *  - * Copyright (c) 2007-2009, Linden Research, Inc. + * Copyright (c) 2010, Linden Research, Inc.   *    * Second Life Viewer Source Code   * The source code in this file ("Source Code") is provided by Linden Lab @@ -12,13 +12,12 @@   * ("GPL"), unless you have obtained a separate licensing agreement   * ("Other License"), formally executed by you and Linden Lab.  Terms of   * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2   *    * There are special exceptions to the terms and conditions of the GPL as   * it is applied to this Source Code. View the full text of the exception   * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * online at http://secondlife.com/developers/opensource/flossexception   *    * By copying, modifying or distributing this software, you acknowledge   * that you have read and understood your obligations described above, @@ -45,10 +44,10 @@  #include "llsidetray.h"  #include "llslurl.h"  #include "llstartup.h"			// gStartupState -#include "llurlsimstring.h"  #include "llweb.h"  #include "llworldmapmessage.h"  #include "llurldispatcherlistener.h" +#include "llviewernetwork.h"  // library includes  #include "llnotificationsutil.h" @@ -59,25 +58,25 @@ static LLURLDispatcherListener sURLDispatcherListener;  class LLURLDispatcherImpl  {  public: -	static bool dispatch(const std::string& url, +	static bool dispatch(const LLSLURL& slurl,  						 LLMediaCtrl* web,  						 bool trusted_browser);  		// returns true if handled or explicitly blocked. -	static bool dispatchRightClick(const std::string& url); +	static bool dispatchRightClick(const LLSLURL& slurl);  private: -	static bool dispatchCore(const std::string& url,  +	static bool dispatchCore(const LLSLURL& slurl,   							 bool right_mouse,  							 LLMediaCtrl* web,  							 bool trusted_browser);  		// handles both left and right click -	static bool dispatchHelp(const std::string& url, bool right_mouse); +	static bool dispatchHelp(const LLSLURL& slurl, bool right_mouse);  		// Handles sl://app.floater.html.help by showing Help floater.  		// Returns true if handled. -	static bool dispatchApp(const std::string& url, +	static bool dispatchApp(const LLSLURL& slurl,  							bool right_mouse,  							LLMediaCtrl* web,  							bool trusted_browser); @@ -85,16 +84,16 @@ private:  		// by showing panel in Search floater.  		// Returns true if handled or explicitly blocked. -	static bool dispatchRegion(const std::string& url, bool right_mouse); +	static bool dispatchRegion(const LLSLURL& slurl, bool right_mouse);  		// handles secondlife://Ahern/123/45/67/  		// Returns true if handled. -	static void regionHandleCallback(U64 handle, const std::string& url, +	static void regionHandleCallback(U64 handle, const LLSLURL& slurl,  		const LLUUID& snapshot_id, bool teleport);  		// Called by LLWorldMap when a location has been resolved to a  	    // region name -	static void regionNameCallback(U64 handle, const std::string& url, +	static void regionNameCallback(U64 handle, const LLSLURL& slurl,  		const LLUUID& snapshot_id, bool teleport);  		// Called by LLWorldMap when a region name has been resolved to a  		// location in-world, used by places-panel display. @@ -103,65 +102,57 @@ private:  };  // static -bool LLURLDispatcherImpl::dispatchCore(const std::string& url, +bool LLURLDispatcherImpl::dispatchCore(const LLSLURL& slurl,  									   bool right_mouse,  									   LLMediaCtrl* web,  									   bool trusted_browser)  { -	if (url.empty()) return false; -	//if (dispatchHelp(url, right_mouse)) return true; -	if (dispatchApp(url, right_mouse, web, trusted_browser)) return true; -	if (dispatchRegion(url, right_mouse)) return true; +	//if (dispatchHelp(slurl, right_mouse)) return true; +	switch(slurl.getType()) +	{ +		case LLSLURL::APP:  +			return dispatchApp(slurl, right_mouse, web, trusted_browser); +		case LLSLURL::LOCATION: +			return dispatchRegion(slurl, right_mouse); +		default: +			return false; +	}  	/*  	// Inform the user we can't handle this  	std::map<std::string, std::string> args; -	args["SLURL"] = url; +	args["SLURL"] = slurl;  	r;  	*/ -	 -	return false;  }  // static -bool LLURLDispatcherImpl::dispatch(const std::string& url, +bool LLURLDispatcherImpl::dispatch(const LLSLURL& slurl,  								   LLMediaCtrl* web,  								   bool trusted_browser)  { -	llinfos << "url: " << url << llendl;  	const bool right_click = false; -	return dispatchCore(url, right_click, web, trusted_browser); +	return dispatchCore(slurl, right_click, web, trusted_browser);  }  // static -bool LLURLDispatcherImpl::dispatchRightClick(const std::string& url) +bool LLURLDispatcherImpl::dispatchRightClick(const LLSLURL& slurl)  { -	llinfos << "url: " << url << llendl;  	const bool right_click = true;  	LLMediaCtrl* web = NULL;  	const bool trusted_browser = false; -	return dispatchCore(url, right_click, web, trusted_browser); +	return dispatchCore(slurl, right_click, web, trusted_browser);  }  // static -bool LLURLDispatcherImpl::dispatchApp(const std::string& url,  +bool LLURLDispatcherImpl::dispatchApp(const LLSLURL& slurl,   									  bool right_mouse,  									  LLMediaCtrl* web,  									  bool trusted_browser)  { -	// ensure the URL is in the secondlife:///app/ format -	if (!LLSLURL::isSLURLCommand(url)) -	{ -		return false; -	} - -	LLURI uri(url); -	LLSD pathArray = uri.pathArray(); -	pathArray.erase(0); // erase "app" -	std::string cmd = pathArray.get(0); -	pathArray.erase(0); // erase "cmd" +	llinfos << "cmd: " << slurl.getAppCmd() << " path: " << slurl.getAppPath() << " query: " << slurl.getAppQuery() << llendl;  	bool handled = LLCommandDispatcher::dispatch( -			cmd, pathArray, uri.queryMap(), web, trusted_browser); +			slurl.getAppCmd(), slurl.getAppPath(), slurl.getAppQuery(), web, trusted_browser);  	// alert if we didn't handle this secondlife:///app/ SLURL  	// (but still return true because it is a valid app SLURL) @@ -173,81 +164,72 @@ bool LLURLDispatcherImpl::dispatchApp(const std::string& url,  }  // static -bool LLURLDispatcherImpl::dispatchRegion(const std::string& url, bool right_mouse) +bool LLURLDispatcherImpl::dispatchRegion(const LLSLURL& slurl, bool right_mouse)  { -	if (!LLSLURL::isSLURL(url)) -	{ -		return false; -	} - -	std::string sim_string = LLSLURL::stripProtocol(url); -	std::string region_name; -	S32 x = 128; -	S32 y = 128; -	S32 z = 0; -	if (! LLURLSimString::parse(sim_string, ®ion_name, &x, &y, &z)) -	{ -		return false; -	} - +  if(slurl.getType() != LLSLURL::LOCATION) +    { +      return false; +    }  	// Before we're logged in, need to update the startup screen  	// to tell the user where they are going.  	if (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP)  	{ -		// Parse it and stash in globals, it will be dispatched in -		// STATE_CLEANUP. -		LLURLSimString::setString(url);  		// We're at the login screen, so make sure user can see  		// the login location box to know where they are going. -		LLPanelLogin::refreshLocation( true ); +		LLPanelLogin::setLocation(slurl);  		return true;  	}  	// LLFloaterURLDisplay functionality moved to LLPanelPlaces in Side Tray. -	//LLFloaterURLDisplay* url_displayp = LLFloaterReg::getTypedInstance<LLFloaterURLDisplay>("preview_url",LLSD()); -	//if(url_displayp) url_displayp->setName(region_name); +	//LLFloaterURLDisplay* slurl_displayp = LLFloaterReg::getTypedInstance<LLFloaterURLDisplay>("preview_url",LLSD()); +	//if(slurl_displayp) slurl_displayp->setName(region_name);  	// Request a region handle by name -	LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name, -								 LLURLDispatcherImpl::regionNameCallback, -								 url, -								 false);	// don't teleport +	LLWorldMapMessage::getInstance()->sendNamedRegionRequest(slurl.getRegion(), +									  LLURLDispatcherImpl::regionNameCallback, +									  slurl.getSLURLString(), +									  false);	// don't teleport  	return true;  }  /*static*/ -void LLURLDispatcherImpl::regionNameCallback(U64 region_handle, const std::string& url, const LLUUID& snapshot_id, bool teleport) +void LLURLDispatcherImpl::regionNameCallback(U64 region_handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport)  { -	std::string sim_string = LLSLURL::stripProtocol(url); -	std::string region_name; -	S32 x = 128; -	S32 y = 128; -	S32 z = 0; - -	if (LLURLSimString::parse(sim_string, ®ion_name, &x, &y, &z)) -	{ -		regionHandleCallback(region_handle, url, snapshot_id, teleport); -	} +       +  if(slurl.getType() == LLSLURL::LOCATION) +    {         +      regionHandleCallback(region_handle, slurl, snapshot_id, teleport); +    }  }  /* static */ -void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const std::string& url, const LLUUID& snapshot_id, bool teleport) +void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport)  { -	std::string sim_string = LLSLURL::stripProtocol(url); -	std::string region_name; -	S32 x = 128; -	S32 y = 128; -	S32 z = 0; -	LLURLSimString::parse(sim_string, ®ion_name, &x, &y, &z); - -	LLVector3 local_pos; -	local_pos.mV[VX] = (F32)x; -	local_pos.mV[VY] = (F32)y; -	local_pos.mV[VZ] = (F32)z; +  // we can't teleport cross grid at this point +	if((!LLGridManager::getInstance()->isSystemGrid(slurl.getGrid()) || !LLGridManager::getInstance()->isSystemGrid()) && +	   (slurl.getGrid() != LLGridManager::getInstance()->getGrid())) +	{ +		LLSD args; +		args["SLURL"] = slurl.getLocationString(); +		args["CURRENT_GRID"] = LLGridManager::getInstance()->getGridLabel(); +		LLSD grid_info = LLGridManager::getInstance()->getGridInfo(slurl.getGrid()); +		 +		if(grid_info.has(GRID_LABEL_VALUE)) +		{ +			args["GRID"] = grid_info[GRID_LABEL_VALUE].asString(); +		} +		else  +		{ +			args["GRID"] = slurl.getGrid(); +		} +		LLNotificationsUtil::add("CantTeleportToGrid", args); +		return; +	} +	  	LLVector3d global_pos = from_region_handle(region_handle); -	global_pos += LLVector3d(local_pos); +	global_pos += LLVector3d(slurl.getPosition());  	if (teleport)  	{	 @@ -271,8 +253,8 @@ void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const std::str  		// LLFloaterURLDisplay functionality moved to LLPanelPlaces in Side Tray.  //		// display informational floater, allow user to click teleport btn -//		LLFloaterURLDisplay* url_displayp = LLFloaterReg::getTypedInstance<LLFloaterURLDisplay>("preview_url",LLSD()); -//		if(url_displayp) +//		LLFloaterURLDisplay* slurl_displayp = LLFloaterReg::getTypedInstance<LLFloaterURLDisplay>("preview_url",LLSD()); +//		if(slurl_displayp)  //		{  //			url_displayp->displayParcelInfo(region_handle, local_pos);  //			if(snapshot_id.notNull()) @@ -287,7 +269,7 @@ void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const std::str  //---------------------------------------------------------------------------  // Teleportation links are handled here because they are tightly coupled -// to URL parsing and sim-fragment parsing +// to SLURL parsing and sim-fragment parsing  class LLTeleportHandler : public LLCommandHandler  {  public: @@ -303,18 +285,21 @@ public:  		// a global position, and teleport to it  		if (tokens.size() < 1) return false; -		// Region names may be %20 escaped. -		std::string region_name = LLURLSimString::unescapeRegionName(tokens[0]); - -		// build secondlife://De%20Haro/123/45/67 for use in callback -		std::string url = LLSLURL::PREFIX_SECONDLIFE; -		for (int i = 0; i < tokens.size(); ++i) +		LLVector3 coords(128, 128, 0); +		if (tokens.size() <= 4)  		{ -			url += tokens[i].asString() + "/"; +			coords = LLVector3(tokens[1].asReal(),  +							   tokens[2].asReal(),  +							   tokens[3].asReal());  		} +		 +		// Region names may be %20 escaped. +		 +		std::string region_name = LLURI::unescape(tokens[0]); +  		LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name,  			LLURLDispatcherImpl::regionHandleCallback, -			url, +			LLSLURL(region_name, coords).getSLURLString(),  			true);	// teleport  		return true;  	} @@ -324,21 +309,21 @@ LLTeleportHandler gTeleportHandler;  //---------------------------------------------------------------------------  // static -bool LLURLDispatcher::dispatch(const std::string& url, +bool LLURLDispatcher::dispatch(const std::string& slurl,  							   LLMediaCtrl* web,  							   bool trusted_browser)  { -	return LLURLDispatcherImpl::dispatch(url, web, trusted_browser); +	return LLURLDispatcherImpl::dispatch(LLSLURL(slurl), web, trusted_browser);  }  // static -bool LLURLDispatcher::dispatchRightClick(const std::string& url) +bool LLURLDispatcher::dispatchRightClick(const std::string& slurl)  { -	return LLURLDispatcherImpl::dispatchRightClick(url); +	return LLURLDispatcherImpl::dispatchRightClick(LLSLURL(slurl));  }  // static -bool LLURLDispatcher::dispatchFromTextEditor(const std::string& url) +bool LLURLDispatcher::dispatchFromTextEditor(const std::string& slurl)  {  	// *NOTE: Text editors are considered sources of trusted URLs  	// in order to make avatar profile links in chat history work. @@ -348,5 +333,7 @@ bool LLURLDispatcher::dispatchFromTextEditor(const std::string& url)  	// *TODO: Make this trust model more refined.  JC  	const bool trusted_browser = true;  	LLMediaCtrl* web = NULL; -	return LLURLDispatcherImpl::dispatch(url, web, trusted_browser); +	return LLURLDispatcherImpl::dispatch(LLSLURL(slurl), web, trusted_browser);  } + + diff --git a/indra/newview/llurldispatcher.h b/indra/newview/llurldispatcher.h index ff8a351253..407e417e58 100644 --- a/indra/newview/llurldispatcher.h +++ b/indra/newview/llurldispatcher.h @@ -2,9 +2,9 @@   * @file llurldispatcher.h   * @brief Central registry for all SL URL handlers   * - * $LicenseInfo:firstyear=2007&license=viewergpl$ + * $LicenseInfo:firstyear=2010&license=viewergpl$   *  - * Copyright (c) 2007-2009, Linden Research, Inc. + * Copyright (c) 2007-2010, Linden Research, Inc.   *    * Second Life Viewer Source Code   * The source code in this file ("Source Code") is provided by Linden Lab @@ -12,13 +12,12 @@   * ("GPL"), unless you have obtained a separate licensing agreement   * ("Other License"), formally executed by you and Linden Lab.  Terms of   * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2   *    * There are special exceptions to the terms and conditions of the GPL as   * it is applied to this Source Code. View the full text of the exception   * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * online at http://secondlife.com/developers/opensource/flossexception   *    * By copying, modifying or distributing this software, you acknowledge   * that you have read and understood your obligations described above, @@ -31,16 +30,16 @@   */  #ifndef LLURLDISPATCHER_H  #define LLURLDISPATCHER_H -  class LLMediaCtrl;  class LLURLDispatcher  {  public: -	static bool dispatch(const std::string& url, +	 +	static bool dispatch(const std::string& slurl,  						 LLMediaCtrl* web, -						 bool trusted_browser); +						 bool trusted_browser);	  		// At startup time and on clicks in internal web browsers,  		// teleport, open map, or run requested command.  		// @param url @@ -54,9 +53,9 @@ public:  		//   that navigates to trusted (Linden Lab) pages.  		// Returns true if someone handled the URL. -	static bool dispatchRightClick(const std::string& url); +	static bool dispatchRightClick(const std::string& slurl); -	static bool dispatchFromTextEditor(const std::string& url); +	static bool dispatchFromTextEditor(const std::string& slurl);  };  #endif diff --git a/indra/newview/llurllineeditorctrl.cpp b/indra/newview/llurllineeditorctrl.cpp index 258c3ddd75..8488527185 100644 --- a/indra/newview/llurllineeditorctrl.cpp +++ b/indra/newview/llurllineeditorctrl.cpp @@ -72,7 +72,7 @@ void LLURLLineEditor::cut()  		if( need_to_rollback )  		{  			rollback.doRollback( this ); -			reportBadKeystroke(); +			LLUI::reportBadKeystroke();  		}  		else  		if( mKeystrokeCallback ) @@ -89,15 +89,10 @@ void LLURLLineEditor::copyEscapedURLToClipboard()  	const std::string unescaped_text = wstring_to_utf8str(mText.getWString().substr(left_pos, length));  	LLWString text_to_copy; -	if (LLSLURL::isSLURL(unescaped_text)) +	if (LLSLURL(unescaped_text).isValid())  		text_to_copy = utf8str_to_wstring(LLWeb::escapeURL(unescaped_text));  	else  		text_to_copy = utf8str_to_wstring(unescaped_text);  	gClipboard.copyFromString( text_to_copy );  } -// Makes UISndBadKeystroke sound -void LLURLLineEditor::reportBadKeystroke() -{ -	make_ui_sound("UISndBadKeystroke"); -} diff --git a/indra/newview/llurllineeditorctrl.h b/indra/newview/llurllineeditorctrl.h index 618f29dfbf..ebe417e855 100644 --- a/indra/newview/llurllineeditorctrl.h +++ b/indra/newview/llurllineeditorctrl.h @@ -55,8 +55,6 @@ protected:  private:  	// util function to escape selected text and copy it to clipboard  	void 			copyEscapedURLToClipboard(); -	// send a beep signal if keystroke is bad. As it is private at LLLineEditor we need own function -	void			reportBadKeystroke();  	// Helper class to do rollback if needed  	class LLURLLineEditorRollback diff --git a/indra/newview/llvieweraudio.cpp b/indra/newview/llvieweraudio.cpp index 2661c9f32b..ef6f4194e0 100644 --- a/indra/newview/llvieweraudio.cpp +++ b/indra/newview/llvieweraudio.cpp @@ -157,21 +157,21 @@ void audio_update_volume(bool force_update)  	LLViewerMedia::setVolume( media_muted ? 0.0f : media_volume );  	// Voice -	if (gVoiceClient) +	if (LLVoiceClient::getInstance())  	{  		F32 voice_volume = gSavedSettings.getF32("AudioLevelVoice");  		voice_volume = mute_volume * master_volume * voice_volume;  		BOOL voice_mute = gSavedSettings.getBOOL("MuteVoice"); -		gVoiceClient->setVoiceVolume(voice_mute ? 0.f : voice_volume); -		gVoiceClient->setMicGain(voice_mute ? 0.f : gSavedSettings.getF32("AudioLevelMic")); +		LLVoiceClient::getInstance()->setVoiceVolume(voice_mute ? 0.f : voice_volume); +		LLVoiceClient::getInstance()->setMicGain(voice_mute ? 0.f : gSavedSettings.getF32("AudioLevelMic"));  		if (!gViewerWindow->getActive() && (gSavedSettings.getBOOL("MuteWhenMinimized")))  		{ -			gVoiceClient->setMuteMic(true); +			LLVoiceClient::getInstance()->setMuteMic(true);  		}  		else  		{ -			gVoiceClient->setMuteMic(false); +			LLVoiceClient::getInstance()->setMuteMic(false);  		}  	}  } diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 23349ab916..ef6379948a 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -74,6 +74,7 @@  #include "llnavigationbar.h"  #include "llfloatertools.h"  #include "llpaneloutfitsinventory.h" +#include "llpanellogin.h"  #ifdef TOGGLE_HACKED_GODLIKE_VIEWER  BOOL 				gHackGodmode = FALSE; @@ -412,10 +413,7 @@ bool handleHighResSnapshotChanged(const LLSD& newvalue)  bool handleVoiceClientPrefsChanged(const LLSD& newvalue)  { -	if(gVoiceClient) -	{ -		gVoiceClient->updateSettings(); -	} +	LLVoiceClient::getInstance()->updateSettings();  	return true;  } @@ -443,6 +441,12 @@ bool handleVelocityInterpolate(const LLSD& newvalue)  	return true;  } +bool handleForceShowGrid(const LLSD& newvalue) +{ +	LLPanelLogin::updateServer( ); +	return true; +} +  bool toggle_agent_pause(const LLSD& newvalue)  {  	if ( newvalue.asBoolean() ) @@ -648,6 +652,7 @@ void settings_setup_listeners()  	gSavedSettings.getControl("ShowNavbarFavoritesPanel")->getSignal()->connect(boost::bind(&toggle_show_favorites_panel, _2));  	gSavedSettings.getControl("ShowDebugAppearanceEditor")->getSignal()->connect(boost::bind(&toggle_show_appearance_editor, _2));  	gSavedSettings.getControl("ShowObjectRenderingCost")->getSignal()->connect(boost::bind(&toggle_show_object_render_cost, _2)); +	gSavedSettings.getControl("ForceShowGrid")->getSignal()->connect(boost::bind(&handleForceShowGrid, _2));  }  #if TEST_CACHED_CONTROL diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index b42d25c1d8..17221219eb 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -36,7 +36,6 @@  #include "llnotificationsutil.h"  #include "llsdserialize.h"  #include "message.h" -#include "indra_constants.h"  #include "llagent.h"  #include "llagentcamera.h" @@ -264,10 +263,14 @@ void LLViewerInventoryItem::fetchFromServer(void) const  		// we have to check region. It can be null after region was destroyed. See EXT-245  		if (region)  		{ -			if( ALEXANDRIA_LINDEN_ID.getString() == mPermissions.getOwner().getString()) -				url = region->getCapability("FetchLib"); -			else	 -				url = region->getCapability("FetchInventory"); +		  if(gAgent.getID() != mPermissions.getOwner()) +		    { +		      url = region->getCapability("FetchLib"); +		    } +		  else +		    {	 +		      url = region->getCapability("FetchInventory"); +		    }  		}  		else  		{ diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index d9fabc7d64..e0cbddafae 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -1473,11 +1473,6 @@ LLViewerMediaImpl::LLViewerMediaImpl(	  const LLUUID& texture_id,  //////////////////////////////////////////////////////////////////////////////////////////  LLViewerMediaImpl::~LLViewerMediaImpl()  { -	if( gEditMenuHandler == this ) -	{ -		gEditMenuHandler = NULL; -	} -	  	destroyMediaSource();  	LLViewerMediaTexture::removeMediaImplFromTexture(mTextureId) ; @@ -1537,7 +1532,10 @@ void LLViewerMediaImpl::createMediaSource()  	}  	else if(! mMimeType.empty())  	{ -		initializeMedia(mMimeType); +		if (!initializeMedia(mMimeType)) +		{ +			LL_WARNS("Media") << "Failed to initialize media for mime type " << mMimeType << LL_ENDL; +		}  	}  } diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 7bdec010ee..e8d064a59f 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -136,6 +136,7 @@ extern BOOL gDebugWindowProc;  LLMenuBarGL		*gMenuBarView = NULL;  LLViewerMenuHolderGL	*gMenuHolder = NULL;  LLMenuGL		*gPopupMenuView = NULL; +LLMenuGL		*gEditMenu = NULL;  LLMenuBarGL		*gLoginMenuBarView = NULL;  // Pie menus @@ -383,8 +384,10 @@ void init_menus()  	///  	/// Context menus  	/// +  	const widget_registry_t& registry =  		LLViewerMenuHolderGL::child_registry_t::instance(); +	gEditMenu = LLUICtrlFactory::createFromFile<LLMenuGL>("menu_edit.xml", gMenuHolder, registry);  	gMenuAvatarSelf = LLUICtrlFactory::createFromFile<LLContextMenu>(  		"menu_avatar_self.xml", gMenuHolder, registry);  	gMenuAvatarOther = LLUICtrlFactory::createFromFile<LLContextMenu>( @@ -426,7 +429,7 @@ void init_menus()  	gPopupMenuView->setBackgroundColor( color );  	// If we are not in production, use a different color to make it apparent. -	if (LLViewerLogin::getInstance()->isInProductionGrid()) +	if (LLGridManager::getInstance()->isInProductionGrid())  	{  		color = LLUIColorTable::instance().getColor( "MenuBarBgColor" );  	} @@ -442,7 +445,7 @@ void init_menus()  	menu_bar_holder->addChild(gMenuBarView);      gViewerWindow->setMenuBackgroundColor(false,  -        LLViewerLogin::getInstance()->isInProductionGrid()); +        LLGridManager::getInstance()->isInProductionGrid());  	// Assume L$10 for now, the server will tell us the real cost at login  	// *TODO:Also fix cost in llfolderview.cpp for Inventory menus @@ -1802,9 +1805,10 @@ class LLAdvancedDebugAvatarTextures : public view_listener_t  {  	bool handleEvent(const LLSD& userdata)  	{ -#ifndef LL_RELEASE_FOR_DOWNLOAD -		handle_debug_avatar_textures(NULL); -#endif +		if (gAgent.isGodlike()) +		{ +			handle_debug_avatar_textures(NULL); +		}  		return true;  	}  }; @@ -3463,7 +3467,7 @@ void set_god_level(U8 god_level)          if(gViewerWindow)          {              gViewerWindow->setMenuBackgroundColor(god_level > GOD_NOT, -            LLViewerLogin::getInstance()->isInProductionGrid()); +            LLGridManager::getInstance()->isInProductionGrid());          }          LLSD args; @@ -3503,7 +3507,7 @@ BOOL check_toggle_hacked_godmode(void*)  bool enable_toggle_hacked_godmode(void*)  { -  return !LLViewerLogin::getInstance()->isInProductionGrid(); +  return !LLGridManager::getInstance()->isInProductionGrid();  }  #endif @@ -4374,7 +4378,7 @@ BOOL enable_take()  		return TRUE;  #else  # ifdef TOGGLE_HACKED_GODLIKE_VIEWER -		if (!LLViewerLogin::getInstance()->isInProductionGrid()  +		if (!LLGridManager::getInstance()->isInProductionGrid()               && gAgent.isGodlike())  		{  			return TRUE; @@ -4987,7 +4991,7 @@ bool enable_object_delete()  	TRUE;  #else  # ifdef TOGGLE_HACKED_GODLIKE_VIEWER -	(!LLViewerLogin::getInstance()->isInProductionGrid() +	(!LLGridManager::getInstance()->isInProductionGrid()       && gAgent.isGodlike()) ||  # endif  	LLSelectMgr::getInstance()->canDoDelete(); @@ -5182,10 +5186,6 @@ void toggle_debug_menus(void*)  {  	BOOL visible = ! gSavedSettings.getBOOL("UseDebugMenus");  	gSavedSettings.setBOOL("UseDebugMenus", visible); -	if(visible) -	{ -		//LLFirstUse::useDebugMenus(); -	}  	show_debug_menus();  } @@ -6637,7 +6637,7 @@ bool enable_object_take_copy()  		all_valid = true;  #ifndef HACKED_GODLIKE_VIEWER  # ifdef TOGGLE_HACKED_GODLIKE_VIEWER -		if (LLViewerLogin::getInstance()->isInProductionGrid() +		if (LLGridManager::getInstance()->isInProductionGrid()              || !gAgent.isGodlike())  # endif  		{ @@ -6699,7 +6699,7 @@ BOOL enable_save_into_inventory(void*)  	return TRUE;  #else  # ifdef TOGGLE_HACKED_GODLIKE_VIEWER -	if (!LLViewerLogin::getInstance()->isInProductionGrid() +	if (!LLGridManager::getInstance()->isInProductionGrid()          && gAgent.isGodlike())  	{  		return TRUE; diff --git a/indra/newview/llviewermenu.h b/indra/newview/llviewermenu.h index d3c34f0de4..d72ea00077 100644 --- a/indra/newview/llviewermenu.h +++ b/indra/newview/llviewermenu.h @@ -157,6 +157,7 @@ extern const std::string SAVE_INTO_INVENTORY;  extern LLMenuBarGL*		gMenuBarView;  //extern LLView*			gMenuBarHolder; +extern LLMenuGL*		gEditMenu;  extern LLMenuGL*		gPopupMenuView;  extern LLViewerMenuHolderGL*	gMenuHolder;  extern LLMenuBarGL*		gLoginMenuBarView; diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 1426c0b9e2..7346b2a76e 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -1573,9 +1573,9 @@ void inventory_offer_handler(LLOfferInfo* info)  	payload["give_inventory_notification"] = FALSE;  	args["OBJECTFROMNAME"] = info->mFromName;  	args["NAME"] = info->mFromName; -	args["NAME_SLURL"] = LLSLURL::buildCommand("agent", info->mFromID, "about"); +	args["NAME_SLURL"] = LLSLURL("agent", info->mFromID, "about").getSLURLString();  	std::string verb = "select?name=" + LLURI::escape(msg); -	args["ITEM_SLURL"] = LLSLURL::buildCommand("inventory", info->mObjectID, verb.c_str()); +	args["ITEM_SLURL"] = LLSLURL("inventory", info->mObjectID, verb.c_str()).getSLURLString();  	LLNotification::Params p("ObjectGiveItem"); @@ -2244,10 +2244,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)  				query_string["groupowned"] = "true";  			}	 -			std::ostringstream link; -			link << "secondlife:///app/objectim/" << session_id << LLURI::mapToQueryString(query_string); - -			chat.mURL = link.str(); +			chat.mURL = LLSLURL("objectim", session_id, "").getSLURLString();  			chat.mText = message;  			chat.mSourceType = CHAT_SOURCE_OBJECT; @@ -2330,7 +2327,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)  			{  				LLSD args;  				// *TODO: Translate -> [FIRST] [LAST] (maybe) -				args["NAME_SLURL"] = LLSLURL::buildCommand("agent", from_id, "about"); +				args["NAME_SLURL"] = LLSLURL("agent", from_id, "about").getSLURLString();  				args["MESSAGE"] = message;  				LLSD payload;  				payload["from_id"] = from_id; @@ -2396,7 +2393,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data)  			}  			else  			{ -				args["NAME_SLURL"] = LLSLURL::buildCommand("agent", from_id, "about"); +				args["NAME_SLURL"] = LLSLURL("agent", from_id, "about").getSLURLString();  				if(message.empty())  				{  					//support for frienship offers from clients before July 2008 @@ -3155,7 +3152,9 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**)  		{  			// Chat the "back" SLURL. (DEV-4907) -			LLSD substitution = LLSD().with("[T_SLURL]", gAgent.getTeleportSourceSLURL()); +			LLSLURL slurl; +			gAgent.getTeleportSourceSLURL(slurl); +			LLSD substitution = LLSD().with("[T_SLURL]", slurl.getSLURLString());  			std::string completed_from = LLAgent::sTeleportProgressMessages["completed_from"];  			LLStringUtil::format(completed_from, substitution); @@ -5548,7 +5547,9 @@ void send_group_notice(const LLUUID& group_id,  bool handle_lure_callback(const LLSD& notification, const LLSD& response)  {  	std::string text = response["message"].asString(); -	text.append("\r\n").append(LLAgentUI::buildSLURL()); +	LLSLURL slurl; +	LLAgentUI::buildSLURL(slurl); +	text.append("\r\n").append(slurl.getSLURLString());  	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);  	if(0 == option) @@ -5991,7 +5992,7 @@ void process_covenant_reply(LLMessageSystem* msg, void**)  	LLFloaterBuyLand::updateEstateName(estate_name);  	std::string owner_name = -		LLSLURL::buildCommand("agent", estate_owner_id, "inspect"); +		LLSLURL("agent", estate_owner_id, "inspect").getSLURLString();  	LLPanelEstateCovenant::updateEstateOwnerName(owner_name);  	LLPanelLandCovenant::updateEstateOwnerName(owner_name);  	LLFloaterBuyLand::updateEstateOwnerName(owner_name); diff --git a/indra/newview/llviewernetwork.cpp b/indra/newview/llviewernetwork.cpp index 987d23630a..d7bb4efe85 100644 --- a/indra/newview/llviewernetwork.cpp +++ b/indra/newview/llviewernetwork.cpp @@ -5,7 +5,7 @@   *   * $LicenseInfo:firstyear=2006&license=viewergpl$   *  - * Copyright (c) 2006-2009, Linden Research, Inc. + * Copyright (c) 2006-2010, Linden Research, Inc.   *    * Second Life Viewer Source Code   * The source code in this file ("Source Code") is provided by Linden Lab @@ -13,13 +13,12 @@   * ("GPL"), unless you have obtained a separate licensing agreement   * ("Other License"), formally executed by you and Linden Lab.  Terms of   * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2   *    * There are special exceptions to the terms and conditions of the GPL as   * it is applied to this Source Code. View the full text of the exception   * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * online at http://secondlife.com/developers/opensource/flossexception   *    * By copying, modifying or distributing this software, you acknowledge   * that you have read and understood your obligations described above, @@ -34,303 +33,478 @@  #include "llviewerprecompiledheaders.h"  #include "llviewernetwork.h" +#include "llviewercontrol.h" +#include "llsdserialize.h" +#include "llweb.h" -#include "llevents.h" -#include "net.h" +                                                             +const char* DEFAULT_LOGIN_PAGE = "http://secondlife.com/app/login/"; -#include "llviewercontrol.h" -#include "lllogin.h" +const char* SYSTEM_GRID_SLURL_BASE = "secondlife://%s/secondlife/"; +const char* MAIN_GRID_SLURL_BASE = "http://maps.secondlife.com/secondlife/"; +const char* SYSTEM_GRID_APP_SLURL_BASE = "secondlife:///app"; -struct LLGridData -{ -	const char* mLabel; -	const char* mName; -	const char* mLoginURI; -	const char* mHelperURI; -}; +const char* DEFAULT_SLURL_BASE = "https://%s/region/"; +const char* DEFAULT_APP_SLURL_BASE = "x-grid-location-info://%s/app"; -static LLGridData gGridInfo[GRID_INFO_COUNT] =  +LLGridManager::LLGridManager()  { -	{ "None", "", "", ""}, -	{ "Aditi",  -	  "util.aditi.lindenlab.com",  -	  "https://login.aditi.lindenlab.com/cgi-bin/login.cgi", -	  "http://aditi-secondlife.webdev.lindenlab.com/helpers/" }, -	{ "Agni",  -	  "util.agni.lindenlab.com",  -	  "https://login.agni.lindenlab.com/cgi-bin/login.cgi", -	  "https://secondlife.com/helpers/" }, -	{ "Aruna", -	  "util.aruna.lindenlab.com", -	  "https://login.aruna.lindenlab.com/cgi-bin/login.cgi", -	  "http://aruna-secondlife.webdev.lindenlab.com/helpers/" }, -	{ "Bharati", -	  "util.bharati.lindenlab.com", -	  "https://login.bharati.lindenlab.com/cgi-bin/login.cgi", -	  "http://bharati-secondlife.webdev.lindenlab.com/helpers/" }, -	{ "Chandra", -	  "util.chandra.lindenlab.com", -	  "https://login.chandra.lindenlab.com/cgi-bin/login.cgi", -	  "http://chandra-secondlife.webdev.lindenlab.com/helpers/" }, -	{ "Damballah", -	  "util.damballah.lindenlab.com", -	  "https://login.damballah.lindenlab.com/cgi-bin/login.cgi", -	  "http://damballah-secondlife.webdev.lindenlab.com/helpers/" }, -	{ "Danu", -	  "util.danu.lindenlab.com", -	  "https://login.danu.lindenlab.com/cgi-bin/login.cgi", -	  "http://danu-secondlife.webdev.lindenlab.com/helpers/" }, -	{ "Durga", -	  "util.durga.lindenlab.com", -	  "https://login.durga.lindenlab.com/cgi-bin/login.cgi", -	  "http://durga-secondlife.webdev.lindenlab.com/helpers/" }, -	{ "Ganga", -	  "util.ganga.lindenlab.com", -	  "https://login.ganga.lindenlab.com/cgi-bin/login.cgi", -	  "http://ganga-secondlife.webdev.lindenlab.com/helpers/" }, -	{ "Mitra", -	  "util.mitra.lindenlab.com", -	  "https://login.mitra.lindenlab.com/cgi-bin/login.cgi", -	  "http://mitra-secondlife.webdev.lindenlab.com/helpers/" }, -	{ "Mohini", -	  "util.mohini.lindenlab.com", -	  "https://login.mohini.lindenlab.com/cgi-bin/login.cgi", -	  "http://mohini-secondlife.webdev.lindenlab.com/helpers/" }, -  	{ "Nandi", -	  "util.nandi.lindenlab.com", -	  "https://login.nandi.lindenlab.com/cgi-bin/login.cgi", -	  "http://nandi-secondlife.webdev.lindenlab.com/helpers/" }, -	{ "Parvati", -	  "util.parvati.lindenlab.com", -	  "https://login.parvati.lindenlab.com/cgi-bin/login.cgi", -	  "http://parvati-secondlife.webdev.lindenlab.com/helpers/" }, -	{ "Radha", -	  "util.radha.lindenlab.com", -	  "https://login.radha.lindenlab.com/cgi-bin/login.cgi", -	  "http://radha-secondlife.webdev.lindenlab.com/helpers/" }, -	{ "Ravi", -	  "util.ravi.lindenlab.com", -	  "https://login.ravi.lindenlab.com/cgi-bin/login.cgi", -	  "http://ravi-secondlife.webdev.lindenlab.com/helpers/" }, -	{ "Siva",  -	  "util.siva.lindenlab.com", -	  "https://login.siva.lindenlab.com/cgi-bin/login.cgi", -	  "http://siva-secondlife.webdev.lindenlab.com/helpers/" }, -	{ "Shakti", -	  "util.shakti.lindenlab.com", -	  "https://login.shakti.lindenlab.com/cgi-bin/login.cgi", -	  "http://shakti-secondlife.webdev.lindenlab.com/helpers/" }, -	{ "Skanda", -	  "util.skanda.lindenlab.com", -	  "https://login.skanda.lindenlab.com/cgi-bin/login.cgi", -	  "http://skanda-secondlife.webdev.lindenlab.com/helpers/" }, -	{ "Soma", -	  "util.soma.lindenlab.com", -	  "https://login.soma.lindenlab.com/cgi-bin/login.cgi", -	  "http://soma-secondlife.webdev.lindenlab.com/helpers/" }, -	{ "Uma", -	  "util.uma.lindenlab.com", -	  "https://login.uma.lindenlab.com/cgi-bin/login.cgi", -	  "http://uma-secondlife.webdev.lindenlab.com/helpers/" }, -	{ "Vaak", -	  "util.vaak.lindenlab.com", -	  "https://login.vaak.lindenlab.com/cgi-bin/login.cgi", -	  "http://vaak-secondlife.webdev.lindenlab.com/helpers/" }, -	{ "Yami", -	  "util.yami.lindenlab.com", -	  "https://login.yami.lindenlab.com/cgi-bin/login.cgi", -	  "http://yami-secondlife.webdev.lindenlab.com/helpers/" }, -	{ "Local",  -	  "localhost",  -	  "https://login.dmz.lindenlab.com/cgi-bin/login.cgi", -	  "" }, -	{ "Other",  -	  "",  -	  "https://login.dmz.lindenlab.com/cgi-bin/login.cgi", -	  "" } -}; - -const EGridInfo DEFAULT_GRID_CHOICE = GRID_INFO_AGNI; - - -unsigned char gMACAddress[MAC_ADDRESS_BYTES];		/* Flawfinder: ignore */ - -LLViewerLogin::LLViewerLogin() : -	mGridChoice(DEFAULT_GRID_CHOICE) +	// by default, we use the 'grids.xml' file in the user settings directory +	// this file is an LLSD file containing multiple grid definitions. +	// This file does not contain definitions for secondlife.com grids, +	// as that would be a security issue when they are overwritten by +	// an attacker.  Don't want someone snagging a password. +	std::string grid_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, +														   "grids.xml"); +	initialize(grid_file); +	 +} + + +LLGridManager::LLGridManager(const std::string& grid_file)  { +	// initialize with an explicity grid file for testing. +	initialize(grid_file);  } - LLViewerLogin::~LLViewerLogin()  - { - } +// +// LLGridManager - class for managing the list of known grids, and the current +// selection +// + -void LLViewerLogin::setGridChoice(EGridInfo grid) -{	 -	if(grid < 0 || grid >= GRID_INFO_COUNT) +// +// LLGridManager::initialze - initialize the list of known grids based +// on the fixed list of linden grids (fixed for security reasons) +// the grids.xml file +// and the command line. +void LLGridManager::initialize(const std::string& grid_file) +{ +	// default grid list. +	// Don't move to a modifiable file for security reasons, +	mGrid.clear() ; +	// set to undefined +	mGridList = LLSD(); +	mGridFile = grid_file; +	// as we don't want an attacker to override our grid list +	// to point the default grid to an invalid grid +	addSystemGrid("None", "", "", "", DEFAULT_LOGIN_PAGE); +	 + + +#ifndef LL_RELEASE_FOR_DOWNLOAD +  	addSystemGrid("Agni",                                                                                              +				  MAINGRID,                                                +				  "https://login.agni.lindenlab.com/cgi-bin/login.cgi",                     +				  "https://secondlife.com/helpers/",      +				  DEFAULT_LOGIN_PAGE); +#else +	addSystemGrid("Secondlife.com",                                                                                              +				  MAINGRID,                                                +				  "https://login.agni.lindenlab.com/cgi-bin/login.cgi",                     +				  "https://secondlife.com/helpers/",      +				  DEFAULT_LOGIN_PAGE, +				  "Agni"); +#endif // LL_RELEASE_FOR_DOWNLOAD	 +	addSystemGrid("Aditi",                                                                                              +				  "util.aditi.lindenlab.com",                                               +				  "https://login.aditi.lindenlab.com/cgi-bin/login.cgi",                    +				  "http://aditi-secondlife.webdev.lindenlab.com/helpers/", +				  DEFAULT_LOGIN_PAGE); +	addSystemGrid("Aruna",                                                                                             +				  "util.aruna.lindenlab.com",                                               +				  "https://login.aruna.lindenlab.com/cgi-bin/login.cgi",                    +				  "http://aruna-secondlife.webdev.lindenlab.com/helpers/", +				  DEFAULT_LOGIN_PAGE); +	addSystemGrid("Durga",                                                                                             +				  "util.durga.lindenlab.com",                                               +				  "https://login.durga.lindenlab.com/cgi-bin/login.cgi",                    +				  "http://durga-secondlife.webdev.lindenlab.com/helpers/", +				  DEFAULT_LOGIN_PAGE); +	addSystemGrid("Ganga",                                                                                             +				  "util.ganga.lindenlab.com",                                               +				  "https://login.ganga.lindenlab.com/cgi-bin/login.cgi",                    +				  "http://ganga-secondlife.webdev.lindenlab.com/helpers/", +				  DEFAULT_LOGIN_PAGE); +	addSystemGrid("Mitra",                                                                                             +				  "util.mitra.lindenlab.com",                                               +				  "https://login.mitra.lindenlab.com/cgi-bin/login.cgi",                    +				  "http://mitra-secondlife.webdev.lindenlab.com/helpers/", +				  DEFAULT_LOGIN_PAGE); +	addSystemGrid("Mohini",                                                                                            +				  "util.mohini.lindenlab.com",                                              +				  "https://login.mohini.lindenlab.com/cgi-bin/login.cgi",                   +				  "http://mohini-secondlife.webdev.lindenlab.com/helpers/", +				  DEFAULT_LOGIN_PAGE); +	addSystemGrid("Nandi",                                                                                             +				  "util.nandi.lindenlab.com",                                               +				  "https://login.nandi.lindenlab.com/cgi-bin/login.cgi",                    +				  "http://nandi-secondlife.webdev.lindenlab.com/helpers/", +				  DEFAULT_LOGIN_PAGE); +	addSystemGrid("Radha",                                                                                             +				  "util.radha.lindenlab.com",                                               +				  "https://login.radha.lindenlab.com/cgi-bin/login.cgi",                    +				  "http://radha-secondlife.webdev.lindenlab.com/helpers/", +				  DEFAULT_LOGIN_PAGE); +	addSystemGrid("Ravi",                                                                                              +				  "util.ravi.lindenlab.com",                                                +				  "https://login.ravi.lindenlab.com/cgi-bin/login.cgi",                     +				  "http://ravi-secondlife.webdev.lindenlab.com/helpers/", +				  DEFAULT_LOGIN_PAGE); +	addSystemGrid("Siva",                                                                                              +				  "util.siva.lindenlab.com",                                                +				  "https://login.siva.lindenlab.com/cgi-bin/login.cgi",                     +				  "http://siva-secondlife.webdev.lindenlab.com/helpers/", +				  DEFAULT_LOGIN_PAGE); +	addSystemGrid("Shakti",                                                                                            +				  "util.shakti.lindenlab.com",                                              +				  "https://login.shakti.lindenlab.com/cgi-bin/login.cgi",                   +				  "http://shakti-secondlife.webdev.lindenlab.com/helpers/", +				  DEFAULT_LOGIN_PAGE); +	addSystemGrid("Soma",                                                                                              +				  "util.soma.lindenlab.com",                                                +				  "https://login.soma.lindenlab.com/cgi-bin/login.cgi",                     +				  "http://soma-secondlife.webdev.lindenlab.com/helpers/", +				  DEFAULT_LOGIN_PAGE); +	 +	addSystemGrid("Uma",                                                                                               +				  "util.uma.lindenlab.com",                                                 +				  "https://login.uma.lindenlab.com/cgi-bin/login.cgi",                      +				  "http://uma-secondlife.webdev.lindenlab.com/helpers/", +				  DEFAULT_LOGIN_PAGE); +	addSystemGrid("Vaak",                                                                                              +				  "util.vaak.lindenlab.com",                                                +				  "https://login.vaak.lindenlab.com/cgi-bin/login.cgi",                     +				  "http://vaak-secondlife.webdev.lindenlab.com/helpers/", +				  DEFAULT_LOGIN_PAGE); +	addSystemGrid("Yami",                                                                                              +				  "util.yami.lindenlab.com",                                                +				  "https://login.yami.lindenlab.com/cgi-bin/login.cgi",                     +				  "http://yami-secondlife.webdev.lindenlab.com/helpers/", +				  DEFAULT_LOGIN_PAGE); +	addSystemGrid("Local (Linden)",                                                                                     +				  "localhost",                                                              +				  "https://login.dmz.lindenlab.com/cgi-bin/login.cgi",                      +				  "", +				  DEFAULT_LOGIN_PAGE);  + +	 +	LLSD other_grids; +	llifstream llsd_xml; +	if (!grid_file.empty())  	{ -		llerrs << "Invalid grid index specified." << llendl; -		return; +		llsd_xml.open( grid_file.c_str(), std::ios::in | std::ios::binary ); + +		// parse through the gridfile, inserting grids into the list unless +		// they overwrite a linden grid. +		if( llsd_xml.is_open())  +		{ +			LLSDSerialize::fromXMLDocument( other_grids, llsd_xml ); +			if(other_grids.isMap()) +			{ +				for(LLSD::map_iterator grid_itr = other_grids.beginMap();  +					grid_itr != other_grids.endMap(); +					++grid_itr) +				{ +					LLSD::String key_name = grid_itr->first; +					LLSD grid = grid_itr->second; +					// TODO:  Make sure gridfile specified label is not  +					// a system grid label +					LL_INFOS("GridManager") << "reading: " << key_name << LL_ENDL; +					if (mGridList.has(key_name) && +						mGridList[key_name].has(GRID_IS_SYSTEM_GRID_VALUE)) +					{ +						LL_INFOS("GridManager") << "Cannot override grid " << key_name << " as it's a system grid" << LL_ENDL; +						// If the system grid does exist in the grids file, and it's marked as a favorite, set it as a favorite. +						if(grid_itr->second.has(GRID_IS_FAVORITE_VALUE) && grid_itr->second[GRID_IS_FAVORITE_VALUE].asBoolean() ) +						{ +							mGridList[key_name][GRID_IS_FAVORITE_VALUE] = TRUE; +						} +					} +					else +					{ +						try +						{ +							addGrid(grid); +							LL_INFOS("GridManager") << "Added grid: " << key_name << LL_ENDL; +						} +						catch (...) +						{ +						} +					} +				} +				llsd_xml.close(); +			}	 +		}       	} +	 +	// load a grid from the command line. +	// if the actual grid name is specified from the command line, +	// set it as the 'selected' grid. +	mGrid = gSavedSettings.getString("CmdLineGridChoice"); +	LL_INFOS("GridManager") << "Grid Name: " << mGrid << LL_ENDL;		 +	 +	// If a command line login URI was passed in, so we should add the command +	// line grid to the list of grids -	if(mGridChoice != grid || gSavedSettings.getS32("ServerChoice") != grid) +	LLSD cmd_line_login_uri = gSavedSettings.getLLSD("CmdLineLoginURI"); +	if (cmd_line_login_uri.isString())  	{ -		mGridChoice = grid; -		if(GRID_INFO_LOCAL == mGridChoice) +		LL_INFOS("GridManager") << "adding cmd line login uri" << LL_ENDL; +		// grab the other related URI values +		std::string cmd_line_helper_uri = gSavedSettings.getString("CmdLineHelperURI"); +		std::string cmd_line_login_page = gSavedSettings.getString("LoginPage"); +		 +		// we've a cmd line login, so add a grid for the command line, +		// overwriting any existing grids +		LLSD grid = LLSD::emptyMap(); +		grid[GRID_LOGIN_URI_VALUE] = LLSD::emptyArray(); +		grid[GRID_LOGIN_URI_VALUE].append(cmd_line_login_uri); +		LL_INFOS("GridManager") << "cmd line login uri: " << cmd_line_login_uri.asString() << LL_ENDL; +		LLURI uri(cmd_line_login_uri.asString()); +		if (mGrid.empty())  		{ -			mGridName = LOOPBACK_ADDRESS_STRING; +			// if a grid name was not passed in via the command line, +			// then set the grid name based on the hostname of the  +			// login uri +			mGrid = uri.hostName();  		} -		else if(GRID_INFO_OTHER == mGridChoice) + +		grid[GRID_VALUE] = mGrid; + +		if (mGridList.has(mGrid) && mGridList[mGrid].has(GRID_LABEL_VALUE))  		{ -			// *FIX:Mani - could this possibly be valid? -			mGridName = "other";  +			grid[GRID_LABEL_VALUE] = mGridList[mGrid][GRID_LABEL_VALUE];  		}  		else  		{ -			mGridName = gGridInfo[mGridChoice].mLabel; +			grid[GRID_LABEL_VALUE] = mGrid;			 +		} +		if(!cmd_line_helper_uri.empty()) +		{ +			grid[GRID_HELPER_URI_VALUE] = cmd_line_helper_uri;  		} -		gSavedSettings.setS32("ServerChoice", mGridChoice); -		gSavedSettings.setString("CustomServer", ""); +		if(!cmd_line_login_page.empty()) +		{ +			grid[GRID_LOGIN_PAGE_VALUE] = cmd_line_login_page; +		} +		// if the login page, helper URI value, and so on are not specified, +		// add grid will generate them. + +		// Also, we will override a system grid if values are passed in via the command +		// line, for testing.  These values will not be remembered though. +		if (mGridList.has(mGrid) && mGridList[mGrid].has(GRID_IS_SYSTEM_GRID_VALUE)) +		{ +			grid[GRID_IS_SYSTEM_GRID_VALUE] = TRUE; +		} +		addGrid(grid);  	} -} +	 +	// if a grid was not passed in via the command line, grab it from the CurrentGrid setting. +	if (mGrid.empty()) +	{ + +		mGrid = gSavedSettings.getString("CurrentGrid"); +	} + +	if (mGrid.empty() || !mGridList.has(mGrid)) +	{ +		// the grid name was empty, or the grid isn't actually in the list, then set it to the +		// appropriate default. +		LL_INFOS("GridManager") << "Resetting grid as grid name " << mGrid << " is not in the list" << LL_ENDL; +#if LL_RELEASE_FOR_DOWNLOAD +		mGrid = MAINGRID; +#else +		mGrid = ""; +#endif +	} +	LL_INFOS("GridManager") << "Selected grid is " << mGrid << LL_ENDL;		 +	gSavedSettings.setString("CurrentGrid", mGrid); -void LLViewerLogin::setGridChoice(const std::string& grid_name) -{ -	// Set the grid choice based on a string. -	// The string can be: -	// - a grid label from the gGridInfo table  -	// - an ip address -    if(!grid_name.empty()) -    { -        // find the grid choice from the user setting. -        int grid_index = GRID_INFO_NONE;  -        for(;grid_index < GRID_INFO_OTHER; ++grid_index) -        { -            if(0 == LLStringUtil::compareInsensitive(gGridInfo[grid_index].mLabel, grid_name)) -            { -				// Founding a matching label in the list... -				setGridChoice((EGridInfo)grid_index); -				break; -            } -        } - -        if(GRID_INFO_OTHER == grid_index) -        { -            // *FIX:MEP Can and should we validate that this is an IP address? -            mGridChoice = GRID_INFO_OTHER; -            mGridName = grid_name; -			gSavedSettings.setS32("ServerChoice", mGridChoice); -			gSavedSettings.setString("CustomServer", mGridName); -        } -    }  } -void LLViewerLogin::resetURIs() +LLGridManager::~LLGridManager()  { -    // Clear URIs when picking a new server -	gSavedSettings.setLLSD("CmdLineLoginURI", LLSD::emptyArray()); -	gSavedSettings.setString("CmdLineHelperURI", ""); +	saveFavorites();  } -EGridInfo LLViewerLogin::getGridChoice() const +// +// LLGridManager::addGrid - add a grid to the grid list, populating the needed values +// if they're not populated yet. +// + +void LLGridManager::addGrid(LLSD& grid_data)  { -	return mGridChoice; +	if (grid_data.isMap() && grid_data.has(GRID_VALUE)) +	{ +		std::string grid = utf8str_tolower(grid_data[GRID_VALUE]); + +		// grid should be in the form of a dns address +		if (!grid.empty() && +			grid.find_first_not_of("abcdefghijklmnopqrstuvwxyz1234567890-_. ") != std::string::npos) +		{ +			printf("grid name: %s", grid.c_str()); +			throw LLInvalidGridName(grid); +		} +		 +		// populate the other values if they don't exist +		if (!grid_data.has(GRID_LABEL_VALUE))  +		{ +			grid_data[GRID_LABEL_VALUE] = grid; +		} +		if (!grid_data.has(GRID_ID_VALUE)) +		{ +			grid_data[GRID_ID_VALUE] = grid; +		} +		 +		// if the grid data doesn't include any of the URIs, then  +		// generate them from the grid, which should be a dns address +		if (!grid_data.has(GRID_LOGIN_URI_VALUE))  +		{ +			grid_data[GRID_LOGIN_URI_VALUE] = LLSD::emptyArray(); +			grid_data[GRID_LOGIN_URI_VALUE].append(std::string("https://") +  +													grid + "/cgi-bin/login.cgi"); +		} +		// Populate to the default values +		if (!grid_data.has(GRID_LOGIN_PAGE_VALUE))  +		{ +			grid_data[GRID_LOGIN_PAGE_VALUE] = std::string("http://") + grid + "/app/login/"; +		}		 +		if (!grid_data.has(GRID_HELPER_URI_VALUE))  +		{ +			grid_data[GRID_HELPER_URI_VALUE] = std::string("https://") + grid + "/helpers/"; +		}		 +		LL_INFOS("GridManager") << "ADDING: " << grid << LL_ENDL; +		mGridList[grid] = grid_data;		 +	}  } -std::string LLViewerLogin::getGridLabel() const +// +// LLGridManager::addSystemGrid - helper for adding a system grid. +void LLGridManager::addSystemGrid(const std::string& label,  +								  const std::string& name,  +								  const std::string& login,  +								  const std::string& helper, +								  const std::string& login_page, +								  const std::string& login_id)  { -	if(mGridChoice == GRID_INFO_NONE) +	LLSD grid = LLSD::emptyMap(); +	grid[GRID_VALUE] = name; +	grid[GRID_LABEL_VALUE] = label; +	grid[GRID_HELPER_URI_VALUE] = helper; +	grid[GRID_LOGIN_URI_VALUE] = LLSD::emptyArray(); +	grid[GRID_LOGIN_URI_VALUE].append(login); +	grid[GRID_LOGIN_PAGE_VALUE] = login_page; +	grid[GRID_IS_SYSTEM_GRID_VALUE] = TRUE; +	grid[GRID_LOGIN_CREDENTIAL_PAGE_TYPE_VALUE] = GRID_LOGIN_CREDENTIAL_PAGE_TYPE_AGENT; +	 +	grid[GRID_APP_SLURL_BASE] = SYSTEM_GRID_APP_SLURL_BASE; +	if (login_id.empty())  	{ -		return "None"; +		grid[GRID_ID_VALUE] = name;  	} -	else if(mGridChoice < GRID_INFO_OTHER) +	else  	{ -		return gGridInfo[mGridChoice].mLabel; +		grid[GRID_ID_VALUE] = login_id;  	} - -	return mGridName; -} - -std::string LLViewerLogin::getKnownGridLabel(EGridInfo grid_index) const -{ -	if(grid_index > GRID_INFO_NONE && grid_index < GRID_INFO_OTHER) +	 +	// only add the system grids beyond agni to the visible list +	// if we're building a debug version. +	if (name == std::string(MAINGRID)) +	{ +		grid[GRID_SLURL_BASE] = MAIN_GRID_SLURL_BASE;		 +		grid[GRID_IS_FAVORITE_VALUE] = TRUE;		 +	} +	else  	{ -		return gGridInfo[grid_index].mLabel; +		grid[GRID_SLURL_BASE] = llformat(SYSTEM_GRID_SLURL_BASE, label.c_str());		  	} -	return gGridInfo[GRID_INFO_NONE].mLabel; +	addGrid(grid);  } -void LLViewerLogin::getLoginURIs(std::vector<std::string>& uris) const +// return a list of grid name -> grid label mappings for UI purposes +std::map<std::string, std::string> LLGridManager::getKnownGrids(bool favorite_only)  { -	// return the login uri set on the command line. -	LLControlVariable* c = gSavedSettings.getControl("CmdLineLoginURI"); -	if(c) +	std::map<std::string, std::string> result; +	for(LLSD::map_iterator grid_iter = mGridList.beginMap(); +		grid_iter != mGridList.endMap(); +		grid_iter++)   	{ -		LLSD v = c->getValue(); -		if(v.isArray()) +		if(!favorite_only || grid_iter->second.has(GRID_IS_FAVORITE_VALUE))  		{ -			for(LLSD::array_const_iterator itr = v.beginArray(); -				itr != v.endArray(); ++itr) -			{ -				std::string uri = itr->asString(); -				if(!uri.empty()) -				{ -					uris.push_back(uri); -				} -			} -		} -		else -		{ -			std::string uri = v.asString(); -			if(!uri.empty()) -			{ -				uris.push_back(uri); -			} +			result[grid_iter->first] = grid_iter->second[GRID_LABEL_VALUE].asString();  		}  	} -	// If there was no command line uri... -	if(uris.empty()) +	return result; +} + +void LLGridManager::setGridChoice(const std::string& grid) +{ +	// Set the grid choice based on a string. +	// The string can be: +	// - a grid label from the gGridInfo table  +	// - a hostname +	// - an ip address + +	// loop through.  We could do just a hash lookup but we also want to match +	// on label +	for(LLSD::map_iterator grid_iter = mGridList.beginMap(); +		grid_iter != mGridList.endMap(); +		grid_iter++)   	{ -		// If its a known grid choice, get the uri from the table, -		// else try the grid name. -		if(mGridChoice > GRID_INFO_NONE && mGridChoice < GRID_INFO_OTHER) +		if((grid == grid_iter->first) ||  +		   (grid == grid_iter->second[GRID_LABEL_VALUE].asString()))  		{ -			uris.push_back(gGridInfo[mGridChoice].mLoginURI); -		} -		else -		{ -			uris.push_back(mGridName); +			mGrid = grid_iter->second[GRID_VALUE].asString(); +			gSavedSettings.setString("CurrentGrid", grid_iter->second[GRID_VALUE]);			 +			return;  +  		}  	} +	LLSD grid_data = LLSD::emptyMap(); +	grid_data[GRID_VALUE] = grid; +	addGrid(grid_data); +	mGrid = grid; +	gSavedSettings.setString("CurrentGrid", grid);  } -std::string LLViewerLogin::getHelperURI() const +std::string LLGridManager::getGridByLabel( const std::string &grid_label)  { -	std::string helper_uri = gSavedSettings.getString("CmdLineHelperURI"); -	if (helper_uri.empty()) +	for(LLSD::map_iterator grid_iter = mGridList.beginMap(); +		grid_iter != mGridList.endMap(); +		grid_iter++)   	{ -		// grab URI from selected grid -		if(mGridChoice > GRID_INFO_NONE && mGridChoice < GRID_INFO_OTHER) +		if (grid_iter->second.has(GRID_LABEL_VALUE) && (grid_iter->second[GRID_LABEL_VALUE].asString() == grid_label))  		{ -			helper_uri = gGridInfo[mGridChoice].mHelperURI; +			return grid_iter->first;  		} +	} +	return std::string(); +} -		if (helper_uri.empty()) -		{ -			// what do we do with unnamed/miscellaneous grids? -			// for now, operations that rely on the helper URI (currency/land purchasing) will fail -		} +void LLGridManager::getLoginURIs(std::vector<std::string>& uris) +{ +	uris.clear(); +	for (LLSD::array_iterator llsd_uri = mGridList[mGrid][GRID_LOGIN_URI_VALUE].beginArray(); +		 llsd_uri != mGridList[mGrid][GRID_LOGIN_URI_VALUE].endArray(); +		 llsd_uri++) +	{ +		uris.push_back(llsd_uri->asString());  	} -	return helper_uri;  } -bool LLViewerLogin::isInProductionGrid() +bool LLGridManager::isInProductionGrid()  {  	// *NOTE:Mani This used to compare GRID_INFO_AGNI to gGridChoice,  	// but it seems that loginURI trumps that.  	std::vector<std::string> uris;  	getLoginURIs(uris); +	if (uris.size() < 1) +	{ +		return 1; +	}  	LLStringUtil::toLower(uris[0]);  	if((uris[0].find("agni") != std::string::npos))  	{ @@ -339,3 +513,51 @@ bool LLViewerLogin::isInProductionGrid()  	return false;  } + +void LLGridManager::saveFavorites() +{ +	// filter out just those marked as favorites +	LLSD output_grid_list = LLSD::emptyMap(); +	for(LLSD::map_iterator grid_iter = mGridList.beginMap(); +		grid_iter != mGridList.endMap(); +		grid_iter++) +	{ +		if(grid_iter->second.has(GRID_IS_FAVORITE_VALUE)) +		{ +			output_grid_list[grid_iter->first] = grid_iter->second; +		} +	}        +	llofstream llsd_xml; +	llsd_xml.open( mGridFile.c_str(), std::ios::out | std::ios::binary);	 +	LLSDSerialize::toPrettyXML(output_grid_list, llsd_xml); +	llsd_xml.close(); +} + + +// build a slurl for the given region within the selected grid +std::string LLGridManager::getSLURLBase(const std::string& grid) +{ +	std::string grid_base; +	if(mGridList.has(grid) && mGridList[grid].has(GRID_SLURL_BASE)) +	{ +		return mGridList[grid][GRID_SLURL_BASE].asString(); +	} +	else +	{ +		return  llformat(DEFAULT_SLURL_BASE, grid.c_str()); +	} +} + +// build a slurl for the given region within the selected grid +std::string LLGridManager::getAppSLURLBase(const std::string& grid) +{ +	std::string grid_base; +	if(mGridList.has(grid) && mGridList[grid].has(GRID_APP_SLURL_BASE)) +	{ +	  return mGridList[grid][GRID_APP_SLURL_BASE].asString(); +	} +	else +	{ +	  return  llformat(DEFAULT_APP_SLURL_BASE, grid.c_str()); +	} +} diff --git a/indra/newview/llviewernetwork.h b/indra/newview/llviewernetwork.h index edae6dc47b..46f21bf20f 100644 --- a/indra/newview/llviewernetwork.h +++ b/indra/newview/llviewernetwork.h @@ -5,7 +5,7 @@   *   * $LicenseInfo:firstyear=2006&license=viewergpl$   *  - * Copyright (c) 2006-2009, Linden Research, Inc. + * Copyright (c) 2006-2010, Linden Research, Inc.   *    * Second Life Viewer Source Code   * The source code in this file ("Source Code") is provided by Linden Lab @@ -13,13 +13,12 @@   * ("GPL"), unless you have obtained a separate licensing agreement   * ("Other License"), formally executed by you and Linden Lab.  Terms of   * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2   *    * There are special exceptions to the terms and conditions of the GPL as   * it is applied to this Source Code. View the full text of the exception   * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * online at http://secondlife.com/developers/opensource/flossexception   *    * By copying, modifying or distributing this software, you acknowledge   * that you have read and understood your obligations described above, @@ -33,83 +32,136 @@  #ifndef LL_LLVIEWERNETWORK_H  #define LL_LLVIEWERNETWORK_H +                                                                                                        +extern const char* DEFAULT_LOGIN_PAGE; +       +#define GRID_VALUE "name" +#define GRID_LABEL_VALUE "label" +#define GRID_ID_VALUE "grid_login_id" +#define GRID_LOGIN_URI_VALUE "login_uri" +#define GRID_HELPER_URI_VALUE "helper_uri" +#define GRID_LOGIN_PAGE_VALUE "login_page" +#define GRID_IS_SYSTEM_GRID_VALUE "system_grid" +#define GRID_IS_FAVORITE_VALUE "favorite" +#define GRID_LOGIN_CREDENTIAL_PAGE_TYPE_VALUE "credential_type" +#define GRID_LOGIN_CREDENTIAL_PAGE_TYPE_AGENT "agent" +#define GRID_LOGIN_CREDENTIAL_PAGE_TYPE_ACCOUNT "account" +#define MAINGRID "util.agni.lindenlab.com" -#include <boost/scoped_ptr.hpp> +// defines slurl formats associated with various grids. +// we need to continue to support existing forms, as slurls +// are shared between viewers that may not understand newer +// forms. +#define GRID_SLURL_BASE "slurl_base" +#define GRID_APP_SLURL_BASE "app_slurl_base" -class LLHost; -class LLLogin; - -enum EGridInfo +class LLInvalidGridName  { -	GRID_INFO_NONE, -	GRID_INFO_ADITI, -	GRID_INFO_AGNI, -	GRID_INFO_ARUNA, -	GRID_INFO_BHARATI, -	GRID_INFO_CHANDRA, -	GRID_INFO_DAMBALLAH, -	GRID_INFO_DANU, -	GRID_INFO_DURGA, -	GRID_INFO_GANGA, -	GRID_INFO_MITRA, -	GRID_INFO_MOHINI, -	GRID_INFO_NANDI, -	GRID_INFO_PARVATI, -	GRID_INFO_RADHA, -	GRID_INFO_RAVI, -	GRID_INFO_SIVA, -	GRID_INFO_SHAKTI, -	GRID_INFO_SKANDA, -	GRID_INFO_SOMA, -	GRID_INFO_UMA, -	GRID_INFO_VAAK, -	GRID_INFO_YAMI, -	GRID_INFO_LOCAL, -	GRID_INFO_OTHER, // IP address set via command line option -	GRID_INFO_COUNT +public: +	LLInvalidGridName(std::string grid) : mGrid(grid) +	{ +	} +protected: +	std::string mGrid;  }; +  /** - * @brief A class to manage the viewer's login state. + * @brief A class to manage the grids available to the viewer + * including persistance.  This class also maintains the currently + * selected grid.   *    **/ -class LLViewerLogin : public LLSingleton<LLViewerLogin> +class LLGridManager : public LLSingleton<LLGridManager>  {  public: -	LLViewerLogin(); -	~LLViewerLogin(); - -	void setGridChoice(EGridInfo grid); -	void setGridChoice(const std::string& grid_name); -	void resetURIs(); +	 +	// when the grid manager is instantiated, the default grids are automatically +	// loaded, and the grids favorites list is loaded from the xml file. +	LLGridManager(const std::string& grid_file); +	LLGridManager(); +	~LLGridManager(); +	 +	void initialize(const std::string& grid_file); +	// grid list management +	 +	// add a grid to the list of grids +	void addGrid(LLSD& grid_info);	 -	/** -	* @brief Get the enumeration of the grid choice. -	* Should only return values > 0 && < GRID_INFO_COUNT -	**/ -	EGridInfo getGridChoice() const; - -	/** -	* @brief Get a readable label for the grid choice. -	* Returns the readable name for the grid choice.  -	* If the grid is 'other', returns something -	* the string used to specifiy the grid. -	**/ -	std::string getGridLabel() const;  - -	std::string getKnownGridLabel(EGridInfo grid_index) const;  - -	void getLoginURIs(std::vector<std::string>& uris) const; -	std::string getHelperURI() const; +	// retrieve a map of grid-name <-> label +	// by default only return the user visible grids +	std::map<std::string, std::string> getKnownGrids(bool favorites_only=FALSE); +	 +	LLSD getGridInfo(const std::string& grid) +	{ +		if(mGridList.has(grid)) +		{ +			return mGridList[grid]; +		} +		else +		{ +			return LLSD(); +		} +	} +	 +	// current grid management +	// select a given grid as the current grid.  If the grid +	// is not a known grid, then it's assumed to be a dns name for the +	// grid, and the various URIs will be automatically generated. +	void setGridChoice(const std::string& grid); +	 +	 +	std::string getGridLabel() { return mGridList[mGrid][GRID_LABEL_VALUE]; } 	 +	std::string getGrid() const { return mGrid; } +	void getLoginURIs(std::vector<std::string>& uris); +	std::string getHelperURI() {return mGridList[mGrid][GRID_HELPER_URI_VALUE];} +	std::string getLoginPage() {return mGridList[mGrid][GRID_LOGIN_PAGE_VALUE];} +	std::string getGridLoginID() { return mGridList[mGrid][GRID_ID_VALUE]; }	 +	std::string getLoginPage(const std::string& grid) { return mGridList[grid][GRID_LOGIN_PAGE_VALUE]; } +	 +	// build a slurl for the given region within the selected grid +	std::string getSLURLBase(const std::string& grid); +	std::string getSLURLBase() { return getSLURLBase(mGrid); } +	 +	std::string getAppSLURLBase(const std::string& grid); +	std::string getAppSLURLBase() { return getAppSLURLBase(mGrid); }	 +	 +	LLSD getGridInfo() { return mGridList[mGrid]; } +	 +	std::string getGridByLabel( const std::string &grid_label); +	 +	bool isSystemGrid(const std::string& grid)  +	{  +		return mGridList.has(grid) && +		      mGridList[grid].has(GRID_IS_SYSTEM_GRID_VALUE) &&  +	           mGridList[grid][GRID_IS_SYSTEM_GRID_VALUE].asBoolean();  +	} +	bool isSystemGrid() { return isSystemGrid(mGrid); } +	// Mark this grid as a favorite that should be persisited on 'save' +	// this is currently used to persist a grid after a successful login +	void setFavorite() { mGridList[mGrid][GRID_IS_FAVORITE_VALUE] = TRUE; } +	  	bool isInProductionGrid(); +	void saveFavorites(); +	void clearFavorites(); + +protected: -private: -	EGridInfo mGridChoice; -	std::string mGridName; +	// helper function for adding the predefined grids +	void addSystemGrid(const std::string& label,  +					   const std::string& name,  +					   const std::string& login,  +					   const std::string& helper, +					   const std::string& login_page, +					   const std::string& login_id = "");	 +	 +	 +	std::string mGrid; +	std::string mGridFile; +	LLSD mGridList;  };  const S32 MAC_ADDRESS_BYTES = 6; -extern unsigned char gMACAddress[MAC_ADDRESS_BYTES];		/* Flawfinder: ignore */  #endif diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 8860b734bb..e6d14079c9 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -4726,7 +4726,7 @@ BOOL LLViewerObject::permYouOwner() const  		return TRUE;  #else  # ifdef TOGGLE_HACKED_GODLIKE_VIEWER -		if (!LLViewerLogin::getInstance()->isInProductionGrid() +		if (!LLGridManager::getInstance()->isInProductionGrid()              && (gAgent.getGodLevel() >= GOD_MAINTENANCE))  		{  			return TRUE; @@ -4763,7 +4763,7 @@ BOOL LLViewerObject::permOwnerModify() const  		return TRUE;  #else  # ifdef TOGGLE_HACKED_GODLIKE_VIEWER -		if (!LLViewerLogin::getInstance()->isInProductionGrid() +		if (!LLGridManager::getInstance()->isInProductionGrid()              && (gAgent.getGodLevel() >= GOD_MAINTENANCE))  	{  			return TRUE; @@ -4787,7 +4787,7 @@ BOOL LLViewerObject::permModify() const  		return TRUE;  #else  # ifdef TOGGLE_HACKED_GODLIKE_VIEWER -		if (!LLViewerLogin::getInstance()->isInProductionGrid() +		if (!LLGridManager::getInstance()->isInProductionGrid()              && (gAgent.getGodLevel() >= GOD_MAINTENANCE))  	{  			return TRUE; @@ -4811,7 +4811,7 @@ BOOL LLViewerObject::permCopy() const  		return TRUE;  #else  # ifdef TOGGLE_HACKED_GODLIKE_VIEWER -		if (!LLViewerLogin::getInstance()->isInProductionGrid() +		if (!LLGridManager::getInstance()->isInProductionGrid()              && (gAgent.getGodLevel() >= GOD_MAINTENANCE))  		{  			return TRUE; @@ -4835,7 +4835,7 @@ BOOL LLViewerObject::permMove() const  		return TRUE;  #else  # ifdef TOGGLE_HACKED_GODLIKE_VIEWER -		if (!LLViewerLogin::getInstance()->isInProductionGrid() +		if (!LLGridManager::getInstance()->isInProductionGrid()              && (gAgent.getGodLevel() >= GOD_MAINTENANCE))  		{  			return TRUE; @@ -4859,7 +4859,7 @@ BOOL LLViewerObject::permTransfer() const  		return TRUE;  #else  # ifdef TOGGLE_HACKED_GODLIKE_VIEWER -		if (!LLViewerLogin::getInstance()->isInProductionGrid() +		if (!LLGridManager::getInstance()->isInProductionGrid()              && (gAgent.getGodLevel() >= GOD_MAINTENANCE))  		{  			return TRUE; diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index b7c265be59..bdc34d0f18 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -768,9 +768,11 @@ void send_stats()  	system["ram"] = (S32) gSysMemory.getPhysicalMemoryKB();  	system["os"] = LLAppViewer::instance()->getOSInfo().getOSStringSimple();  	system["cpu"] = gSysCPU.getCPUString(); +	unsigned char MACAddress[MAC_ADDRESS_BYTES]; +	LLUUID::getNodeID(MACAddress);  	std::string macAddressString = llformat("%02x-%02x-%02x-%02x-%02x-%02x", -											gMACAddress[0],gMACAddress[1],gMACAddress[2], -											gMACAddress[3],gMACAddress[4],gMACAddress[5]); +											MACAddress[0],MACAddress[1],MACAddress[2], +											MACAddress[3],MACAddress[4],MACAddress[5]);  	system["mac_address"] = macAddressString;  	system["serial_number"] = LLAppViewer::instance()->getSerialNumber();  	std::string gpu_desc = llformat( diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index d7d5cbbd10..4c6a02db87 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -85,7 +85,6 @@  #include "lltooltip.h"  #include "llmediaentry.h"  #include "llurldispatcher.h" -#include "llurlsimstring.h"  // newview includes  #include "llagent.h" @@ -799,7 +798,7 @@ BOOL LLViewerWindow::handleRightMouseUp(LLWindow *window,  LLCoordGL pos, MASK m  BOOL LLViewerWindow::handleMiddleMouseDown(LLWindow *window,  LLCoordGL pos, MASK mask)  {  	BOOL down = TRUE; -	gVoiceClient->middleMouseState(true); +	LLVoiceClient::getInstance()->middleMouseState(true);   	handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down);    	// Always handled as far as the OS is concerned. @@ -826,20 +825,15 @@ LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *wi  				if (slurl_dnd_enabled)  				{ -					 -					// special case SLURLs -					// isValidSLURL() call was added here to make sure that dragged SLURL is valid (EXT-4964) -					if ( LLSLURL::isSLURL( data ) && LLSLURL::isValidSLURL( data ) ) +					LLSLURL dropped_slurl(data); +					if(dropped_slurl.isSpatial())  					{  						if (drop)  						{ -							LLURLDispatcher::dispatch( data, NULL, true ); -							LLURLSimString::setStringRaw( LLSLURL::stripProtocol( data ) ); -							LLPanelLogin::refreshLocation( true ); -							LLPanelLogin::updateLocationUI(); +							LLURLDispatcher::dispatch( dropped_slurl.getSLURLString(), NULL, true ); +							return LLWindowCallbacks::DND_MOVE;  						} -						return LLWindowCallbacks::DND_MOVE; -					}; +					}  				}  				if (prim_media_dnd_enabled) @@ -957,7 +951,7 @@ LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *wi  BOOL LLViewerWindow::handleMiddleMouseUp(LLWindow *window,  LLCoordGL pos, MASK mask)  {  	BOOL down = FALSE; -	gVoiceClient->middleMouseState(false); +	LLVoiceClient::getInstance()->middleMouseState(false);   	handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down);    	// Always handled as far as the OS is concerned. @@ -1074,7 +1068,7 @@ void LLViewerWindow::handleFocusLost(LLWindow *window)  BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key,  MASK mask, BOOL repeated)  {  	// Let the voice chat code check for its PTT key.  Note that this never affects event processing. -	gVoiceClient->keyDown(key, mask); +	LLVoiceClient::getInstance()->keyDown(key, mask);  	if (gAwayTimer.getElapsedTimeF32() > MIN_AFK_TIME)  	{ @@ -1096,7 +1090,7 @@ BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key,  MASK mask, BOOL repeated)  BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key,  MASK mask)  {  	// Let the voice chat code check for its PTT key.  Note that this never affects event processing. -	gVoiceClient->keyUp(key, mask); +	LLVoiceClient::getInstance()->keyUp(key, mask);  	return FALSE;  } @@ -1955,7 +1949,7 @@ void LLViewerWindow::setNormalControlsVisible( BOOL visible )  		// ...and set the menu color appropriately.  		setMenuBackgroundColor(gAgent.getGodLevel() > GOD_NOT,  -			LLViewerLogin::getInstance()->isInProductionGrid()); +			LLGridManager::getInstance()->isInProductionGrid());  	}  	if ( gStatusBar ) @@ -1976,15 +1970,15 @@ void LLViewerWindow::setMenuBackgroundColor(bool god_mode, bool dev_grid)      LLSD args;      LLColor4 new_bg_color; -    if(god_mode && LLViewerLogin::getInstance()->isInProductionGrid()) +    if(god_mode && LLGridManager::getInstance()->isInProductionGrid())      {          new_bg_color = LLUIColorTable::instance().getColor( "MenuBarGodBgColor" );      } -    else if(god_mode && !LLViewerLogin::getInstance()->isInProductionGrid()) +    else if(god_mode && !LLGridManager::getInstance()->isInProductionGrid())      {          new_bg_color = LLUIColorTable::instance().getColor( "MenuNonProductionGodBgColor" );      } -    else if(!god_mode && !LLViewerLogin::getInstance()->isInProductionGrid()) +    else if(!god_mode && !LLGridManager::getInstance()->isInProductionGrid())      {          new_bg_color = LLUIColorTable::instance().getColor( "MenuNonProductionBgColor" );      } @@ -2145,12 +2139,14 @@ void LLViewerWindow::draw()  // Takes a single keydown event, usually when UI is visible  BOOL LLViewerWindow::handleKey(KEY key, MASK mask)  { +	// hide tooltips on keypress +	LLToolTipMgr::instance().blockToolTips(); +  	if (gFocusMgr.getKeyboardFocus()   		&& !(mask & (MASK_CONTROL | MASK_ALT))  		&& !gFocusMgr.getKeystrokesOnly())  	{  		// We have keyboard focus, and it's not an accelerator -  		if (key < 0x80)  		{  			// Not a special key, so likely (we hope) to generate a character.  Let it fall through to character handler first. @@ -2158,68 +2154,48 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)  		}  	} -	// hide tooltips on keypress -	LLToolTipMgr::instance().blockToolTips(); -	 -	// Explicit hack for debug menu. -	if ((MASK_ALT & mask) && -		(MASK_CONTROL & mask) && -		('D' == key || 'd' == key)) +	// let menus handle navigation keys for navigation +	if ((gMenuBarView && gMenuBarView->handleKey(key, mask, TRUE)) +		||(gLoginMenuBarView && gLoginMenuBarView->handleKey(key, mask, TRUE)) +		||(gMenuHolder && gMenuHolder->handleKey(key, mask, TRUE)))  	{ -		toggle_debug_menus(NULL); +		return TRUE;  	} -		// Explicit hack for debug menu. -	if ((mask == (MASK_SHIFT | MASK_CONTROL)) && -		('G' == key || 'g' == key)) +	// give menus a chance to handle modified (Ctrl, Alt) shortcut keys before current focus  +	// as long as focus isn't locked +	if (mask & (MASK_CONTROL | MASK_ALT) && !gFocusMgr.focusLocked())  	{ -		if  (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP)  //on splash page +		if ((gMenuBarView && gMenuBarView->handleAcceleratorKey(key, mask)) +			||(gLoginMenuBarView && gLoginMenuBarView->handleAcceleratorKey(key, mask)))  		{ -			BOOL visible = ! gSavedSettings.getBOOL("ForceShowGrid"); -			gSavedSettings.setBOOL("ForceShowGrid", visible); - -			// Initialize visibility (and don't force visibility - use prefs) -			LLPanelLogin::refreshLocation( false ); +			return TRUE;  		}  	} -	// Debugging view for unified notifications: CTRL-SHIFT-5 -	// *FIXME: Having this special-cased right here (just so this can be invoked from the login screen) sucks. -	if ((MASK_SHIFT & mask)  -	    && (!(MASK_ALT & mask)) -	    && (MASK_CONTROL & mask) -	    && ('5' == key)) +	// give floaters first chance to handle TAB key +	// so frontmost floater gets focus +	// if nothing has focus, go to first or last UI element as appropriate +	if (key == KEY_TAB && (mask & MASK_CONTROL || gFocusMgr.getKeyboardFocus() == NULL))  	{ -		//LLFloaterNotificationConsole::showInstance(); -		LLFloaterReg::showInstance("notifications_console"); -		return TRUE; -	} - -	// handle escape key -	//if (key == KEY_ESCAPE && mask == MASK_NONE) -	//{ +		if (gMenuHolder) gMenuHolder->hideMenus(); -		// *TODO: get this to play well with mouselook and hidden -		// cursor modes, etc, and re-enable. -		//if (gFocusMgr.getMouseCapture()) -		//{ -		//	gFocusMgr.setMouseCapture(NULL); -		//	return TRUE; -		//} -	//} +		// if CTRL-tabbing (and not just TAB with no focus), go into window cycle mode +		gFloaterView->setCycleMode((mask & MASK_CONTROL) != 0); -	// let menus handle navigation keys -	if (gMenuBarView && gMenuBarView->handleKey(key, mask, TRUE)) -	{ -		return TRUE; -	} -	// let menus handle navigation keys -	if (gLoginMenuBarView && gLoginMenuBarView->handleKey(key, mask, TRUE)) -	{ +		// do CTRL-TAB and CTRL-SHIFT-TAB logic +		if (mask & MASK_SHIFT) +		{ +			mRootView->focusPrevRoot(); +		} +		else +		{ +			mRootView->focusNextRoot(); +		}  		return TRUE;  	} -	//some of context menus use this container, let context menu handle navigation keys -	if(gMenuHolder && gMenuHolder->handleKey(key, mask, TRUE)) +	// hidden edit menu for cut/copy/paste +	if (gEditMenu && gEditMenu->handleAcceleratorKey(key, mask))  	{  		return TRUE;  	} @@ -2284,50 +2260,10 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)  		return TRUE;  	} -	// Topmost view gets a chance before the hierarchy -	// *FIX: get rid of this? -	//LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl(); -	//if (top_ctrl) -	//{ -	//	if( top_ctrl->handleKey( key, mask, TRUE ) ) -	//	{ -	//		return TRUE; -	//	} -	//} - -	// give floaters first chance to handle TAB key -	// so frontmost floater gets focus -	if (key == KEY_TAB) -	{ -		// if nothing has focus, go to first or last UI element as appropriate -		if (mask & MASK_CONTROL || gFocusMgr.getKeyboardFocus() == NULL) -		{ -			if (gMenuHolder) gMenuHolder->hideMenus(); - -			// if CTRL-tabbing (and not just TAB with no focus), go into window cycle mode -			gFloaterView->setCycleMode((mask & MASK_CONTROL) != 0); -			// do CTRL-TAB and CTRL-SHIFT-TAB logic -			if (mask & MASK_SHIFT) -			{ -				mRootView->focusPrevRoot(); -			} -			else -			{ -				mRootView->focusNextRoot(); -			} -			return TRUE; -		} -	} -	 -	// give menus a chance to handle keys -	if (gMenuBarView && gMenuBarView->handleAcceleratorKey(key, mask)) -	{ -		return TRUE; -	} -	 -	// give menus a chance to handle keys -	if (gLoginMenuBarView && gLoginMenuBarView->handleAcceleratorKey(key, mask)) +	// give menus a chance to handle unmodified accelerator keys +	if ((gMenuBarView && gMenuBarView->handleAcceleratorKey(key, mask)) +		||(gLoginMenuBarView && gLoginMenuBarView->handleAcceleratorKey(key, mask)))  	{  		return TRUE;  	} diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index c400e8510e..540cb47710 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -1266,7 +1266,7 @@ void LLVOAvatar::initInstance(void)  	//VTPause();  // VTune -	mVoiceVisualizer->setVoiceEnabled( gVoiceClient->getVoiceEnabled( mID ) ); +	mVoiceVisualizer->setVoiceEnabled( LLVoiceClient::getInstance()->getVoiceEnabled( mID ) );  }  const LLVector3 LLVOAvatar::getRenderPosition() const @@ -2197,8 +2197,8 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)  	}  	static LLUICachedControl<bool> visualizers_in_calls("ShowVoiceVisualizersInCalls", false); -	bool voice_enabled = (visualizers_in_calls || gVoiceClient->inProximalChannel()) && -						 gVoiceClient->getVoiceEnabled(mID); +	bool voice_enabled = (visualizers_in_calls || LLVoiceClient::getInstance()->inProximalChannel()) && +						 LLVoiceClient::getInstance()->getVoiceEnabled(mID);  	idleUpdateVoiceVisualizer( voice_enabled );  	idleUpdateMisc( detailed_update ); @@ -2261,7 +2261,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled)  		// Notice the calls to "gAwayTimer.reset()". This resets the timer that determines how long the avatar has been  		// "away", so that the avatar doesn't lapse into away-mode (and slump over) while the user is still talking.   		//----------------------------------------------------------------------------------------------------------------- -		if (gVoiceClient->getIsSpeaking( mID )) +		if (LLVoiceClient::getInstance()->getIsSpeaking( mID ))  		{		  			if (!mVoiceVisualizer->getCurrentlySpeaking())  			{ @@ -2270,7 +2270,7 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled)  				//printf( "gAwayTimer.reset();\n" );  			} -			mVoiceVisualizer->setSpeakingAmplitude( gVoiceClient->getCurrentPower( mID ) ); +			mVoiceVisualizer->setSpeakingAmplitude( LLVoiceClient::getInstance()->getCurrentPower( mID ) );  			if( isSelf() )  			{ @@ -2499,7 +2499,7 @@ F32 LLVOAvatar::calcMorphAmount()  void LLVOAvatar::idleUpdateLipSync(bool voice_enabled)  {  	// Use the Lipsync_Ooh and Lipsync_Aah morphs for lip sync -	if ( voice_enabled && (gVoiceClient->lipSyncEnabled()) && gVoiceClient->getIsSpeaking( mID ) ) +	if ( voice_enabled && (LLVoiceClient::getInstance()->lipSyncEnabled()) && LLVoiceClient::getInstance()->getIsSpeaking( mID ) )  	{  		F32 ooh_morph_amount = 0.0f;  		F32 aah_morph_amount = 0.0f; @@ -2929,14 +2929,7 @@ void LLVOAvatar::idleUpdateNameTag(const LLVector3& root_pos_last)  			}  			else  			{ -				if (gSavedSettings.getBOOL("SmallAvatarNames")) -				{ -					mNameText->setFont(LLFontGL::getFontSansSerif()); -				} -				else -				{ -					mNameText->setFont(LLFontGL::getFontSansSerifBig()); -				} +				mNameText->setFont(LLFontGL::getFontSansSerif());  				mNameText->setTextAlignment(LLHUDText::ALIGN_TEXT_CENTER);  				mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f);  				mNameText->setVisibleOffScreen(FALSE); diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index fe6990eae9..c15dbeb8c6 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -87,14 +87,14 @@ using namespace LLVOAvatarDefines;  struct LocalTextureData  {  	LocalTextureData() :  -		mIsBakedReady(FALSE),  +		mIsBakedReady(false),   		mDiscard(MAX_DISCARD_LEVEL+1),   		mImage(NULL),   		mWearableID(IMG_DEFAULT_AVATAR),  		mTexEntry(NULL)  	{}  	LLPointer<LLViewerFetchedTexture> mImage; -	BOOL mIsBakedReady; +	bool mIsBakedReady;  	S32 mDiscard;  	LLUUID mWearableID;	// UUID of the wearable that this texture belongs to, not of the image itself  	LLTextureEntry *mTexEntry; diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h index 4960d4d103..4856e82275 100644 --- a/indra/newview/llvoavatarself.h +++ b/indra/newview/llvoavatarself.h @@ -124,12 +124,10 @@ public:  public:  	/*virtual*/ BOOL    updateIsFullyLoaded();  private: -	BOOL                mIsBaked; // are the stored baked textures up to date?  	//--------------------------------------------------------------------  	// Region state  	//-------------------------------------------------------------------- -private:  	U64				mLastRegionHandle;  	LLFrameTimer	mRegionCrossingTimer;  	S32				mRegionCrossingCount; diff --git a/indra/newview/llvoicechannel.cpp b/indra/newview/llvoicechannel.cpp index fac7fa6a18..338bc12f04 100644 --- a/indra/newview/llvoicechannel.cpp +++ b/indra/newview/llvoicechannel.cpp @@ -72,9 +72,9 @@ private:  void LLVoiceCallCapResponder::error(U32 status, const std::string& reason)  { -	llwarns << "LLVoiceCallCapResponder::error(" +	LL_WARNS("Voice") << "LLVoiceCallCapResponder::error("  		<< status << ": " << reason << ")" -		<< llendl; +		<< LL_ENDL;  	LLVoiceChannel* channelp = LLVoiceChannel::getChannelByID(mSessionID);  	if ( channelp )  	{ @@ -104,8 +104,8 @@ void LLVoiceCallCapResponder::result(const LLSD& content)  		LLSD::map_const_iterator iter;  		for(iter = content.beginMap(); iter != content.endMap(); ++iter)  		{ -			llinfos << "LLVoiceCallCapResponder::result got "  -				<< iter->first << llendl; +			LL_DEBUGS("Voice") << "LLVoiceCallCapResponder::result got "  +				<< iter->first << LL_ENDL;  		}  		channelp->setChannelInfo( @@ -131,10 +131,8 @@ LLVoiceChannel::LLVoiceChannel(const LLUUID& session_id, const std::string& sess  	{  		// a voice channel already exists for this session id, so this instance will be orphaned  		// the end result should simply be the failure to make voice calls -		llwarns << "Duplicate voice channels registered for session_id " << session_id << llendl; +		LL_WARNS("Voice") << "Duplicate voice channels registered for session_id " << session_id << LL_ENDL;  	} - -	LLVoiceClient::getInstance()->addObserver(this);  }  LLVoiceChannel::~LLVoiceChannel() @@ -145,7 +143,7 @@ LLVoiceChannel::~LLVoiceChannel()  	// later in other destructors anyway). EXT-5524  	if(LLVoiceClient::instanceExists())  	{ -		gVoiceClient->removeObserver(this); +		LLVoiceClient::getInstance()->removeObserver(this);  	}  	sVoiceChannelMap.erase(mSessionID); @@ -165,13 +163,13 @@ void LLVoiceChannel::setChannelInfo(  		if (mURI.empty())  		{  			LLNotificationsUtil::add("VoiceChannelJoinFailed", mNotifyArgs); -			llwarns << "Received empty URI for channel " << mSessionName << llendl; +			LL_WARNS("Voice") << "Received empty URI for channel " << mSessionName << LL_ENDL;  			deactivate();  		}  		else if (mCredentials.empty())  		{  			LLNotificationsUtil::add("VoiceChannelJoinFailed", mNotifyArgs); -			llwarns << "Received empty credentials for channel " << mSessionName << llendl; +			LL_WARNS("Voice") << "Received empty credentials for channel " << mSessionName << LL_ENDL;  			deactivate();  		}  		else @@ -286,13 +284,14 @@ void LLVoiceChannel::deactivate()  		//Default mic is OFF when leaving voice calls  		if (gSavedSettings.getBOOL("AutoDisengageMic") &&   			sCurrentVoiceChannel == this && -			gVoiceClient->getUserPTTState()) +			LLVoiceClient::getInstance()->getUserPTTState())  		{  			gSavedSettings.setBOOL("PTTCurrentlyEnabled", true); -			gVoiceClient->inputUserControlState(true); +			LLVoiceClient::getInstance()->inputUserControlState(true);  		}  	} - +	LLVoiceClient::getInstance()->removeObserver(this); +	  	if (sCurrentVoiceChannel == this)  	{  		// default channel is proximal channel @@ -332,7 +331,9 @@ void LLVoiceChannel::activate()  	{  		setState(STATE_CALL_STARTED);  	} - +	 +	LLVoiceClient::getInstance()->addObserver(this); +	  	//do not send earlier, channel should be initialized, should not be in STATE_NO_CHANNEL_INFO state  	sCurrentVoiceChannelChangedSignal(this->mSessionID);  } @@ -374,6 +375,11 @@ LLVoiceChannel* LLVoiceChannel::getChannelByURI(std::string uri)  	}  } +LLVoiceChannel* LLVoiceChannel::getCurrentVoiceChannel() +{ +	return sCurrentVoiceChannel; +} +  void LLVoiceChannel::updateSessionID(const LLUUID& new_session_id)  {  	sVoiceChannelMap.erase(sVoiceChannelMap.find(mSessionID)); @@ -425,7 +431,6 @@ void LLVoiceChannel::initClass()  	sCurrentVoiceChannel = LLVoiceChannelProximal::getInstance();  } -  //static   void LLVoiceChannel::suspend()  { @@ -441,7 +446,7 @@ void LLVoiceChannel::resume()  {  	if (sSuspended)  	{ -		if (gVoiceClient->voiceEnabled()) +		if (LLVoiceClient::getInstance()->voiceEnabled())  		{  			if (sSuspendedVoiceChannel)  			{ @@ -511,9 +516,9 @@ void LLVoiceChannelGroup::activate()  #endif  		//Mic default state is OFF on initiating/joining Ad-Hoc/Group calls -		if (gVoiceClient->getUserPTTState() && gVoiceClient->getPTTIsToggle()) +		if (LLVoiceClient::getInstance()->getUserPTTState() && LLVoiceClient::getInstance()->getPTTIsToggle())  		{ -			gVoiceClient->inputUserControlState(true); +			LLVoiceClient::getInstance()->inputUserControlState(true);  		}  	} @@ -560,7 +565,7 @@ void LLVoiceChannelGroup::setChannelInfo(  		else  		{  			//*TODO: notify user -			llwarns << "Received invalid credentials for channel " << mSessionName << llendl; +			LL_WARNS("Voice") << "Received invalid credentials for channel " << mSessionName << LL_ENDL;  			deactivate();  		}  	} @@ -659,7 +664,6 @@ void LLVoiceChannelGroup::setState(EState state)  LLVoiceChannelProximal::LLVoiceChannelProximal() :   	LLVoiceChannel(LLUUID::null, LLStringUtil::null)  { -	activate();  }  BOOL LLVoiceChannelProximal::isActive() @@ -671,13 +675,13 @@ void LLVoiceChannelProximal::activate()  {  	if (callStarted()) return; -	LLVoiceChannel::activate(); - -	if (callStarted()) +	if((LLVoiceChannel::sCurrentVoiceChannel != this) && (LLVoiceChannel::getState() == STATE_CONNECTED))  	{ -		// this implicitly puts you back in the spatial channel -		LLVoiceClient::getInstance()->leaveNonSpatialChannel(); +		// we're connected to a non-spatial channel, so disconnect. +		LLVoiceClient::getInstance()->leaveNonSpatialChannel();	  	} +	LLVoiceChannel::activate(); +	  }  void LLVoiceChannelProximal::onChange(EStatusType type, const std::string &channelURI, bool proximal) @@ -707,7 +711,7 @@ void LLVoiceChannelProximal::handleStatusChange(EStatusType status)  		return;  	case STATUS_VOICE_DISABLED:  		//skip showing "Voice not available at your current location" when agent voice is disabled (EXT-4749) -		if(LLVoiceClient::voiceEnabled() && gVoiceClient->voiceWorking()) +		if(LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking())  		{  			//TODO: remove or redirect this call status notification  //			LLCallInfoDialog::show("unavailable", mNotifyArgs); @@ -767,7 +771,7 @@ LLVoiceChannelP2P::LLVoiceChannelP2P(const LLUUID& session_id, const std::string  void LLVoiceChannelP2P::handleStatusChange(EStatusType type)  { -	llinfos << "P2P CALL CHANNEL STATUS CHANGE: incoming=" << int(mReceivedCall) << " newstatus=" << LLVoiceClientStatusObserver::status2string(type) << " (mState=" << mState << ")" << llendl; +	LL_INFOS("Voice") << "P2P CALL CHANNEL STATUS CHANGE: incoming=" << int(mReceivedCall) << " newstatus=" << LLVoiceClientStatusObserver::status2string(type) << " (mState=" << mState << ")" << LL_ENDL;  	// status updates  	switch(type) @@ -841,9 +845,9 @@ void LLVoiceChannelP2P::activate()  		LLRecentPeople::instance().add(mOtherUserID);  		//Default mic is ON on initiating/joining P2P calls -		if (!gVoiceClient->getUserPTTState() && gVoiceClient->getPTTIsToggle()) +		if (!LLVoiceClient::getInstance()->getUserPTTState() && LLVoiceClient::getInstance()->getPTTIsToggle())  		{ -			gVoiceClient->inputUserControlState(true); +			LLVoiceClient::getInstance()->inputUserControlState(true);  		}  	}  } @@ -906,7 +910,7 @@ void LLVoiceChannelP2P::setSessionHandle(const std::string& handle, const std::s  void LLVoiceChannelP2P::setState(EState state)  { -	llinfos << "P2P CALL STATE CHANGE: incoming=" << int(mReceivedCall) << " oldstate=" << mState << " newstate=" << state << llendl; +	LL_INFOS("Voice") << "P2P CALL STATE CHANGE: incoming=" << int(mReceivedCall) << " oldstate=" << mState << " newstate=" << state << LL_ENDL;  	if (mReceivedCall) // incoming call  	{ diff --git a/indra/newview/llvoicechannel.h b/indra/newview/llvoicechannel.h index 941cccacc3..573fab1f4f 100644 --- a/indra/newview/llvoicechannel.h +++ b/indra/newview/llvoicechannel.h @@ -98,7 +98,8 @@ public:  	static LLVoiceChannel* getChannelByID(const LLUUID& session_id);  	static LLVoiceChannel* getChannelByURI(std::string uri); -	static LLVoiceChannel* getCurrentVoiceChannel() { return sCurrentVoiceChannel; } +	static LLVoiceChannel* getCurrentVoiceChannel(); +	  	static void initClass();  	static void suspend(); diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 2238acd643..e067754e3e 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -1,6 +1,6 @@   /**    * @file llvoiceclient.cpp - * @brief Implementation of LLVoiceClient class which is the interface to the voice client process. + * @brief Voice client delegation class implementation.   *   * $LicenseInfo:firstyear=2001&license=viewergpl$   *  @@ -17,8 +17,7 @@   * There are special exceptions to the terms and conditions of the GPL as   * it is applied to this Source Code. View the full text of the exception   * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception   *    * By copying, modifying or distributing this software, you acknowledge   * that you have read and understood your obligations described above, @@ -32,5421 +31,312 @@  #include "llviewerprecompiledheaders.h"  #include "llvoiceclient.h" - -#include <boost/tokenizer.hpp> - -// library includes -#include "llnotificationsutil.h" -#include "llsdserialize.h" -#include "llsdutil.h" - - -// project includes -#include "llvoavatar.h" -#include "llbufferstream.h" -#include "llfile.h" -#ifdef LL_STANDALONE -# include "expat.h" -#else -# include "expat/expat.h" -#endif -#include "llcallbacklist.h" -#include "llcallingcard.h"   // for LLFriendObserver -#include "llviewerregion.h" -#include "llviewernetwork.h"		// for gGridChoice -#include "llbase64.h"  #include "llviewercontrol.h" -#include "llkeyboard.h" -#include "llappviewer.h"	// for gDisconnected, gDisableVoice -#include "llmutelist.h"  // to check for muted avatars -#include "llagent.h" -#include "llvoavatarself.h" -#include "llcachename.h" -#include "llimview.h" // for LLIMMgr -#include "llparcel.h" -#include "llviewerparcelmgr.h" -//#include "llfirstuse.h" -#include "llspeakers.h" -#include "lltrans.h"  #include "llviewerwindow.h" -#include "llviewercamera.h" -#include "llvoavatarself.h" -#include "llvoicechannel.h" - -// for base64 decoding -#include "apr_base64.h" - -// for SHA1 hash -#include "apr_sha1.h" - -// for MD5 hash -#include "llmd5.h" - -#define USE_SESSION_GROUPS 0 - -static bool sConnectingToAgni = false; -F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f; - -const F32 LLVoiceClient::VOLUME_MIN = 0.f; -const F32 LLVoiceClient::VOLUME_DEFAULT = 0.5f; -const F32 LLVoiceClient::VOLUME_MAX = 1.0f; - -const F32 VOLUME_SCALE_VIVOX = 0.01f; - -const F32 SPEAKING_TIMEOUT = 1.f; - -const int VOICE_MAJOR_VERSION = 1; -const int VOICE_MINOR_VERSION = 0; - -LLVoiceClient *gVoiceClient = NULL; - -// Don't retry connecting to the daemon more frequently than this: -const F32 CONNECT_THROTTLE_SECONDS = 1.0f; - -// Don't send positional updates more frequently than this: -const F32 UPDATE_THROTTLE_SECONDS = 0.1f; - -const F32 LOGIN_RETRY_SECONDS = 10.0f; -const int MAX_LOGIN_RETRIES = 12; - -// Defines the maximum number of times(in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine() -// which is treated as normal. If this number is exceeded we suspect there is a problem with connection -// to voice server (EXT-4313). When voice works correctly, there is from 1 to 15 times. 50 was chosen  -// to make sure we don't make mistake when slight connection problems happen- situation when connection to server is  -// blocked is VERY rare and it's better to sacrifice response time in this situation for the sake of stability. -const int MAX_NORMAL_JOINING_SPATIAL_NUM = 50; - -static void setUUIDFromStringHash(LLUUID &uuid, const std::string &str) -{ -	LLMD5 md5_uuid; -	md5_uuid.update((const unsigned char*)str.data(), str.size()); -	md5_uuid.finalize(); -	md5_uuid.raw_digest(uuid.mData); -} - -static int scale_mic_volume(float volume) -{ -	// incoming volume has the range [0.0 ... 2.0], with 1.0 as the default. -	// Map it to Vivox levels as follows: 0.0 -> 30, 1.0 -> 50, 2.0 -> 70 -	return 30 + (int)(volume * 20.0f); -} - -static int scale_speaker_volume(float volume) -{ -	// incoming volume has the range [0.0 ... 1.0], with 0.5 as the default. -	// Map it to Vivox levels as follows: 0.0 -> 30, 0.5 -> 50, 1.0 -> 70 -	return 30 + (int)(volume * 40.0f); -} - -class LLViewerVoiceAccountProvisionResponder : -	public LLHTTPClient::Responder -{ -public: -	LLViewerVoiceAccountProvisionResponder(int retries) -	{ -		mRetries = retries; -	} - -	virtual void error(U32 status, const std::string& reason) -	{ -		if ( mRetries > 0 ) -		{ -			LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, retrying.  status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL; -			if ( gVoiceClient ) gVoiceClient->requestVoiceAccountProvision( -				mRetries - 1); -		} -		else -		{ -			LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, too many retries (giving up).  status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL; -			if ( gVoiceClient ) gVoiceClient->giveUp(); -		} -	} - -	virtual void result(const LLSD& content) -	{ -		if ( gVoiceClient ) -		{ -			std::string voice_sip_uri_hostname; -			std::string voice_account_server_uri; -			 -			LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response:" << ll_pretty_print_sd(content) << LL_ENDL; -			 -			if(content.has("voice_sip_uri_hostname")) -				voice_sip_uri_hostname = content["voice_sip_uri_hostname"].asString(); -			 -			// this key is actually misnamed -- it will be an entire URI, not just a hostname. -			if(content.has("voice_account_server_name")) -				voice_account_server_uri = content["voice_account_server_name"].asString(); -			 -			gVoiceClient->login( -				content["username"].asString(), -				content["password"].asString(), -				voice_sip_uri_hostname, -				voice_account_server_uri); -		} -	} - -private: -	int mRetries; -}; - -/**  - * @class LLVivoxProtocolParser - * @brief This class helps construct new LLIOPipe specializations - * @see LLIOPipe - * - * THOROUGH_DESCRIPTION - */ -class LLVivoxProtocolParser : public LLIOPipe -{ -	LOG_CLASS(LLVivoxProtocolParser); -public: -	LLVivoxProtocolParser(); -	virtual ~LLVivoxProtocolParser(); - -protected: -	/* @name LLIOPipe virtual implementations -	 */ -	//@{ -	/**  -	 * @brief Process the data in buffer -	 */ -	virtual EStatus process_impl( -		const LLChannelDescriptors& channels, -		buffer_ptr_t& buffer, -		bool& eos, -		LLSD& context, -		LLPumpIO* pump); -	//@} -	 -	std::string 	mInput; -	 -	// Expat control members -	XML_Parser		parser; -	int				responseDepth; -	bool			ignoringTags; -	bool			isEvent; -	int				ignoreDepth; - -	// Members for processing responses. The values are transient and only valid within a call to processResponse(). -	bool			squelchDebugOutput; -	int				returnCode; -	int				statusCode; -	std::string		statusString; -	std::string		requestId; -	std::string		actionString; -	std::string		connectorHandle; -	std::string		versionID; -	std::string		accountHandle; -	std::string		sessionHandle; -	std::string		sessionGroupHandle; -	std::string		alias; -	std::string		applicationString; - -	// Members for processing events. The values are transient and only valid within a call to processResponse(). -	std::string		eventTypeString; -	int				state; -	std::string		uriString; -	bool			isChannel; -	bool			incoming; -	bool			enabled; -	std::string		nameString; -	std::string		audioMediaString; -	std::string		displayNameString; -	std::string		deviceString; -	int				participantType; -	bool			isLocallyMuted; -	bool			isModeratorMuted; -	bool			isSpeaking; -	int				volume; -	F32				energy; -	std::string		messageHeader; -	std::string		messageBody; -	std::string		notificationType; -	bool			hasText; -	bool			hasAudio; -	bool			hasVideo; -	bool			terminated; -	std::string		blockMask; -	std::string		presenceOnly; -	std::string		autoAcceptMask; -	std::string		autoAddAsBuddy; -	int				numberOfAliases; -	std::string		subscriptionHandle; -	std::string		subscriptionType; -		 - -	// Members for processing text between tags -	std::string		textBuffer; -	bool			accumulateText; -	 -	void			reset(); - -	void			processResponse(std::string tag); - -static void XMLCALL ExpatStartTag(void *data, const char *el, const char **attr); -static void XMLCALL ExpatEndTag(void *data, const char *el); -static void XMLCALL ExpatCharHandler(void *data, const XML_Char *s, int len); - -	void			StartTag(const char *tag, const char **attr); -	void			EndTag(const char *tag); -	void			CharData(const char *buffer, int length); -	 -}; - -LLVivoxProtocolParser::LLVivoxProtocolParser() -{ -	parser = NULL; -	parser = XML_ParserCreate(NULL); -	 -	reset(); -} - -void LLVivoxProtocolParser::reset() -{ -	responseDepth = 0; -	ignoringTags = false; -	accumulateText = false; -	energy = 0.f; -	hasText = false; -	hasAudio = false; -	hasVideo = false; -	terminated = false; -	ignoreDepth = 0; -	isChannel = false; -	incoming = false; -	enabled = false; -	isEvent = false; -	isLocallyMuted = false; -	isModeratorMuted = false; -	isSpeaking = false; -	participantType = 0; -	squelchDebugOutput = false; -	returnCode = -1; -	state = 0; -	statusCode = 0; -	volume = 0; -	textBuffer.clear(); -	alias.clear(); -	numberOfAliases = 0; -	applicationString.clear(); -} - -//virtual  -LLVivoxProtocolParser::~LLVivoxProtocolParser() -{ -	if (parser) -		XML_ParserFree(parser); -} - -// virtual -LLIOPipe::EStatus LLVivoxProtocolParser::process_impl( -	const LLChannelDescriptors& channels, -	buffer_ptr_t& buffer, -	bool& eos, -	LLSD& context, -	LLPumpIO* pump) -{ -	LLBufferStream istr(channels, buffer.get()); -	std::ostringstream ostr; -	while (istr.good()) -	{ -		char buf[1024]; -		istr.read(buf, sizeof(buf)); -		mInput.append(buf, istr.gcount()); -	} -	 -	// Look for input delimiter(s) in the input buffer.  If one is found, send the message to the xml parser. -	int start = 0; -	int delim; -	while((delim = mInput.find("\n\n\n", start)) != std::string::npos) -	{	 -		 -		// Reset internal state of the LLVivoxProtocolParser (no effect on the expat parser) -		reset(); -		 -		XML_ParserReset(parser, NULL); -		XML_SetElementHandler(parser, ExpatStartTag, ExpatEndTag); -		XML_SetCharacterDataHandler(parser, ExpatCharHandler); -		XML_SetUserData(parser, this);	 -		XML_Parse(parser, mInput.data() + start, delim - start, false); -		 -		// If this message isn't set to be squelched, output the raw XML received. -		if(!squelchDebugOutput) -		{ -			LL_DEBUGS("Voice") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL; -		} -		 -		start = delim + 3; -	} -	 -	if(start != 0) -		mInput = mInput.substr(start); - -	LL_DEBUGS("VivoxProtocolParser") << "at end, mInput is: " << mInput << LL_ENDL; -	 -	if(!gVoiceClient->mConnected) -	{ -		// If voice has been disabled, we just want to close the socket.  This does so. -		LL_INFOS("Voice") << "returning STATUS_STOP" << LL_ENDL; -		return STATUS_STOP; -	} -	 -	return STATUS_OK; -} - -void XMLCALL LLVivoxProtocolParser::ExpatStartTag(void *data, const char *el, const char **attr) -{ -	if (data) -	{ -		LLVivoxProtocolParser	*object = (LLVivoxProtocolParser*)data; -		object->StartTag(el, attr); -	} -} - -// -------------------------------------------------------------------------------- - -void XMLCALL LLVivoxProtocolParser::ExpatEndTag(void *data, const char *el) -{ -	if (data) -	{ -		LLVivoxProtocolParser	*object = (LLVivoxProtocolParser*)data; -		object->EndTag(el); -	} -} - -// -------------------------------------------------------------------------------- - -void XMLCALL LLVivoxProtocolParser::ExpatCharHandler(void *data, const XML_Char *s, int len) -{ -	if (data) -	{ -		LLVivoxProtocolParser	*object = (LLVivoxProtocolParser*)data; -		object->CharData(s, len); -	} -} - -// -------------------------------------------------------------------------------- - - -void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr) -{ -	// Reset the text accumulator. We shouldn't have strings that are inturrupted by new tags -	textBuffer.clear(); -	// only accumulate text if we're not ignoring tags. -	accumulateText = !ignoringTags; -	 -	if (responseDepth == 0) -	{	 -		isEvent = !stricmp("Event", tag); -		 -		if (!stricmp("Response", tag) || isEvent) -		{ -			// Grab the attributes -			while (*attr) -			{ -				const char	*key = *attr++; -				const char	*value = *attr++; -				 -				if (!stricmp("requestId", key)) -				{ -					requestId = value; -				} -				else if (!stricmp("action", key)) -				{ -					actionString = value; -				} -				else if (!stricmp("type", key)) -				{ -					eventTypeString = value; -				} -			} -		} -		LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")"  << LL_ENDL; -	} -	else -	{ -		if (ignoringTags) -		{ -			LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; -		} -		else -		{ -			LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")"  << LL_ENDL; -	 -			// Ignore the InputXml stuff so we don't get confused -			if (!stricmp("InputXml", tag)) -			{ -				ignoringTags = true; -				ignoreDepth = responseDepth; -				accumulateText = false; - -				LL_DEBUGS("VivoxProtocolParser") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL; -			} -			else if (!stricmp("CaptureDevices", tag)) -			{ -				gVoiceClient->clearCaptureDevices(); -			} -			else if (!stricmp("RenderDevices", tag)) -			{ -				gVoiceClient->clearRenderDevices(); -			} -			else if (!stricmp("CaptureDevice", tag)) -			{ -				deviceString.clear(); -			} -			else if (!stricmp("RenderDevice", tag)) -			{ -				deviceString.clear(); -			} -			else if (!stricmp("Buddies", tag)) -			{ -				gVoiceClient->deleteAllBuddies(); -			} -			else if (!stricmp("BlockRules", tag)) -			{ -				gVoiceClient->deleteAllBlockRules(); -			} -			else if (!stricmp("AutoAcceptRules", tag)) -			{ -				gVoiceClient->deleteAllAutoAcceptRules(); -			} -			 -		} -	} -	responseDepth++; -} - -// -------------------------------------------------------------------------------- - -void LLVivoxProtocolParser::EndTag(const char *tag) -{ -	const std::string& string = textBuffer; - -	responseDepth--; - -	if (ignoringTags) -	{ -		if (ignoreDepth == responseDepth) -		{ -			LL_DEBUGS("VivoxProtocolParser") << "end of ignore" << LL_ENDL; -			ignoringTags = false; -		} -		else -		{ -			LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; -		} -	} -	 -	if (!ignoringTags) -	{ -		LL_DEBUGS("VivoxProtocolParser") << "processing tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; - -		// Closing a tag. Finalize the text we've accumulated and reset -		if (!stricmp("ReturnCode", tag)) -			returnCode = strtol(string.c_str(), NULL, 10); -		else if (!stricmp("SessionHandle", tag)) -			sessionHandle = string; -		else if (!stricmp("SessionGroupHandle", tag)) -			sessionGroupHandle = string; -		else if (!stricmp("StatusCode", tag)) -			statusCode = strtol(string.c_str(), NULL, 10); -		else if (!stricmp("StatusString", tag)) -			statusString = string; -		else if (!stricmp("ParticipantURI", tag)) -			uriString = string; -		else if (!stricmp("Volume", tag)) -			volume = strtol(string.c_str(), NULL, 10); -		else if (!stricmp("Energy", tag)) -			energy = (F32)strtod(string.c_str(), NULL); -		else if (!stricmp("IsModeratorMuted", tag)) -			isModeratorMuted = !stricmp(string.c_str(), "true"); -		else if (!stricmp("IsSpeaking", tag)) -			isSpeaking = !stricmp(string.c_str(), "true"); -		else if (!stricmp("Alias", tag)) -			alias = string; -		else if (!stricmp("NumberOfAliases", tag)) -			numberOfAliases = strtol(string.c_str(), NULL, 10); -		else if (!stricmp("Application", tag)) -			applicationString = string; -		else if (!stricmp("ConnectorHandle", tag)) -			connectorHandle = string; -		else if (!stricmp("VersionID", tag)) -			versionID = string; -		else if (!stricmp("AccountHandle", tag)) -			accountHandle = string; -		else if (!stricmp("State", tag)) -			state = strtol(string.c_str(), NULL, 10); -		else if (!stricmp("URI", tag)) -			uriString = string; -		else if (!stricmp("IsChannel", tag)) -			isChannel = !stricmp(string.c_str(), "true"); -		else if (!stricmp("Incoming", tag)) -			incoming = !stricmp(string.c_str(), "true"); -		else if (!stricmp("Enabled", tag)) -			enabled = !stricmp(string.c_str(), "true"); -		else if (!stricmp("Name", tag)) -			nameString = string; -		else if (!stricmp("AudioMedia", tag)) -			audioMediaString = string; -		else if (!stricmp("ChannelName", tag)) -			nameString = string; -		else if (!stricmp("DisplayName", tag)) -			displayNameString = string; -		else if (!stricmp("Device", tag)) -			deviceString = string; -		else if (!stricmp("AccountName", tag)) -			nameString = string; -		else if (!stricmp("ParticipantType", tag)) -			participantType = strtol(string.c_str(), NULL, 10); -		else if (!stricmp("IsLocallyMuted", tag)) -			isLocallyMuted = !stricmp(string.c_str(), "true"); -		else if (!stricmp("MicEnergy", tag)) -			energy = (F32)strtod(string.c_str(), NULL); -		else if (!stricmp("ChannelName", tag)) -			nameString = string; -		else if (!stricmp("ChannelURI", tag)) -			uriString = string; -		else if (!stricmp("BuddyURI", tag)) -			uriString = string; -		else if (!stricmp("Presence", tag)) -			statusString = string; -		else if (!stricmp("CaptureDevice", tag)) -		{ -			gVoiceClient->addCaptureDevice(deviceString); -		} -		else if (!stricmp("RenderDevice", tag)) -		{ -			gVoiceClient->addRenderDevice(deviceString); -		} -		else if (!stricmp("Buddy", tag)) -		{ -			gVoiceClient->processBuddyListEntry(uriString, displayNameString); -		} -		else if (!stricmp("BlockRule", tag)) -		{ -			gVoiceClient->addBlockRule(blockMask, presenceOnly); -		} -		else if (!stricmp("BlockMask", tag)) -			blockMask = string; -		else if (!stricmp("PresenceOnly", tag)) -			presenceOnly = string; -		else if (!stricmp("AutoAcceptRule", tag)) -		{ -			gVoiceClient->addAutoAcceptRule(autoAcceptMask, autoAddAsBuddy); -		} -		else if (!stricmp("AutoAcceptMask", tag)) -			autoAcceptMask = string; -		else if (!stricmp("AutoAddAsBuddy", tag)) -			autoAddAsBuddy = string; -		else if (!stricmp("MessageHeader", tag)) -			messageHeader = string; -		else if (!stricmp("MessageBody", tag)) -			messageBody = string; -		else if (!stricmp("NotificationType", tag)) -			notificationType = string; -		else if (!stricmp("HasText", tag)) -			hasText = !stricmp(string.c_str(), "true"); -		else if (!stricmp("HasAudio", tag)) -			hasAudio = !stricmp(string.c_str(), "true"); -		else if (!stricmp("HasVideo", tag)) -			hasVideo = !stricmp(string.c_str(), "true"); -		else if (!stricmp("Terminated", tag)) -			terminated = !stricmp(string.c_str(), "true"); -		else if (!stricmp("SubscriptionHandle", tag)) -			subscriptionHandle = string; -		else if (!stricmp("SubscriptionType", tag)) -			subscriptionType = string; -		 -		textBuffer.clear(); -		accumulateText= false; -		 -		if (responseDepth == 0) -		{ -			// We finished all of the XML, process the data -			processResponse(tag); -		} -	} -} - -// -------------------------------------------------------------------------------- - -void LLVivoxProtocolParser::CharData(const char *buffer, int length) -{ -	/* -		This method is called for anything that isn't a tag, which can be text you -		want that lies between tags, and a lot of stuff you don't want like file formatting -		(tabs, spaces, CR/LF, etc). -		 -		Only copy text if we are in accumulate mode... -	*/ -	if (accumulateText) -		textBuffer.append(buffer, length); -} - -// -------------------------------------------------------------------------------- - -void LLVivoxProtocolParser::processResponse(std::string tag) -{ -	LL_DEBUGS("VivoxProtocolParser") << tag << LL_ENDL; - -	// SLIM SDK: the SDK now returns a statusCode of "200" (OK) for success.  This is a change vs. previous SDKs. -	// According to Mike S., "The actual API convention is that responses with return codes of 0 are successful, regardless of the status code returned", -	// so I believe this will give correct behavior. -	 -	if(returnCode == 0) -		statusCode = 0; -		 -	if (isEvent) -	{ -		const char *eventTypeCstr = eventTypeString.c_str(); -		if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent")) -		{ -			gVoiceClient->accountLoginStateChangeEvent(accountHandle, statusCode, statusString, state); -		} -		else if (!stricmp(eventTypeCstr, "SessionAddedEvent")) -		{ -			/* -			<Event type="SessionAddedEvent"> -				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle> -				<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle> -				<Uri>sip:confctl-1408789@bhr.vivox.com</Uri> -				<IsChannel>true</IsChannel> -				<Incoming>false</Incoming> -				<ChannelName /> -			</Event> -			*/ -			gVoiceClient->sessionAddedEvent(uriString, alias, sessionHandle, sessionGroupHandle, isChannel, incoming, nameString, applicationString); -		} -		else if (!stricmp(eventTypeCstr, "SessionRemovedEvent")) -		{ -			gVoiceClient->sessionRemovedEvent(sessionHandle, sessionGroupHandle); -		} -		else if (!stricmp(eventTypeCstr, "SessionGroupAddedEvent")) -		{ -			gVoiceClient->sessionGroupAddedEvent(sessionGroupHandle); -		} -		else if (!stricmp(eventTypeCstr, "MediaStreamUpdatedEvent")) -		{ -			/* -			<Event type="MediaStreamUpdatedEvent"> -				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle> -				<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle> -				<StatusCode>200</StatusCode> -				<StatusString>OK</StatusString> -				<State>2</State> -				<Incoming>false</Incoming> -			</Event> -			*/ -			gVoiceClient->mediaStreamUpdatedEvent(sessionHandle, sessionGroupHandle, statusCode, statusString, state, incoming); -		}		 -		else if (!stricmp(eventTypeCstr, "TextStreamUpdatedEvent")) -		{ -			/* -			<Event type="TextStreamUpdatedEvent"> -				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg1</SessionGroupHandle> -				<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==1</SessionHandle> -				<Enabled>true</Enabled> -				<State>1</State> -				<Incoming>true</Incoming> -			</Event> -			*/ -			gVoiceClient->textStreamUpdatedEvent(sessionHandle, sessionGroupHandle, enabled, state, incoming); -		} -		else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent")) -		{ -			/*  -			<Event type="ParticipantAddedEvent"> -				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4</SessionGroupHandle> -				<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==4</SessionHandle> -				<ParticipantUri>sip:xI5auBZ60SJWIk606-1JGRQ==@bhr.vivox.com</ParticipantUri> -				<AccountName>xI5auBZ60SJWIk606-1JGRQ==</AccountName> -				<DisplayName /> -				<ParticipantType>0</ParticipantType> -			</Event> -			*/ -			gVoiceClient->participantAddedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString, displayNameString, participantType); -		} -		else if (!stricmp(eventTypeCstr, "ParticipantRemovedEvent")) -		{ -			/* -			<Event type="ParticipantRemovedEvent"> -				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4</SessionGroupHandle> -				<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==4</SessionHandle> -				<ParticipantUri>sip:xtx7YNV-3SGiG7rA1fo5Ndw==@bhr.vivox.com</ParticipantUri> -				<AccountName>xtx7YNV-3SGiG7rA1fo5Ndw==</AccountName> -			</Event> -			*/ -			gVoiceClient->participantRemovedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString); -		} -		else if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent")) -		{ -			/* -			<Event type="ParticipantUpdatedEvent"> -				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle> -				<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle> -				<ParticipantUri>sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com</ParticipantUri> -				<IsModeratorMuted>false</IsModeratorMuted> -				<IsSpeaking>true</IsSpeaking> -				<Volume>44</Volume> -				<Energy>0.0879437</Energy> -			</Event> -			*/ -			 -			// These happen so often that logging them is pretty useless. -			squelchDebugOutput = true; -			 -			gVoiceClient->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy); -		} -		else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent")) -		{ -			gVoiceClient->auxAudioPropertiesEvent(energy); -		} -		else if (!stricmp(eventTypeCstr, "BuddyPresenceEvent")) -		{ -			gVoiceClient->buddyPresenceEvent(uriString, alias, statusString, applicationString); -		} -		else if (!stricmp(eventTypeCstr, "BuddyAndGroupListChangedEvent")) -		{ -			// The buddy list was updated during parsing. -			// Need to recheck against the friends list. -			gVoiceClient->buddyListChanged(); -		} -		else if (!stricmp(eventTypeCstr, "BuddyChangedEvent")) -		{ -			/* -			<Event type="BuddyChangedEvent"> -				<AccountHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==</AccountHandle> -				<BuddyURI>sip:x9fFHFZjOTN6OESF1DUPrZQ==@bhr.vivox.com</BuddyURI> -				<DisplayName>Monroe Tester</DisplayName> -				<BuddyData /> -				<GroupID>0</GroupID> -				<ChangeType>Set</ChangeType> -			</Event> -			*/		 -			// TODO: Question: Do we need to process this at all? -		} -		else if (!stricmp(eventTypeCstr, "MessageEvent"))   -		{ -			gVoiceClient->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString); -		} -		else if (!stricmp(eventTypeCstr, "SessionNotificationEvent"))   -		{ -			gVoiceClient->sessionNotificationEvent(sessionHandle, uriString, notificationType); -		} -		else if (!stricmp(eventTypeCstr, "SubscriptionEvent"))   -		{ -			gVoiceClient->subscriptionEvent(uriString, subscriptionHandle, alias, displayNameString, applicationString, subscriptionType); -		} -		else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent"))   -		{ -			/* -			<Event type="SessionUpdatedEvent"> -				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle> -				<SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle> -				<Uri>sip:confctl-9@bhd.vivox.com</Uri> -				<IsMuted>0</IsMuted> -				<Volume>50</Volume> -				<TransmitEnabled>1</TransmitEnabled> -				<IsFocused>0</IsFocused> -				<SpeakerPosition><Position><X>0</X><Y>0</Y><Z>0</Z></Position></SpeakerPosition> -				<SessionFontID>0</SessionFontID> -			</Event> -			*/ -			// We don't need to process this, but we also shouldn't warn on it, since that confuses people. -		} -		 -		else if (!stricmp(eventTypeCstr, "SessionGroupRemovedEvent"))   -		{ -			/* -			<Event type="SessionGroupRemovedEvent"> -				<SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle> -			</Event> -			*/ -			// We don't need to process this, but we also shouldn't warn on it, since that confuses people. -		} -		else -		{ -			LL_WARNS("VivoxProtocolParser") << "Unknown event type " << eventTypeString << LL_ENDL; -		} -	} -	else -	{ -		const char *actionCstr = actionString.c_str(); -		if (!stricmp(actionCstr, "Connector.Create.1")) -		{ -			gVoiceClient->connectorCreateResponse(statusCode, statusString, connectorHandle, versionID); -		} -		else if (!stricmp(actionCstr, "Account.Login.1")) -		{ -			gVoiceClient->loginResponse(statusCode, statusString, accountHandle, numberOfAliases); -		} -		else if (!stricmp(actionCstr, "Session.Create.1")) -		{ -			gVoiceClient->sessionCreateResponse(requestId, statusCode, statusString, sessionHandle);			 -		} -		else if (!stricmp(actionCstr, "SessionGroup.AddSession.1")) -		{ -			gVoiceClient->sessionGroupAddSessionResponse(requestId, statusCode, statusString, sessionHandle);			 -		} -		else if (!stricmp(actionCstr, "Session.Connect.1")) -		{ -			gVoiceClient->sessionConnectResponse(requestId, statusCode, statusString);			 -		} -		else if (!stricmp(actionCstr, "Account.Logout.1")) -		{ -			gVoiceClient->logoutResponse(statusCode, statusString);			 -		} -		else if (!stricmp(actionCstr, "Connector.InitiateShutdown.1")) -		{ -			gVoiceClient->connectorShutdownResponse(statusCode, statusString);			 -		} -		else if (!stricmp(actionCstr, "Account.ListBlockRules.1")) -		{ -			gVoiceClient->accountListBlockRulesResponse(statusCode, statusString);						 -		} -		else if (!stricmp(actionCstr, "Account.ListAutoAcceptRules.1")) -		{ -			gVoiceClient->accountListAutoAcceptRulesResponse(statusCode, statusString);						 -		} -		else if (!stricmp(actionCstr, "Session.Set3DPosition.1")) -		{ -			// We don't need to process these, but they're so spammy we don't want to log them. -			squelchDebugOutput = true; -		} -/* -		else if (!stricmp(actionCstr, "Account.ChannelGetList.1")) -		{ -			gVoiceClient->channelGetListResponse(statusCode, statusString); -		} -		else if (!stricmp(actionCstr, "Connector.AccountCreate.1")) -		{ -			 -		} -		else if (!stricmp(actionCstr, "Connector.MuteLocalMic.1")) -		{ -			 -		} -		else if (!stricmp(actionCstr, "Connector.MuteLocalSpeaker.1")) -		{ -			 -		} -		else if (!stricmp(actionCstr, "Connector.SetLocalMicVolume.1")) -		{ -			 -		} -		else if (!stricmp(actionCstr, "Connector.SetLocalSpeakerVolume.1")) -		{ -			 -		} -		else if (!stricmp(actionCstr, "Session.ListenerSetPosition.1")) -		{ -			 -		} -		else if (!stricmp(actionCstr, "Session.SpeakerSetPosition.1")) -		{ -			 -		} -		else if (!stricmp(actionCstr, "Session.AudioSourceSetPosition.1")) -		{ -			 -		} -		else if (!stricmp(actionCstr, "Session.GetChannelParticipants.1")) -		{ -			 -		} -		else if (!stricmp(actionCstr, "Account.ChannelCreate.1")) -		{ -			 -		} -		else if (!stricmp(actionCstr, "Account.ChannelUpdate.1")) -		{ -			 -		} -		else if (!stricmp(actionCstr, "Account.ChannelDelete.1")) -		{ -			 -		} -		else if (!stricmp(actionCstr, "Account.ChannelCreateAndInvite.1")) -		{ -			 -		} -		else if (!stricmp(actionCstr, "Account.ChannelFolderCreate.1")) -		{ -			 -		} -		else if (!stricmp(actionCstr, "Account.ChannelFolderUpdate.1")) -		{ -			 -		} -		else if (!stricmp(actionCstr, "Account.ChannelFolderDelete.1")) -		{ -			 -		} -		else if (!stricmp(actionCstr, "Account.ChannelAddModerator.1")) -		{ -			 -		} -		else if (!stricmp(actionCstr, "Account.ChannelDeleteModerator.1")) -		{ -			 -		} -*/ -	} -} - -/////////////////////////////////////////////////////////////////////////////////////////////// - -class LLVoiceClientMuteListObserver : public LLMuteListObserver -{ -	/* virtual */ void onChange()  { gVoiceClient->muteListChanged();} -}; - -class LLVoiceClientFriendsObserver : public LLFriendObserver -{ -public: -	/* virtual */ void changed(U32 mask) { gVoiceClient->updateFriends(mask);} -}; - -static LLVoiceClientMuteListObserver mutelist_listener; -static bool sMuteListListener_listening = false; - -static LLVoiceClientFriendsObserver *friendslist_listener = NULL; - -/////////////////////////////////////////////////////////////////////////////////////////////// - -class LLVoiceClientCapResponder : public LLHTTPClient::Responder -{ -public: -	LLVoiceClientCapResponder(void){}; - -	virtual void error(U32 status, const std::string& reason);	// called with bad status codes -	virtual void result(const LLSD& content); - -private: -}; - -void LLVoiceClientCapResponder::error(U32 status, const std::string& reason) -{ -	LL_WARNS("Voice") << "LLVoiceClientCapResponder::error(" -		<< status << ": " << reason << ")" -		<< LL_ENDL; -} - -void LLVoiceClientCapResponder::result(const LLSD& content) -{ -	LLSD::map_const_iterator iter; -	 -	LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest response:" << ll_pretty_print_sd(content) << LL_ENDL; - -	if ( content.has("voice_credentials") ) -	{ -		LLSD voice_credentials = content["voice_credentials"]; -		std::string uri; -		std::string credentials; - -		if ( voice_credentials.has("channel_uri") ) -		{ -			uri = voice_credentials["channel_uri"].asString(); -		} -		if ( voice_credentials.has("channel_credentials") ) -		{ -			credentials = -				voice_credentials["channel_credentials"].asString(); -		} - -		gVoiceClient->setSpatialChannel(uri, credentials); -	} -} - - - -#if LL_WINDOWS -static HANDLE sGatewayHandle = 0; - -static bool isGatewayRunning() -{ -	bool result = false; -	if(sGatewayHandle != 0)		 -	{ -		DWORD waitresult = WaitForSingleObject(sGatewayHandle, 0); -		if(waitresult != WAIT_OBJECT_0) -		{ -			result = true; -		}			 -	} -	return result; -} -static void killGateway() -{ -	if(sGatewayHandle != 0) -	{ -		TerminateProcess(sGatewayHandle,0); -	} -} - -#else // Mac and linux - -static pid_t sGatewayPID = 0; -static bool isGatewayRunning() -{ -	bool result = false; -	if(sGatewayPID != 0) -	{ -		// A kill with signal number 0 has no effect, just does error checking.  It should return an error if the process no longer exists. -		if(kill(sGatewayPID, 0) == 0) -		{ -			result = true; -		} -	} -	return result; -} - -static void killGateway() -{ -	if(sGatewayPID != 0) -	{ -		kill(sGatewayPID, SIGTERM); -	} -} - -#endif - -class LLSpeakerVolumeStorage : public LLSingleton<LLSpeakerVolumeStorage> -{ -	LOG_CLASS(LLSpeakerVolumeStorage); -public: - -	/** -	 * Stores volume level for specified user. -	 * -	 * @param[in] speaker_id - LLUUID of user to store volume level for. -	 * @param[in] volume - volume level to be stored for user. -	 */ -	void storeSpeakerVolume(const LLUUID& speaker_id, F32 volume); - -	/** -	 * Gets stored volume level for specified speaker -	 * -	 * @param[in] speaker_id - LLUUID of user to retrieve volume level for. -	 * @param[out] volume - set to stored volume if found, otherwise unmodified. -	 * @return - true if a stored volume is found. -	 */ -	bool getSpeakerVolume(const LLUUID& speaker_id, F32& volume); - -	/** -	 * Removes stored volume level for specified user. -	 * -	 * @param[in] speaker_id - LLUUID of user to remove. -	 */ -	void removeSpeakerVolume(const LLUUID& speaker_id); - -private: -	friend class LLSingleton<LLSpeakerVolumeStorage>; -	LLSpeakerVolumeStorage(); -	~LLSpeakerVolumeStorage(); - -	const static std::string SETTINGS_FILE_NAME; - -	void load(); -	void save(); - -	static F32 transformFromLegacyVolume(F32 volume_in); -	static F32 transformToLegacyVolume(F32 volume_in); - -	typedef std::map<LLUUID, F32> speaker_data_map_t; -	speaker_data_map_t mSpeakersData; -}; - -const std::string LLSpeakerVolumeStorage::SETTINGS_FILE_NAME = "volume_settings.xml"; - -LLSpeakerVolumeStorage::LLSpeakerVolumeStorage() -{ -	load(); -} - -LLSpeakerVolumeStorage::~LLSpeakerVolumeStorage() -{ -	save(); -} - -void LLSpeakerVolumeStorage::storeSpeakerVolume(const LLUUID& speaker_id, F32 volume) -{ -	if ((volume >= LLVoiceClient::VOLUME_MIN) && (volume <= LLVoiceClient::VOLUME_MAX)) -	{ -		mSpeakersData[speaker_id] = volume; - -		// Enable this when debugging voice slider issues.  It's way to spammy even for debug-level logging. -		// LL_DEBUGS("Voice") << "Stored volume = " << volume <<  " for " << id << LL_ENDL; -	} -	else -	{ -		LL_WARNS("Voice") << "Attempted to store out of range volume " << volume << " for " << speaker_id << LL_ENDL; -		llassert(0); -	} -} - -bool LLSpeakerVolumeStorage::getSpeakerVolume(const LLUUID& speaker_id, F32& volume) -{ -	speaker_data_map_t::const_iterator it = mSpeakersData.find(speaker_id); -	 -	if (it != mSpeakersData.end()) -	{ -		volume = it->second; - -		// Enable this when debugging voice slider issues.  It's way to spammy even for debug-level logging. -		// LL_DEBUGS("Voice") << "Retrieved stored volume = " << volume <<  " for " << id << LL_ENDL; - -		return true; -	} - -	return false; -} - -void LLSpeakerVolumeStorage::removeSpeakerVolume(const LLUUID& speaker_id) -{ -	mSpeakersData.erase(speaker_id); - -	// Enable this when debugging voice slider issues.  It's way to spammy even for debug-level logging. -	// LL_DEBUGS("Voice") << "Removing stored volume for  " << id << LL_ENDL; -} - -/* static */ F32 LLSpeakerVolumeStorage::transformFromLegacyVolume(F32 volume_in) -{ -	// Convert to linear-logarithmic [0.0..1.0] with 0.5 = 0dB -	// from legacy characteristic composed of two square-curves -	// that intersect at volume_in = 0.5, volume_out = 0.56 - -	F32 volume_out = 0.f; -	volume_in = llclamp(volume_in, 0.f, 1.0f); - -	if (volume_in <= 0.5f) -	{ -		volume_out = volume_in * volume_in * 4.f * 0.56f; -	} -	else -	{ -		volume_out = (1.f - 0.56f) * (4.f * volume_in * volume_in - 1.f) / 3.f + 0.56f; -	} - -	return volume_out; -} - -/* static */ F32 LLSpeakerVolumeStorage::transformToLegacyVolume(F32 volume_in) -{ -	// Convert from linear-logarithmic [0.0..1.0] with 0.5 = 0dB -	// to legacy characteristic composed of two square-curves -	// that intersect at volume_in = 0.56, volume_out = 0.5 - -	F32 volume_out = 0.f; -	volume_in = llclamp(volume_in, 0.f, 1.0f); - -	if (volume_in <= 0.56f) -	{ -		volume_out = sqrt(volume_in / (4.f * 0.56f)); -	} -	else -	{ -		volume_out = sqrt((3.f * (volume_in - 0.56f) / (1.f - 0.56f) + 1.f) / 4.f); -	} - -	return volume_out; -} - -void LLSpeakerVolumeStorage::load() -{ -	// load per-resident voice volume information -	std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SETTINGS_FILE_NAME); - -	LL_INFOS("Voice") << "Loading stored speaker volumes from: " << filename << LL_ENDL; - -	LLSD settings_llsd; -	llifstream file; -	file.open(filename); -	if (file.is_open()) -	{ -		LLSDSerialize::fromXML(settings_llsd, file); -	} - -	for (LLSD::map_const_iterator iter = settings_llsd.beginMap(); -		iter != settings_llsd.endMap(); ++iter) -	{ -		// Maintain compatibility with 1.23 non-linear saved volume levels -		F32 volume = transformFromLegacyVolume((F32)iter->second.asReal()); - -		storeSpeakerVolume(LLUUID(iter->first), volume); -	} -} - -void LLSpeakerVolumeStorage::save() -{ -	// If we quit from the login screen we will not have an SL account -	// name.  Don't try to save, otherwise we'll dump a file in -	// C:\Program Files\SecondLife\ or similar. JC -	std::string user_dir = gDirUtilp->getLindenUserDir(); -	if (!user_dir.empty()) -	{ -		std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SETTINGS_FILE_NAME); -		LLSD settings_llsd; - -		LL_INFOS("Voice") << "Saving stored speaker volumes to: " << filename << LL_ENDL; - -		for(speaker_data_map_t::const_iterator iter = mSpeakersData.begin(); iter != mSpeakersData.end(); ++iter) -		{ -			// Maintain compatibility with 1.23 non-linear saved volume levels -			F32 volume = transformToLegacyVolume(iter->second); - -			settings_llsd[iter->first.asString()] = volume; -		} - -		llofstream file; -		file.open(filename); -		LLSDSerialize::toPrettyXML(settings_llsd, file); -	} -} - - -/////////////////////////////////////////////////////////////////////////////////////////////// - -LLVoiceClient::LLVoiceClient() : -	mState(stateDisabled), -	mSessionTerminateRequested(false), -	mRelogRequested(false), -	mConnected(false), -	mPump(NULL), -	mSpatialJoiningNum(0), -	 -	mTuningMode(false), -	mTuningEnergy(0.0f), -	mTuningMicVolume(0), -	mTuningMicVolumeDirty(true), -	mTuningSpeakerVolume(0), -	mTuningSpeakerVolumeDirty(true), -	mTuningExitState(stateDisabled), -	 -	mAreaVoiceDisabled(false), -	mAudioSession(NULL), -	mAudioSessionChanged(false), -	mNextAudioSession(NULL), -	 -	mCurrentParcelLocalID(0), -	mNumberOfAliases(0), -	mCommandCookie(0), -	mLoginRetryCount(0), -	 -	mBuddyListMapPopulated(false), -	mBlockRulesListReceived(false), -	mAutoAcceptRulesListReceived(false), -	mCaptureDeviceDirty(false), -	mRenderDeviceDirty(false), -	mSpatialCoordsDirty(false), - -	mPTTDirty(true), -	mPTT(true), -	mUsePTT(true), -	mPTTIsMiddleMouse(false), -	mPTTKey(0), -	mPTTIsToggle(false), -	mUserPTTState(false), -	mMuteMic(false), -	mFriendsListDirty(true), -	 -	mEarLocation(0), -	mSpeakerVolumeDirty(true), -	mSpeakerMuteDirty(true), -	mMicVolume(0), -	mMicVolumeDirty(true), -	 -	mVoiceEnabled(false), -	mWriteInProgress(false), -	 -	mLipSyncEnabled(false) -{	 -	gVoiceClient = this; -	 -	mAPIVersion = LLTrans::getString("NotConnected"); - -	mSpeakerVolume = scale_speaker_volume(0); -	 -#if LL_DARWIN || LL_LINUX || LL_SOLARIS -		// HACK: THIS DOES NOT BELONG HERE -		// When the vivox daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us. -		// This should cause us to ignore SIGPIPE and handle the error through proper channels. -		// This should really be set up elsewhere.  Where should it go? -		signal(SIGPIPE, SIG_IGN); -		 -		// Since we're now launching the gateway with fork/exec instead of system(), we need to deal with zombie processes. -		// Ignoring SIGCHLD should prevent zombies from being created.  Alternately, we could use wait(), but I'd rather not do that. -		signal(SIGCHLD, SIG_IGN); -#endif - -	// set up state machine -	setState(stateDisabled); -	 -	gIdleCallbacks.addFunction(idle, this); -} - -//--------------------------------------------------- - -LLVoiceClient::~LLVoiceClient() -{ -} - -//---------------------------------------------- - -void LLVoiceClient::init(LLPumpIO *pump) -{ -	// constructor will set up gVoiceClient -	LLVoiceClient::getInstance()->mPump = pump; -	LLVoiceClient::getInstance()->updateSettings(); -} - -void LLVoiceClient::terminate() -{ -	if(gVoiceClient) -	{ -//		gVoiceClient->leaveAudioSession(); -		gVoiceClient->logout(); -		// As of SDK version 4885, this should no longer be necessary.  It will linger after the socket close if it needs to. -		// ms_sleep(2000); -		gVoiceClient->connectorShutdown(); -		gVoiceClient->closeSocket();		// Need to do this now -- bad things happen if the destructor does it later. -		 -		// This will do unpleasant things on windows. -//		killGateway(); -		 -		// Don't do this anymore -- LLSingleton will take care of deleting the object.		 -//		delete gVoiceClient; -		 -		// Hint to other code not to access the voice client anymore. -		gVoiceClient = NULL; -	} -} - -//--------------------------------------------------- - -void LLVoiceClient::updateSettings() -{ -	setVoiceEnabled(gSavedSettings.getBOOL("EnableVoiceChat")); -	setUsePTT(gSavedSettings.getBOOL("PTTCurrentlyEnabled")); -	std::string keyString = gSavedSettings.getString("PushToTalkButton"); -	setPTTKey(keyString); -	setPTTIsToggle(gSavedSettings.getBOOL("PushToTalkToggle")); -	setEarLocation(gSavedSettings.getS32("VoiceEarLocation")); - -	std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); -	setCaptureDevice(inputDevice); -	std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); -	setRenderDevice(outputDevice); -	F32 mic_level = gSavedSettings.getF32("AudioLevelMic"); -	setMicGain(mic_level); -	setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled")); -} - -///////////////////////////// -// utility functions - -bool LLVoiceClient::writeString(const std::string &str) -{ -	bool result = false; -	if(mConnected) -	{ -		apr_status_t err; -		apr_size_t size = (apr_size_t)str.size(); -		apr_size_t written = size; -	 -		//MARK: Turn this on to log outgoing XML -//		LL_DEBUGS("Voice") << "sending: " << str << LL_ENDL; - -		// check return code - sockets will fail (broken, etc.) -		err = apr_socket_send( -				mSocket->getSocket(), -				(const char*)str.data(), -				&written); -		 -		if(err == 0) -		{ -			// Success. -			result = true; -		} -		// TODO: handle partial writes (written is number of bytes written) -		// Need to set socket to non-blocking before this will work. -//		else if(APR_STATUS_IS_EAGAIN(err)) -//		{ -//			//  -//		} -		else -		{ -			// Assume any socket error means something bad.  For now, just close the socket. -			char buf[MAX_STRING]; -			LL_WARNS("Voice") << "apr error " << err << " ("<< apr_strerror(err, buf, MAX_STRING) << ") sending data to vivox daemon." << LL_ENDL; -			daemonDied(); -		} -	} -		 -	return result; -} - - -///////////////////////////// -// session control messages -void LLVoiceClient::connectorCreate() -{ -	std::ostringstream stream; -	std::string logpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); -	std::string loglevel = "0"; -	 -	// Transition to stateConnectorStarted when the connector handle comes back. -	setState(stateConnectorStarting); - -	std::string savedLogLevel = gSavedSettings.getString("VivoxDebugLevel"); -		 -	if(savedLogLevel != "-1") -	{ -		LL_DEBUGS("Voice") << "creating connector with logging enabled" << LL_ENDL; -		loglevel = "10"; -	} -	 -	stream  -	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.Create.1\">" -		<< "<ClientName>V2 SDK</ClientName>" -		<< "<AccountManagementServer>" << mVoiceAccountServerURI << "</AccountManagementServer>" -		<< "<Mode>Normal</Mode>" -		<< "<Logging>" -			<< "<Folder>" << logpath << "</Folder>" -			<< "<FileNamePrefix>Connector</FileNamePrefix>" -			<< "<FileNameSuffix>.log</FileNameSuffix>" -			<< "<LogLevel>" << loglevel << "</LogLevel>" -		<< "</Logging>" -		<< "<Application>SecondLifeViewer.1</Application>" -	<< "</Request>\n\n\n"; -	 -	writeString(stream.str()); -} - -void LLVoiceClient::connectorShutdown() -{ -	setState(stateConnectorStopping); -	 -	if(!mConnectorHandle.empty()) -	{ -		std::ostringstream stream; -		stream -		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.InitiateShutdown.1\">" -			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" -		<< "</Request>" -		<< "\n\n\n"; -		 -		mConnectorHandle.clear(); -		 -		writeString(stream.str()); -	} -} - -void LLVoiceClient::userAuthorized(const std::string& firstName, const std::string& lastName, const LLUUID &agentID) -{ -	mAccountFirstName = firstName; -	mAccountLastName = lastName; - -	mAccountDisplayName = firstName; -	mAccountDisplayName += " "; -	mAccountDisplayName += lastName; - -	LL_INFOS("Voice") << "name \"" << mAccountDisplayName << "\" , ID " << agentID << LL_ENDL; - -	sConnectingToAgni = LLViewerLogin::getInstance()->isInProductionGrid(); - -	mAccountName = nameFromID(agentID); -} - -void LLVoiceClient::requestVoiceAccountProvision(S32 retries) -{ -	if ( gAgent.getRegion() && mVoiceEnabled ) -	{ -		std::string url =  -			gAgent.getRegion()->getCapability( -				"ProvisionVoiceAccountRequest"); - -		if ( url == "" ) return; - -		LLHTTPClient::post( -			url, -			LLSD(), -			new LLViewerVoiceAccountProvisionResponder(retries)); -	} -} - -void LLVoiceClient::login( -	const std::string& account_name, -	const std::string& password, -	const std::string& voice_sip_uri_hostname, -	const std::string& voice_account_server_uri) -{ -	mVoiceSIPURIHostName = voice_sip_uri_hostname; -	mVoiceAccountServerURI = voice_account_server_uri; - -	if(!mAccountHandle.empty()) -	{ -		// Already logged in. -		LL_WARNS("Voice") << "Called while already logged in." << LL_ENDL; -		 -		// Don't process another login. -		return; -	} -	else if ( account_name != mAccountName ) -	{ -		//TODO: error? -		LL_WARNS("Voice") << "Wrong account name! " << account_name -				<< " instead of " << mAccountName << LL_ENDL; -	} -	else -	{ -		mAccountPassword = password; -	} - -	std::string debugSIPURIHostName = gSavedSettings.getString("VivoxDebugSIPURIHostName"); -	 -	if( !debugSIPURIHostName.empty() ) -	{ -		mVoiceSIPURIHostName = debugSIPURIHostName; -	} -	 -	if( mVoiceSIPURIHostName.empty() ) -	{ -		// we have an empty account server name -		// so we fall back to hardcoded defaults - -		if(sConnectingToAgni) -		{ -			// Use the release account server -			mVoiceSIPURIHostName = "bhr.vivox.com"; -		} -		else -		{ -			// Use the development account server -			mVoiceSIPURIHostName = "bhd.vivox.com"; -		} -	} -	 -	std::string debugAccountServerURI = gSavedSettings.getString("VivoxDebugVoiceAccountServerURI"); - -	if( !debugAccountServerURI.empty() ) -	{ -		mVoiceAccountServerURI = debugAccountServerURI; -	} -	 -	if( mVoiceAccountServerURI.empty() ) -	{ -		// If the account server URI isn't specified, construct it from the SIP URI hostname -		mVoiceAccountServerURI = "https://www." + mVoiceSIPURIHostName + "/api2/";		 -	} -} - -void LLVoiceClient::idle(void* user_data) -{ -	LLVoiceClient* self = (LLVoiceClient*)user_data; -	self->stateMachine(); -} - -std::string LLVoiceClient::state2string(LLVoiceClient::state inState) -{ -	std::string result = "UNKNOWN"; -	 -		// Prevent copy-paste errors when updating this list... -#define CASE(x)  case x:  result = #x;  break - -	switch(inState) -	{ -		CASE(stateDisableCleanup); -		CASE(stateDisabled); -		CASE(stateStart); -		CASE(stateDaemonLaunched); -		CASE(stateConnecting); -		CASE(stateConnected); -		CASE(stateIdle); -		CASE(stateMicTuningStart); -		CASE(stateMicTuningRunning); -		CASE(stateMicTuningStop); -		CASE(stateConnectorStart); -		CASE(stateConnectorStarting); -		CASE(stateConnectorStarted); -		CASE(stateLoginRetry); -		CASE(stateLoginRetryWait); -		CASE(stateNeedsLogin); -		CASE(stateLoggingIn); -		CASE(stateLoggedIn); -		CASE(stateCreatingSessionGroup); -		CASE(stateNoChannel); -		CASE(stateJoiningSession); -		CASE(stateSessionJoined); -		CASE(stateRunning); -		CASE(stateLeavingSession); -		CASE(stateSessionTerminated); -		CASE(stateLoggingOut); -		CASE(stateLoggedOut); -		CASE(stateConnectorStopping); -		CASE(stateConnectorStopped); -		CASE(stateConnectorFailed); -		CASE(stateConnectorFailedWaiting); -		CASE(stateLoginFailed); -		CASE(stateLoginFailedWaiting); -		CASE(stateJoinSessionFailed); -		CASE(stateJoinSessionFailedWaiting); -		CASE(stateJail); -	} +#include "llvoicedw.h" +#include "llvoicevivox.h" +#include "llviewernetwork.h" +#include "llhttpnode.h" +#include "llnotificationsutil.h" +#include "llsdserialize.h" +#include "llui.h" -#undef CASE -	 -	return result; -} +const F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f;  std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserver::EStatusType inStatus)  {  	std::string result = "UNKNOWN"; -		// Prevent copy-paste errors when updating this list... +	// Prevent copy-paste errors when updating this list...  #define CASE(x)  case x:  result = #x;  break - +	  	switch(inStatus)  	{ -		CASE(STATUS_LOGIN_RETRY); -		CASE(STATUS_LOGGED_IN); -		CASE(STATUS_JOINING); -		CASE(STATUS_JOINED); -		CASE(STATUS_LEFT_CHANNEL); -		CASE(STATUS_VOICE_DISABLED); -		CASE(STATUS_VOICE_ENABLED); -		CASE(BEGIN_ERROR_STATUS); -		CASE(ERROR_CHANNEL_FULL); -		CASE(ERROR_CHANNEL_LOCKED); -		CASE(ERROR_NOT_AVAILABLE); -		CASE(ERROR_UNKNOWN); -	default: -		break; +			CASE(STATUS_LOGIN_RETRY); +			CASE(STATUS_LOGGED_IN); +			CASE(STATUS_JOINING); +			CASE(STATUS_JOINED); +			CASE(STATUS_LEFT_CHANNEL); +			CASE(STATUS_VOICE_DISABLED); +			CASE(BEGIN_ERROR_STATUS); +			CASE(ERROR_CHANNEL_FULL); +			CASE(ERROR_CHANNEL_LOCKED); +			CASE(ERROR_NOT_AVAILABLE); +			CASE(ERROR_UNKNOWN); +		default: +			break;  	} - +	  #undef CASE  	return result;  } -void LLVoiceClient::setState(state inState) -{ -	LL_DEBUGS("Voice") << "entering state " << state2string(inState) << LL_ENDL; -	 -	mState = inState; -} -void LLVoiceClient::stateMachine() -{ -	if(gDisconnected) -	{ -		// The viewer has been disconnected from the sim.  Disable voice. -		setVoiceEnabled(false); -	} -	 -	if(mVoiceEnabled) -	{ -		updatePosition(); -	} -	else if(mTuningMode) -	{ -		// Tuning mode is special -- it needs to launch SLVoice even if voice is disabled. -	} -	else -	{ -		if((getState() != stateDisabled) && (getState() != stateDisableCleanup)) -		{ -			// User turned off voice support.  Send the cleanup messages, close the socket, and reset. -			if(!mConnected) -			{ -				// if voice was turned off after the daemon was launched but before we could connect to it, we may need to issue a kill. -				LL_INFOS("Voice") << "Disabling voice before connection to daemon, terminating." << LL_ENDL; -				killGateway(); -			} -			 -			logout(); -			connectorShutdown(); -			 -			setState(stateDisableCleanup); -		} -	} -	 -	// Check for parcel boundary crossing -	{ -		LLViewerRegion *region = gAgent.getRegion(); -		LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); -		 -		if(region && parcel) -		{ -			S32 parcelLocalID = parcel->getLocalID(); -			std::string regionName = region->getName(); -			std::string capURI = region->getCapability("ParcelVoiceInfoRequest"); -		 -//			LL_DEBUGS("Voice") << "Region name = \"" << regionName << "\", parcel local ID = " << parcelLocalID << ", cap URI = \"" << capURI << "\"" << LL_ENDL; -			// The region name starts out empty and gets filled in later.   -			// Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes. -			// If either is empty, wait for the next time around. -			if(!regionName.empty()) -			{ -				if(!capURI.empty()) -				{ -					if((parcelLocalID != mCurrentParcelLocalID) || (regionName != mCurrentRegionName)) -					{ -						// We have changed parcels.  Initiate a parcel channel lookup. -						mCurrentParcelLocalID = parcelLocalID; -						mCurrentRegionName = regionName; -						 -						parcelChanged(); -					} -				} -				else -				{ -					LL_WARNS_ONCE("Voice") << "region doesn't have ParcelVoiceInfoRequest capability.  This is normal for a short time after teleporting, but bad if it persists for very long." << LL_ENDL; -				} -			} -		} -	} -	switch(getState()) -	{ -		//MARK: stateDisableCleanup -		case stateDisableCleanup: -			// Clean up and reset everything.  -			closeSocket(); -			deleteAllSessions(); -			deleteAllBuddies();		 -			 -			mConnectorHandle.clear(); -			mAccountHandle.clear(); -			mAccountPassword.clear(); -			mVoiceAccountServerURI.clear(); -			 -			setState(stateDisabled);	 -		break; -		 -		//MARK: stateDisabled -		case stateDisabled: -			if(mTuningMode || (mVoiceEnabled && !mAccountName.empty())) -			{ -				setState(stateStart); -			} -		break; -		 -		//MARK: stateStart -		case stateStart: -			if(gSavedSettings.getBOOL("CmdLineDisableVoice")) -			{ -				// Voice is locked out, we must not launch the vivox daemon. -				setState(stateJail); -			} -			else if(!isGatewayRunning()) -			{ -				if(true) -				{ -					// Launch the voice daemon -					 -					// *FIX:Mani - Using the executable dir instead  -					// of mAppRODataDir, the working directory from which the app -					// is launched. -					//std::string exe_path = gDirUtilp->getAppRODataDir(); -					std::string exe_path = gDirUtilp->getExecutableDir(); -					exe_path += gDirUtilp->getDirDelimiter(); -#if LL_WINDOWS -					exe_path += "SLVoice.exe"; -#elif LL_DARWIN -					exe_path += "../Resources/SLVoice"; -#else -					exe_path += "SLVoice"; -#endif -					// See if the vivox executable exists -					llstat s; -					if(!LLFile::stat(exe_path, &s)) -					{ -						// vivox executable exists.  Build the command line and launch the daemon. -						// SLIM SDK: these arguments are no longer necessary. -//						std::string args = " -p tcp -h -c"; -						std::string args; -						std::string loglevel = gSavedSettings.getString("VivoxDebugLevel"); -						 -						if(loglevel.empty()) -						{ -							loglevel = "-1";	// turn logging off completely -						} -						 -						args += " -ll "; -						args += loglevel; -						 -						LL_DEBUGS("Voice") << "Args for SLVoice: " << args << LL_ENDL; - -#if LL_WINDOWS -						PROCESS_INFORMATION pinfo; -						STARTUPINFOW sinfo; -						memset(&sinfo, 0, sizeof(sinfo)); - -						std::string exe_dir = gDirUtilp->getExecutableDir(); - -						llutf16string exe_path16 = utf8str_to_utf16str(exe_path); -						llutf16string exe_dir16 = utf8str_to_utf16str(exe_dir); -						llutf16string args16 = utf8str_to_utf16str(args); -						// Create a writeable copy to keep Windows happy. -						U16 *argscpy_16 = new U16[args16.size() + 1]; -						wcscpy_s(argscpy_16,args16.size()+1,args16.c_str()); -						if(!CreateProcessW(exe_path16.c_str(), argscpy_16, NULL, NULL, FALSE, 0, NULL, exe_dir16.c_str(), &sinfo, &pinfo)) -						{ -//							DWORD dwErr = GetLastError(); -						} -						else -						{ -							// foo = pinfo.dwProcessId; // get your pid here if you want to use it later on -							// CloseHandle(pinfo.hProcess); // stops leaks - nothing else -							sGatewayHandle = pinfo.hProcess; -							CloseHandle(pinfo.hThread); // stops leaks - nothing else -						}		 -						 -						delete[] argscpy_16; -#else	// LL_WINDOWS -						// This should be the same for mac and linux -						{ -							std::vector<std::string> arglist; -							arglist.push_back(exe_path); -							 -							// Split the argument string into separate strings for each argument -							typedef boost::tokenizer<boost::char_separator<char> > tokenizer; -							boost::char_separator<char> sep(" "); -							tokenizer tokens(args, sep); -							tokenizer::iterator token_iter; - -							for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) -							{ -								arglist.push_back(*token_iter); -							} -							 -							// create an argv vector for the child process -							char **fakeargv = new char*[arglist.size() + 1]; -							int i; -							for(i=0; i < arglist.size(); i++) -								fakeargv[i] = const_cast<char*>(arglist[i].c_str()); - -							fakeargv[i] = NULL; -							 -							fflush(NULL); // flush all buffers before the child inherits them -							pid_t id = vfork(); -							if(id == 0) -							{ -								// child -								execv(exe_path.c_str(), fakeargv); -								 -								// If we reach this point, the exec failed. -								// Use _exit() instead of exit() per the vfork man page. -								_exit(0); -							} - -							// parent -							delete[] fakeargv; -							sGatewayPID = id; -						} -#endif	// LL_WINDOWS -						mDaemonHost = LLHost(gSavedSettings.getString("VoiceHost").c_str(), gSavedSettings.getU32("VoicePort")); -					}	 -					else -					{ -						LL_INFOS("Voice") << exe_path << " not found." << LL_ENDL; -					}	 -				} -				else -				{		 -					// SLIM SDK: port changed from 44124 to 44125. -					// We can connect to a client gateway running on another host.  This is useful for testing. -					// To do this, launch the gateway on a nearby host like this: -					//  vivox-gw.exe -p tcp -i 0.0.0.0:44125 -					// and put that host's IP address here. -					mDaemonHost = LLHost(gSavedSettings.getString("VoiceHost"), gSavedSettings.getU32("VoicePort")); -				} - -				mUpdateTimer.start(); -				mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS); - -				setState(stateDaemonLaunched); -				 -				// Dirty the states we'll need to sync with the daemon when it comes up. -				mPTTDirty = true; -				mMicVolumeDirty = true; -				mSpeakerVolumeDirty = true; -				mSpeakerMuteDirty = true; -				// These only need to be set if they're not default (i.e. empty string). -				mCaptureDeviceDirty = !mCaptureDevice.empty(); -				mRenderDeviceDirty = !mRenderDevice.empty(); -				 -				mMainSessionGroupHandle.clear(); -			} -		break; - -		//MARK: stateDaemonLaunched -		case stateDaemonLaunched: -			if(mUpdateTimer.hasExpired()) -			{ -				LL_DEBUGS("Voice") << "Connecting to vivox daemon" << LL_ENDL; - -				mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS); - -				if(!mSocket) -				{ -					mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);	 -				} -				 -				mConnected = mSocket->blockingConnect(mDaemonHost); -				if(mConnected) -				{ -					setState(stateConnecting); -				} -				else -				{ -					// If the connect failed, the socket may have been put into a bad state.  Delete it. -					closeSocket(); -				} -			} -		break; - -		//MARK: stateConnecting -		case stateConnecting: -		// Can't do this until we have the pump available. -		if(mPump) -		{ -			// MBW -- Note to self: pumps and pipes examples in -			//  indra/test/io.cpp -			//  indra/test/llpipeutil.{cpp|h} - -			// Attach the pumps and pipes -				 -			LLPumpIO::chain_t readChain; - -			readChain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket))); -			readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser())); - -			mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS); - -			setState(stateConnected); -		} - -		break; -		 -		//MARK: stateConnected -		case stateConnected: -			// Initial devices query -			getCaptureDevicesSendMessage(); -			getRenderDevicesSendMessage(); - -			mLoginRetryCount = 0; - -			setState(stateIdle); -		break; - -		//MARK: stateIdle -		case stateIdle: -			// This is the idle state where we're connected to the daemon but haven't set up a connector yet. -			if(mTuningMode) -			{ -				mTuningExitState = stateIdle; -				setState(stateMicTuningStart); -			} -			else if(!mVoiceEnabled) -			{ -				// We never started up the connector.  This will shut down the daemon. -				setState(stateConnectorStopped); -			} -			else if(!mAccountName.empty()) -			{ -				LLViewerRegion *region = gAgent.getRegion(); -				 -				if(region) -				{ -					if ( region->getCapability("ProvisionVoiceAccountRequest") != "" ) -					{ -						if ( mAccountPassword.empty() ) -						{ -							requestVoiceAccountProvision(); -						} -						setState(stateConnectorStart); -					} -					else -					{ -						LL_WARNS_ONCE("Voice") << "region doesn't have ProvisionVoiceAccountRequest capability!" << LL_ENDL; -					} -				} -			} -		break; - -		//MARK: stateMicTuningStart -		case stateMicTuningStart: -			if(mUpdateTimer.hasExpired()) -			{ -				if(mCaptureDeviceDirty || mRenderDeviceDirty) -				{ -					// These can't be changed while in tuning mode.  Set them before starting. -					std::ostringstream stream; -					 -					buildSetCaptureDevice(stream); -					buildSetRenderDevice(stream); - -					if(!stream.str().empty()) -					{ -						writeString(stream.str()); -					}				 - -					// This will come around again in the same state and start the capture, after the timer expires. -					mUpdateTimer.start(); -					mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); -				} -				else -				{ -					// duration parameter is currently unused, per Mike S. -					tuningCaptureStartSendMessage(10000); - -					setState(stateMicTuningRunning); -				} -			} -			 -		break; -		 -		//MARK: stateMicTuningRunning -		case stateMicTuningRunning: -			if(!mTuningMode || mCaptureDeviceDirty || mRenderDeviceDirty) -			{ -				// All of these conditions make us leave tuning mode. -				setState(stateMicTuningStop); -			} -			else -			{ -				// process mic/speaker volume changes -				if(mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty) -				{ -					std::ostringstream stream; -					 -					if(mTuningMicVolumeDirty) -					{ -						LL_INFOS("Voice") << "setting tuning mic level to " << mTuningMicVolume << LL_ENDL; -						stream -						<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetMicLevel.1\">" -						<< "<Level>" << mTuningMicVolume << "</Level>" -						<< "</Request>\n\n\n"; -					} -					 -					if(mTuningSpeakerVolumeDirty) -					{ -						stream -						<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetSpeakerLevel.1\">" -						<< "<Level>" << mTuningSpeakerVolume << "</Level>" -						<< "</Request>\n\n\n"; -					} -					 -					mTuningMicVolumeDirty = false; -					mTuningSpeakerVolumeDirty = false; - -					if(!stream.str().empty()) -					{ -						writeString(stream.str()); -					} -				} -			} -		break; -		 -		//MARK: stateMicTuningStop -		case stateMicTuningStop: -		{ -			// transition out of mic tuning -			tuningCaptureStopSendMessage(); -			 -			setState(mTuningExitState); -			 -			// if we exited just to change devices, this will keep us from re-entering too fast. -			mUpdateTimer.start(); -			mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); -			 -		} -		break; -												 -		//MARK: stateConnectorStart -		case stateConnectorStart: -			if(!mVoiceEnabled) -			{ -				// We were never logged in.  This will shut down the connector. -				setState(stateLoggedOut); -			} -			else if(!mVoiceAccountServerURI.empty()) -			{ -				connectorCreate(); -			} -		break; -		 -		//MARK: stateConnectorStarting -		case stateConnectorStarting:	// waiting for connector handle -			// connectorCreateResponse() will transition from here to stateConnectorStarted. -		break; -		 -		//MARK: stateConnectorStarted -		case stateConnectorStarted:		// connector handle received -			if(!mVoiceEnabled) -			{ -				// We were never logged in.  This will shut down the connector. -				setState(stateLoggedOut); -			} -			else -			{ -				// The connector is started.  Send a login message. -				setState(stateNeedsLogin); -			} -		break; -				 -		//MARK: stateLoginRetry -		case stateLoginRetry: -			if(mLoginRetryCount == 0) -			{ -				// First retry -- display a message to the user -				notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY); -			} -			 -			mLoginRetryCount++; -			 -			if(mLoginRetryCount > MAX_LOGIN_RETRIES) -			{ -				LL_WARNS("Voice") << "too many login retries, giving up." << LL_ENDL; -				setState(stateLoginFailed); -			} -			else -			{ -				LL_INFOS("Voice") << "will retry login in " << LOGIN_RETRY_SECONDS << " seconds." << LL_ENDL; -				mUpdateTimer.start(); -				mUpdateTimer.setTimerExpirySec(LOGIN_RETRY_SECONDS); -				setState(stateLoginRetryWait); -			} -		break; -		 -		//MARK: stateLoginRetryWait -		case stateLoginRetryWait: -			if(mUpdateTimer.hasExpired()) -			{ -				setState(stateNeedsLogin); -			} -		break; -		 -		//MARK: stateNeedsLogin -		case stateNeedsLogin: -			if(!mAccountPassword.empty()) -			{ -				setState(stateLoggingIn); -				loginSendMessage(); -			}		 -		break; -		 -		//MARK: stateLoggingIn -		case stateLoggingIn:			// waiting for account handle -			// loginResponse() will transition from here to stateLoggedIn. -		break; -		 -		//MARK: stateLoggedIn -		case stateLoggedIn:				// account handle received - -			notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); - -			// request the current set of block rules (we'll need them when updating the friends list) -			accountListBlockRulesSendMessage(); -			 -			// request the current set of auto-accept rules -			accountListAutoAcceptRulesSendMessage(); -			 -			// Set up the mute list observer if it hasn't been set up already. -			if((!sMuteListListener_listening)) -			{ -				LLMuteList::getInstance()->addObserver(&mutelist_listener); -				sMuteListListener_listening = true; -			} - -			// Set up the friends list observer if it hasn't been set up already. -			if(friendslist_listener == NULL) -			{ -				friendslist_listener = new LLVoiceClientFriendsObserver; -				LLAvatarTracker::instance().addObserver(friendslist_listener); -			} -			 -			// Set the initial state of mic mute, local speaker volume, etc. -			{ -				std::ostringstream stream; -				 -				buildLocalAudioUpdates(stream); -				 -				if(!stream.str().empty()) -				{ -					writeString(stream.str()); -				} -			} -			 -#if USE_SESSION_GROUPS			 -			// create the main session group -			sessionGroupCreateSendMessage(); -			 -			setState(stateCreatingSessionGroup); -#else -			// Not using session groups -- skip the stateCreatingSessionGroup state. -			setState(stateNoChannel); - -			// Initial kick-off of channel lookup logic -			parcelChanged();		 -#endif -		break; -		 -		//MARK: stateCreatingSessionGroup -		case stateCreatingSessionGroup: -			if(mSessionTerminateRequested || !mVoiceEnabled) -			{ -				// TODO: Question: is this the right way out of this state -				setState(stateSessionTerminated); -			} -			else if(!mMainSessionGroupHandle.empty()) -			{ -				setState(stateNoChannel); -				 -				// Start looped recording (needed for "panic button" anti-griefing tool) -				recordingLoopStart(); - -				// Initial kick-off of channel lookup logic -				parcelChanged();		 -			} -		break; -					 -		//MARK: stateNoChannel -		case stateNoChannel: -			 -			mSpatialJoiningNum = 0; -			// Do this here as well as inside sendPositionalUpdate().   -			// Otherwise, if you log in but don't join a proximal channel (such as when your login location has voice disabled), your friends list won't sync. -			sendFriendsListUpdates(); -			 -			if(mSessionTerminateRequested || !mVoiceEnabled) -			{ -				// TODO: Question: Is this the right way out of this state? -				setState(stateSessionTerminated); -			} -			else if(mTuningMode) -			{ -				mTuningExitState = stateNoChannel; -				setState(stateMicTuningStart); -			} -			else if(sessionNeedsRelog(mNextAudioSession)) -			{ -				requestRelog(); -				setState(stateSessionTerminated); -			} -			else if(mNextAudioSession) -			{				 -				sessionState *oldSession = mAudioSession; - -				mAudioSession = mNextAudioSession; -				if(!mAudioSession->mReconnect)	 -				{ -					mNextAudioSession = NULL; -				} -				 -				// The old session may now need to be deleted. -				reapSession(oldSession); -				 -				if(!mAudioSession->mHandle.empty()) -				{ -					// Connect to a session by session handle - -					sessionMediaConnectSendMessage(mAudioSession); -				} -				else -				{ -					// Connect to a session by URI -					sessionCreateSendMessage(mAudioSession, true, false); -				} - -				notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING); -				setState(stateJoiningSession); -			} -			else if(!mSpatialSessionURI.empty()) -			{ -				// If we're not headed elsewhere and have a spatial URI, return to spatial. -				switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials); -			} -		break; - -		//MARK: stateJoiningSession -		case stateJoiningSession:		// waiting for session handle - -			// If this is true we have problem with connection to voice server (EXT-4313). -			// See descriptions of mSpatialJoiningNum and MAX_NORMAL_JOINING_SPATIAL_NUM. -			if(mSpatialJoiningNum == MAX_NORMAL_JOINING_SPATIAL_NUM)  -			{ -				// Notify observers to let them know there is problem with voice -				notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); -				llwarns << "There seems to be problem with connection to voice server. Disabling voice chat abilities." << llendl; -			} - -			// Increase mSpatialJoiningNum only for spatial sessions- it's normal to reach this case for -			// example for p2p many times while waiting for response, so it can't be used to detect errors -			if(mAudioSession && mAudioSession->mIsSpatial) -			{ -				mSpatialJoiningNum++; -			} -			 -			// joinedAudioSession() will transition from here to stateSessionJoined. -			if(!mVoiceEnabled) -			{ -				// User bailed out during connect -- jump straight to teardown. -				setState(stateSessionTerminated); -			} -			else if(mSessionTerminateRequested) -			{ -				if(mAudioSession && !mAudioSession->mHandle.empty()) -				{ -					// Only allow direct exits from this state in p2p calls (for cancelling an invite). -					// Terminating a half-connected session on other types of calls seems to break something in the vivox gateway. -					if(mAudioSession->mIsP2P) -					{ -						sessionMediaDisconnectSendMessage(mAudioSession); -						setState(stateSessionTerminated); -					} -				} -			} -		break; -		 -		//MARK: stateSessionJoined -		case stateSessionJoined:		// session handle received -			 -			mSpatialJoiningNum = 0; -			// It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4 -			// before continuing from this state.  They can happen in either order, and if I don't wait for both, things can get stuck. -			// For now, the SessionGroup.AddSession response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined. -			// This is a cheap way to make sure both have happened before proceeding. -			if(mAudioSession && mAudioSession->mVoiceEnabled) -			{ -				// Dirty state that may need to be sync'ed with the daemon. -				mPTTDirty = true; -				mSpeakerVolumeDirty = true; -				mSpatialCoordsDirty = true; -				 -				setState(stateRunning); -				 -				// Start the throttle timer -				mUpdateTimer.start(); -				mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); - -				// Events that need to happen when a session is joined could go here. -				// Maybe send initial spatial data? -				notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); - -			} -			else if(!mVoiceEnabled) -			{ -				// User bailed out during connect -- jump straight to teardown. -				setState(stateSessionTerminated); -			} -			else if(mSessionTerminateRequested) -			{ -				// Only allow direct exits from this state in p2p calls (for cancelling an invite). -				// Terminating a half-connected session on other types of calls seems to break something in the vivox gateway. -				if(mAudioSession && mAudioSession->mIsP2P) -				{ -					sessionMediaDisconnectSendMessage(mAudioSession); -					setState(stateSessionTerminated); -				} -			} -		break; -		 -		//MARK: stateRunning -		case stateRunning:				// steady state -			// Disabling voice or disconnect requested. -			if(!mVoiceEnabled || mSessionTerminateRequested) -			{ -				leaveAudioSession(); -			} -			else -			{ -				 -				// Figure out whether the PTT state needs to change -				{ -					bool newPTT; -					if(mUsePTT) -					{ -						// If configured to use PTT, track the user state. -						newPTT = mUserPTTState; -					} -					else -					{ -						// If not configured to use PTT, it should always be true (otherwise the user will be unable to speak). -						newPTT = true; -					} -					 -					if(mMuteMic) -					{ -						// This always overrides any other PTT setting. -						newPTT = false; -					} -					 -					// Dirty if state changed. -					if(newPTT != mPTT) -					{ -						mPTT = newPTT; -						mPTTDirty = true; -					} -				} -				 -				if(!inSpatialChannel()) -				{ -					// When in a non-spatial channel, never send positional updates. -					mSpatialCoordsDirty = false; -				} -				else -				{ -					// Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position) -					enforceTether(); -				} -				 -				// Send an update only if the ptt or mute state has changed (which shouldn't be able to happen that often -				// -- the user can only click so fast) or every 10hz, whichever is sooner. -				// Sending for every volume update causes an excessive flood of messages whenever a volume slider is dragged. -				if((mAudioSession && mAudioSession->mMuteDirty) || mPTTDirty || mUpdateTimer.hasExpired()) -				{ -					mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); -					sendPositionalUpdate(); -				} -			} -		break; -		 -		//MARK: stateLeavingSession -		case stateLeavingSession:		// waiting for terminate session response -			// The handler for the Session.Terminate response will transition from here to stateSessionTerminated. -		break; - -		//MARK: stateSessionTerminated -		case stateSessionTerminated: -			 -			// Must do this first, since it uses mAudioSession. -			notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); -			 -			if(mAudioSession) -			{ -				sessionState *oldSession = mAudioSession; - -				mAudioSession = NULL; -				// We just notified status observers about this change.  Don't do it again. -				mAudioSessionChanged = false; - -				// The old session may now need to be deleted. -				reapSession(oldSession); -			} -			else -			{ -				LL_WARNS("Voice") << "stateSessionTerminated with NULL mAudioSession" << LL_ENDL; -			} -	 -			// Always reset the terminate request flag when we get here. -			mSessionTerminateRequested = false; - -			if(mVoiceEnabled && !mRelogRequested) -			{				 -				// Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state). -				setState(stateNoChannel); -			} -			else -			{ -				// Shutting down voice, continue with disconnecting. -				logout(); -				 -				// The state machine will take it from here -				mRelogRequested = false; -			} -			 -		break; -		 -		//MARK: stateLoggingOut -		case stateLoggingOut:			// waiting for logout response -			// The handler for the AccountLoginStateChangeEvent will transition from here to stateLoggedOut. -		break; -		 -		//MARK: stateLoggedOut -		case stateLoggedOut:			// logout response received -			 -			// Once we're logged out, all these things are invalid. -			mAccountHandle.clear(); -			deleteAllSessions(); -			deleteAllBuddies(); - -			if(mVoiceEnabled && !mRelogRequested) -			{ -				// User was logged out, but wants to be logged in.  Send a new login request. -				setState(stateNeedsLogin); -			} -			else -			{ -				// shut down the connector -				connectorShutdown(); -			} -		break; -		 -		//MARK: stateConnectorStopping -		case stateConnectorStopping:	// waiting for connector stop -			// The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped. -		break; - -		//MARK: stateConnectorStopped -		case stateConnectorStopped:		// connector stop received -			setState(stateDisableCleanup); -		break; - -		//MARK: stateConnectorFailed -		case stateConnectorFailed: -			setState(stateConnectorFailedWaiting); -		break; -		//MARK: stateConnectorFailedWaiting -		case stateConnectorFailedWaiting: -			if(!mVoiceEnabled) -			{ -				setState(stateDisableCleanup); -			} -		break; - -		//MARK: stateLoginFailed -		case stateLoginFailed: -			setState(stateLoginFailedWaiting); -		break; -		//MARK: stateLoginFailedWaiting -		case stateLoginFailedWaiting: -			if(!mVoiceEnabled) -			{ -				setState(stateDisableCleanup); -			} -		break; - -		//MARK: stateJoinSessionFailed -		case stateJoinSessionFailed: -			// Transition to error state.  Send out any notifications here. -			if(mAudioSession) -			{ -				LL_WARNS("Voice") << "stateJoinSessionFailed: (" << mAudioSession->mErrorStatusCode << "): " << mAudioSession->mErrorStatusString << LL_ENDL; -			} -			else -			{ -				LL_WARNS("Voice") << "stateJoinSessionFailed with no current session" << LL_ENDL; -			} -			 -			notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); -			setState(stateJoinSessionFailedWaiting); -		break; -		 -		//MARK: stateJoinSessionFailedWaiting -		case stateJoinSessionFailedWaiting: -			// Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message. -			// Region crossings may leave this state and try the join again. -			if(mSessionTerminateRequested) -			{ -				setState(stateSessionTerminated); -			} -		break; -		 -		//MARK: stateJail -		case stateJail: -			// We have given up.  Do nothing. -		break; - -	} -	 -	if(mAudioSession && mAudioSession->mParticipantsChanged) -	{ -		mAudioSession->mParticipantsChanged = false; -		mAudioSessionChanged = true; -	} -	 -	if(mAudioSessionChanged) -	{ -		mAudioSessionChanged = false; -		notifyParticipantObservers(); -	} -} - -void LLVoiceClient::closeSocket(void) -{ -	mSocket.reset(); -	mConnected = false;	 -} - -void LLVoiceClient::loginSendMessage() -{ -	std::ostringstream stream; - -	bool autoPostCrashDumps = gSavedSettings.getBOOL("VivoxAutoPostCrashDumps"); - -	stream -	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.Login.1\">" -		<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" -		<< "<AccountName>" << mAccountName << "</AccountName>" -		<< "<AccountPassword>" << mAccountPassword << "</AccountPassword>" -		<< "<AudioSessionAnswerMode>VerifyAnswer</AudioSessionAnswerMode>" -		<< "<EnableBuddiesAndPresence>true</EnableBuddiesAndPresence>" -		<< "<BuddyManagementMode>Application</BuddyManagementMode>" -		<< "<ParticipantPropertyFrequency>5</ParticipantPropertyFrequency>" -		<< (autoPostCrashDumps?"<AutopostCrashDumps>true</AutopostCrashDumps>":"") -	<< "</Request>\n\n\n"; -	 -	writeString(stream.str()); -} - -void LLVoiceClient::logout() -{ -	// Ensure that we'll re-request provisioning before logging in again -	mAccountPassword.clear(); -	mVoiceAccountServerURI.clear(); -	 -	setState(stateLoggingOut); -	logoutSendMessage(); -} - -void LLVoiceClient::logoutSendMessage() -{ -	if(!mAccountHandle.empty()) -	{ -		std::ostringstream stream; -		stream -		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.Logout.1\">" -			<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" -		<< "</Request>" -		<< "\n\n\n"; - -		mAccountHandle.clear(); - -		writeString(stream.str()); -	} -} +/////////////////////////////////////////////////////////////////////////////////////////////// -void LLVoiceClient::accountListBlockRulesSendMessage() +LLVoiceClient::LLVoiceClient()  { -	if(!mAccountHandle.empty()) -	{		 -		std::ostringstream stream; - -		LL_DEBUGS("Voice") << "requesting block rules" << LL_ENDL; - -		stream -		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ListBlockRules.1\">" -			<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" -		<< "</Request>" -		<< "\n\n\n"; - -		writeString(stream.str()); -	} +	mVoiceModule = NULL;  } -void LLVoiceClient::accountListAutoAcceptRulesSendMessage() -{ -	if(!mAccountHandle.empty()) -	{		 -		std::ostringstream stream; - -		LL_DEBUGS("Voice") << "requesting auto-accept rules" << LL_ENDL; - -		stream -		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ListAutoAcceptRules.1\">" -			<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" -		<< "</Request>" -		<< "\n\n\n"; - -		writeString(stream.str()); -	} -} +//--------------------------------------------------- +// Basic setup/shutdown -void LLVoiceClient::sessionGroupCreateSendMessage() +LLVoiceClient::~LLVoiceClient()  { -	if(!mAccountHandle.empty()) -	{		 -		std::ostringstream stream; - -		LL_DEBUGS("Voice") << "creating session group" << LL_ENDL; - -		stream -		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.Create.1\">" -			<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" -			<< "<Type>Normal</Type>" -		<< "</Request>" -		<< "\n\n\n"; - -		writeString(stream.str()); -	}  } -void LLVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAudio, bool startText) +void LLVoiceClient::init(LLPumpIO *pump)  { -	LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL; -	 -	session->mCreateInProgress = true; -	if(startAudio) -	{ -		session->mMediaConnectInProgress = true; -	} - -	std::ostringstream stream; -	stream -	<< "<Request requestId=\"" << session->mSIPURI << "\" action=\"Session.Create.1\">" -		<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" -		<< "<URI>" << session->mSIPURI << "</URI>"; - -	static const std::string allowed_chars = -				"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" -				"0123456789" -				"-._~"; - -	if(!session->mHash.empty()) -	{ -		stream -			<< "<Password>" << LLURI::escape(session->mHash, allowed_chars) << "</Password>" -			<< "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>"; -	} -	 -	stream -		<< "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>" -		<< "<ConnectText>" << (startText?"true":"false") << "</ConnectText>" -		<< "<Name>" << mChannelName << "</Name>" -	<< "</Request>\n\n\n"; -	writeString(stream.str()); +	// Initialize all of the voice modules +	m_servicePump = pump;  } -void LLVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio, bool startText) +void LLVoiceClient::userAuthorized(const std::string& user_id, const LLUUID &agentID)  { -	LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL; -	 -	session->mCreateInProgress = true; -	if(startAudio) +	// In the future, we should change this to allow voice module registration +	// with a table lookup of sorts. +	std::string voice_server = gSavedSettings.getString("VoiceServerType"); +	LL_DEBUGS("Voice") << "voice server type " << voice_server << LL_ENDL; +	if(voice_server == "diamondware")  	{ -		session->mMediaConnectInProgress = true; +		mVoiceModule = (LLVoiceModuleInterface *)LLDiamondwareVoiceClient::getInstance();  	} -	 -	std::string password; -	if(!session->mHash.empty()) +	else if(voice_server == "vivox")  	{ -		static const std::string allowed_chars = -					"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" -					"0123456789" -					"-._~" -					; -		password = LLURI::escape(session->mHash, allowed_chars); -	} - -	std::ostringstream stream; -	stream -	<< "<Request requestId=\"" << session->mSIPURI << "\" action=\"SessionGroup.AddSession.1\">" -		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>" -		<< "<URI>" << session->mSIPURI << "</URI>" -		<< "<Name>" << mChannelName << "</Name>" -		<< "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>" -		<< "<ConnectText>" << (startText?"true":"false") << "</ConnectText>" -		<< "<Password>" << password << "</Password>" -		<< "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>" -	<< "</Request>\n\n\n" -	; -	 -	writeString(stream.str()); -} - -void LLVoiceClient::sessionMediaConnectSendMessage(sessionState *session) -{ -	LL_DEBUGS("Voice") << "connecting audio to session handle: " << session->mHandle << LL_ENDL; - -	session->mMediaConnectInProgress = true; -	 -	std::ostringstream stream; - -	stream -	<< "<Request requestId=\"" << session->mHandle << "\" action=\"Session.MediaConnect.1\">" -		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>" -		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>" -		<< "<Media>Audio</Media>" -	<< "</Request>\n\n\n"; - -	writeString(stream.str()); -} - -void LLVoiceClient::sessionTextConnectSendMessage(sessionState *session) -{ -	LL_DEBUGS("Voice") << "connecting text to session handle: " << session->mHandle << LL_ENDL; -	 -	std::ostringstream stream; - -	stream -	<< "<Request requestId=\"" << session->mHandle << "\" action=\"Session.TextConnect.1\">" -		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>" -		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>" -	<< "</Request>\n\n\n"; - -	writeString(stream.str()); -} - -void LLVoiceClient::sessionTerminate() -{ -	mSessionTerminateRequested = true; -} - -void LLVoiceClient::requestRelog() -{ -	mSessionTerminateRequested = true; -	mRelogRequested = true; -} - - -void LLVoiceClient::leaveAudioSession() -{ -	if(mAudioSession) -	{ -		LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mSIPURI << LL_ENDL; - -		switch(getState()) -		{ -			case stateNoChannel: -				// In this case, we want to pretend the join failed so our state machine doesn't get stuck. -				// Skip the join failed transition state so we don't send out error notifications. -				setState(stateJoinSessionFailedWaiting); -			break; -			case stateJoiningSession: -			case stateSessionJoined: -			case stateRunning: -				if(!mAudioSession->mHandle.empty()) -				{ - -#if RECORD_EVERYTHING -					// HACK: for testing only -					// Save looped recording -					std::string savepath("/tmp/vivoxrecording"); -					{ -						time_t now = time(NULL); -						const size_t BUF_SIZE = 64; -						char time_str[BUF_SIZE];	/* Flawfinder: ignore */ -						 -						strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now)); -						savepath += time_str; -					} -					recordingLoopSave(savepath); -#endif - -					sessionMediaDisconnectSendMessage(mAudioSession); -					setState(stateLeavingSession); -				} -				else -				{ -					LL_WARNS("Voice") << "called with no session handle" << LL_ENDL;	 -					setState(stateSessionTerminated); -				} -			break; -			case stateJoinSessionFailed: -			case stateJoinSessionFailedWaiting: -				setState(stateSessionTerminated); -			break; -			 -			default: -				LL_WARNS("Voice") << "called from unknown state" << LL_ENDL; -			break; -		} +		mVoiceModule = (LLVoiceModuleInterface *)LLVivoxVoiceClient::getInstance();  	}  	else  	{ -		LL_WARNS("Voice") << "called with no active session" << LL_ENDL; -		setState(stateSessionTerminated); +		mVoiceModule = NULL; +		return;   	} +	mVoiceModule->init(m_servicePump);	 +	mVoiceModule->userAuthorized(user_id, agentID);  } -void LLVoiceClient::sessionTerminateSendMessage(sessionState *session) -{ -	std::ostringstream stream; -	 -	LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL;	 -	stream -	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Terminate.1\">" -		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>" -	<< "</Request>\n\n\n"; -	 -	writeString(stream.str()); -} - -void LLVoiceClient::sessionGroupTerminateSendMessage(sessionState *session) -{ -	std::ostringstream stream; -	 -	LL_DEBUGS("Voice") << "Sending SessionGroup.Terminate with handle " << session->mGroupHandle << LL_ENDL;	 -	stream -	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.Terminate.1\">" -		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>" -	<< "</Request>\n\n\n"; -	 -	writeString(stream.str()); -} - -void LLVoiceClient::sessionMediaDisconnectSendMessage(sessionState *session) -{ -	std::ostringstream stream; -	 -	LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL;	 -	stream -	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.MediaDisconnect.1\">" -		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>" -		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>" -		<< "<Media>Audio</Media>" -	<< "</Request>\n\n\n"; -	 -	writeString(stream.str()); -	 -} - -void LLVoiceClient::sessionTextDisconnectSendMessage(sessionState *session) -{ -	std::ostringstream stream; -	 -	LL_DEBUGS("Voice") << "Sending Session.TextDisconnect with handle " << session->mHandle << LL_ENDL;	 -	stream -	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.TextDisconnect.1\">" -		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>" -		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>" -	<< "</Request>\n\n\n"; -	 -	writeString(stream.str()); -} -void LLVoiceClient::getCaptureDevicesSendMessage() -{ -	std::ostringstream stream; -	stream -	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.GetCaptureDevices.1\">" -	<< "</Request>\n\n\n"; -	 -	writeString(stream.str()); -} - -void LLVoiceClient::getRenderDevicesSendMessage() -{ -	std::ostringstream stream; -	stream -	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.GetRenderDevices.1\">" -	<< "</Request>\n\n\n"; -	 -	writeString(stream.str()); -} - -void LLVoiceClient::clearCaptureDevices() -{ -	LL_DEBUGS("Voice") << "called" << LL_ENDL; -	mCaptureDevices.clear(); -} - -void LLVoiceClient::addCaptureDevice(const std::string& name) -{ -	LL_DEBUGS("Voice") << name << LL_ENDL; - -	mCaptureDevices.push_back(name); -} - -LLVoiceClient::deviceList *LLVoiceClient::getCaptureDevices() +void LLVoiceClient::terminate()  { -	return &mCaptureDevices; +	if (mVoiceModule) mVoiceModule->terminate(); +	mVoiceModule = NULL;  } -void LLVoiceClient::setCaptureDevice(const std::string& name) +const LLVoiceVersionInfo LLVoiceClient::getVersion()  { -	if(name == "Default") +	if (mVoiceModule)   	{ -		if(!mCaptureDevice.empty()) -		{ -			mCaptureDevice.clear(); -			mCaptureDeviceDirty = true;	 -		} +		return mVoiceModule->getVersion();  	}  	else  	{ -		if(mCaptureDevice != name) -		{ -			mCaptureDevice = name; -			mCaptureDeviceDirty = true;	 -		} +		LLVoiceVersionInfo result; +		result.serverVersion = std::string(); +		result.serverType = std::string(); +		return result;  	}  } -void LLVoiceClient::clearRenderDevices() -{	 -	LL_DEBUGS("Voice") << "called" << LL_ENDL; -	mRenderDevices.clear(); -} - -void LLVoiceClient::addRenderDevice(const std::string& name) -{ -	LL_DEBUGS("Voice") << name << LL_ENDL; -	mRenderDevices.push_back(name); -} - -LLVoiceClient::deviceList *LLVoiceClient::getRenderDevices() +void LLVoiceClient::updateSettings()  { -	return &mRenderDevices; +	if (mVoiceModule) mVoiceModule->updateSettings();  } -void LLVoiceClient::setRenderDevice(const std::string& name) -{ -	if(name == "Default") -	{ -		if(!mRenderDevice.empty()) -		{ -			mRenderDevice.clear(); -			mRenderDeviceDirty = true;	 -		} -	} -	else -	{ -		if(mRenderDevice != name) -		{ -			mRenderDevice = name; -			mRenderDeviceDirty = true;	 -		} -	} -	 -} +//-------------------------------------------------- +// tuning  void LLVoiceClient::tuningStart()  { -	mTuningMode = true; -	if(getState() >= stateNoChannel) -	{ -		sessionTerminate(); -	} +	if (mVoiceModule) mVoiceModule->tuningStart();  }  void LLVoiceClient::tuningStop()  { -	mTuningMode = false; +	if (mVoiceModule) mVoiceModule->tuningStop();  }  bool LLVoiceClient::inTuningMode()  { -	bool result = false; -	switch(getState()) -	{ -	case stateMicTuningRunning: -		result = true; -		break; -	default: -		break; -	} -	return result; -} - -void LLVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop) -{		 -	mTuningAudioFile = name; -	std::ostringstream stream; -	stream -	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.RenderAudioStart.1\">" -    << "<SoundFilePath>" << mTuningAudioFile << "</SoundFilePath>" -    << "<Loop>" << (loop?"1":"0") << "</Loop>" -	<< "</Request>\n\n\n"; -	 -	writeString(stream.str()); -} - -void LLVoiceClient::tuningRenderStopSendMessage() -{ -	std::ostringstream stream; -	stream -	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.RenderAudioStop.1\">" -    << "<SoundFilePath>" << mTuningAudioFile << "</SoundFilePath>" -	<< "</Request>\n\n\n"; -	 -	writeString(stream.str()); -} - -void LLVoiceClient::tuningCaptureStartSendMessage(int duration) -{ -	LL_DEBUGS("Voice") << "sending CaptureAudioStart" << LL_ENDL; -	 -	std::ostringstream stream; -	stream -	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStart.1\">" -    << "<Duration>" << duration << "</Duration>" -	<< "</Request>\n\n\n"; -	 -	writeString(stream.str()); -} - -void LLVoiceClient::tuningCaptureStopSendMessage() -{ -	LL_DEBUGS("Voice") << "sending CaptureAudioStop" << LL_ENDL; -	 -	std::ostringstream stream; -	stream -	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStop.1\">" -	<< "</Request>\n\n\n"; -	 -	writeString(stream.str()); - -	mTuningEnergy = 0.0f; -} - -void LLVoiceClient::tuningSetMicVolume(float volume) -{ -	int scaled_volume = scale_mic_volume(volume); - -	if(scaled_volume != mTuningMicVolume) -	{ -		mTuningMicVolume = scaled_volume; -		mTuningMicVolumeDirty = true; -	} -} - -void LLVoiceClient::tuningSetSpeakerVolume(float volume) -{ -	int scaled_volume = scale_speaker_volume(volume);	 - -	if(scaled_volume != mTuningSpeakerVolume) +	if (mVoiceModule)   	{ -		mTuningSpeakerVolume = scaled_volume; -		mTuningSpeakerVolumeDirty = true; -	} -} -				 -float LLVoiceClient::tuningGetEnergy(void) -{ -	return mTuningEnergy; -} - -bool LLVoiceClient::deviceSettingsAvailable() -{ -	bool result = true; -	 -	if(!mConnected) -		result = false; -	 -	if(mRenderDevices.empty()) -		result = false; -	 -	return result; -} - -void LLVoiceClient::refreshDeviceLists(bool clearCurrentList) -{ -	if(clearCurrentList) -	{ -		clearCaptureDevices(); -		clearRenderDevices(); -	} -	getCaptureDevicesSendMessage(); -	getRenderDevicesSendMessage(); -} - -void LLVoiceClient::daemonDied() -{ -	// The daemon died, so the connection is gone.  Reset everything and start over. -	LL_WARNS("Voice") << "Connection to vivox daemon lost.  Resetting state."<< LL_ENDL; - -	// Try to relaunch the daemon -	setState(stateDisableCleanup); -} - -void LLVoiceClient::giveUp() -{ -	// All has failed.  Clean up and stop trying. -	closeSocket(); -	deleteAllSessions(); -	deleteAllBuddies(); -	 -	setState(stateJail); -} - -static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVector3d &pos, LLVector3 &vel) -{ -	F32 nat[3], nup[3], nl[3], nvel[3]; // the new at, up, left vectors and the  new position and velocity -	F64 npos[3]; -	 -	// The original XML command was sent like this: -	/* -			<< "<Position>" -				<< "<X>" << pos[VX] << "</X>" -				<< "<Y>" << pos[VZ] << "</Y>" -				<< "<Z>" << pos[VY] << "</Z>" -			<< "</Position>" -			<< "<Velocity>" -				<< "<X>" << mAvatarVelocity[VX] << "</X>" -				<< "<Y>" << mAvatarVelocity[VZ] << "</Y>" -				<< "<Z>" << mAvatarVelocity[VY] << "</Z>" -			<< "</Velocity>" -			<< "<AtOrientation>" -				<< "<X>" << l.mV[VX] << "</X>" -				<< "<Y>" << u.mV[VX] << "</Y>" -				<< "<Z>" << a.mV[VX] << "</Z>" -			<< "</AtOrientation>" -			<< "<UpOrientation>" -				<< "<X>" << l.mV[VZ] << "</X>" -				<< "<Y>" << u.mV[VY] << "</Y>" -				<< "<Z>" << a.mV[VZ] << "</Z>" -			<< "</UpOrientation>" -			<< "<LeftOrientation>" -				<< "<X>" << l.mV [VY] << "</X>" -				<< "<Y>" << u.mV [VZ] << "</Y>" -				<< "<Z>" << a.mV [VY] << "</Z>" -			<< "</LeftOrientation>"; -	*/ - -#if 1 -	// This was the original transform done when building the XML command -	nat[0] = left.mV[VX]; -	nat[1] = up.mV[VX]; -	nat[2] = at.mV[VX]; - -	nup[0] = left.mV[VZ]; -	nup[1] = up.mV[VY]; -	nup[2] = at.mV[VZ]; - -	nl[0] = left.mV[VY]; -	nl[1] = up.mV[VZ]; -	nl[2] = at.mV[VY]; - -	npos[0] = pos.mdV[VX]; -	npos[1] = pos.mdV[VZ]; -	npos[2] = pos.mdV[VY]; - -	nvel[0] = vel.mV[VX]; -	nvel[1] = vel.mV[VZ]; -	nvel[2] = vel.mV[VY]; - -	for(int i=0;i<3;++i) { -		at.mV[i] = nat[i]; -		up.mV[i] = nup[i]; -		left.mV[i] = nl[i]; -		pos.mdV[i] = npos[i]; -	} -	 -	// This was the original transform done in the SDK -	nat[0] = at.mV[2]; -	nat[1] = 0; // y component of at vector is always 0, this was up[2] -	nat[2] = -1 * left.mV[2]; - -	// We override whatever the application gives us -	nup[0] = 0; // x component of up vector is always 0 -	nup[1] = 1; // y component of up vector is always 1 -	nup[2] = 0; // z component of up vector is always 0 - -	nl[0] = at.mV[0]; -	nl[1] = 0;  // y component of left vector is always zero, this was up[0] -	nl[2] = -1 * left.mV[0]; - -	npos[2] = pos.mdV[2] * -1.0; -	npos[1] = pos.mdV[1]; -	npos[0] = pos.mdV[0]; - -	for(int i=0;i<3;++i) { -		at.mV[i] = nat[i]; -		up.mV[i] = nup[i]; -		left.mV[i] = nl[i]; -		pos.mdV[i] = npos[i]; -	} -#else -	// This is the compose of the two transforms (at least, that's what I'm trying for) -	nat[0] = at.mV[VX]; -	nat[1] = 0; // y component of at vector is always 0, this was up[2] -	nat[2] = -1 * up.mV[VZ]; - -	// We override whatever the application gives us -	nup[0] = 0; // x component of up vector is always 0 -	nup[1] = 1; // y component of up vector is always 1 -	nup[2] = 0; // z component of up vector is always 0 - -	nl[0] = left.mV[VX]; -	nl[1] = 0;  // y component of left vector is always zero, this was up[0] -	nl[2] = -1 * left.mV[VY]; - -	npos[0] = pos.mdV[VX]; -	npos[1] = pos.mdV[VZ]; -	npos[2] = pos.mdV[VY] * -1.0; - -	nvel[0] = vel.mV[VX]; -	nvel[1] = vel.mV[VZ]; -	nvel[2] = vel.mV[VY]; - -	for(int i=0;i<3;++i) { -		at.mV[i] = nat[i]; -		up.mV[i] = nup[i]; -		left.mV[i] = nl[i]; -		pos.mdV[i] = npos[i]; +		return mVoiceModule->inTuningMode();  	} -	 -#endif -} - -void LLVoiceClient::sendPositionalUpdate(void) -{	 -	std::ostringstream stream; -	 -	if(mSpatialCoordsDirty) -	{ -		LLVector3 l, u, a, vel; -		LLVector3d pos; - -		mSpatialCoordsDirty = false; -		 -		// Always send both speaker and listener positions together. -		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Set3DPosition.1\">"		 -			<< "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>"; -		 -		stream << "<SpeakerPosition>"; - -//		LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL; -		l = mAvatarRot.getLeftRow(); -		u = mAvatarRot.getUpRow(); -		a = mAvatarRot.getFwdRow(); -		pos = mAvatarPosition; -		vel = mAvatarVelocity; - -		// SLIM SDK: the old SDK was doing a transform on the passed coordinates that the new one doesn't do anymore. -		// The old transform is replicated by this function. -		oldSDKTransform(l, u, a, pos, vel); -		 -		stream  -			<< "<Position>" -				<< "<X>" << pos.mdV[VX] << "</X>" -				<< "<Y>" << pos.mdV[VY] << "</Y>" -				<< "<Z>" << pos.mdV[VZ] << "</Z>" -			<< "</Position>" -			<< "<Velocity>" -				<< "<X>" << vel.mV[VX] << "</X>" -				<< "<Y>" << vel.mV[VY] << "</Y>" -				<< "<Z>" << vel.mV[VZ] << "</Z>" -			<< "</Velocity>" -			<< "<AtOrientation>" -				<< "<X>" << a.mV[VX] << "</X>" -				<< "<Y>" << a.mV[VY] << "</Y>" -				<< "<Z>" << a.mV[VZ] << "</Z>" -			<< "</AtOrientation>" -			<< "<UpOrientation>" -				<< "<X>" << u.mV[VX] << "</X>" -				<< "<Y>" << u.mV[VY] << "</Y>" -				<< "<Z>" << u.mV[VZ] << "</Z>" -			<< "</UpOrientation>" -			<< "<LeftOrientation>" -				<< "<X>" << l.mV [VX] << "</X>" -				<< "<Y>" << l.mV [VY] << "</Y>" -				<< "<Z>" << l.mV [VZ] << "</Z>" -			<< "</LeftOrientation>"; - -		stream << "</SpeakerPosition>"; - -		stream << "<ListenerPosition>"; - -		LLVector3d	earPosition; -		LLVector3	earVelocity; -		LLMatrix3	earRot; -		 -		switch(mEarLocation) -		{ -			case earLocCamera: -			default: -				earPosition = mCameraPosition; -				earVelocity = mCameraVelocity; -				earRot = mCameraRot; -			break; -			 -			case earLocAvatar: -				earPosition = mAvatarPosition; -				earVelocity = mAvatarVelocity; -				earRot = mAvatarRot; -			break; -			 -			case earLocMixed: -				earPosition = mAvatarPosition; -				earVelocity = mAvatarVelocity; -				earRot = mCameraRot; -			break; -		} - -		l = earRot.getLeftRow(); -		u = earRot.getUpRow(); -		a = earRot.getFwdRow(); -		pos = earPosition; -		vel = earVelocity; - -//		LL_DEBUGS("Voice") << "Sending listener position " << earPosition << LL_ENDL; -		 -		oldSDKTransform(l, u, a, pos, vel); -		 -		stream  -			<< "<Position>" -				<< "<X>" << pos.mdV[VX] << "</X>" -				<< "<Y>" << pos.mdV[VY] << "</Y>" -				<< "<Z>" << pos.mdV[VZ] << "</Z>" -			<< "</Position>" -			<< "<Velocity>" -				<< "<X>" << vel.mV[VX] << "</X>" -				<< "<Y>" << vel.mV[VY] << "</Y>" -				<< "<Z>" << vel.mV[VZ] << "</Z>" -			<< "</Velocity>" -			<< "<AtOrientation>" -				<< "<X>" << a.mV[VX] << "</X>" -				<< "<Y>" << a.mV[VY] << "</Y>" -				<< "<Z>" << a.mV[VZ] << "</Z>" -			<< "</AtOrientation>" -			<< "<UpOrientation>" -				<< "<X>" << u.mV[VX] << "</X>" -				<< "<Y>" << u.mV[VY] << "</Y>" -				<< "<Z>" << u.mV[VZ] << "</Z>" -			<< "</UpOrientation>" -			<< "<LeftOrientation>" -				<< "<X>" << l.mV [VX] << "</X>" -				<< "<Y>" << l.mV [VY] << "</Y>" -				<< "<Z>" << l.mV [VZ] << "</Z>" -			<< "</LeftOrientation>"; - - -		stream << "</ListenerPosition>"; - -		stream << "</Request>\n\n\n"; -	}	 -	 -	if(mAudioSession && (mAudioSession->mVolumeDirty || mAudioSession->mMuteDirty)) -	{ -		participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin(); - -		mAudioSession->mVolumeDirty = false; -		mAudioSession->mMuteDirty = false; -		 -		for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) -		{ -			participantState *p = iter->second; - -			if(p->mVolumeDirty) -			{ -				// Can't set volume/mute for yourself -				if(!p->mIsSelf) -				{ -					// scale from the range 0.0-1.0 to vivox volume in the range 0-100 -					S32 volume = llround(p->mVolume / VOLUME_SCALE_VIVOX); - -					bool mute = p->mOnMuteList; - -					if(mute) -					{ -						// SetParticipantMuteForMe doesn't work in p2p sessions. -						// If we want the user to be muted, set their volume to 0 as well. -						// This isn't perfect, but it will at least reduce their volume to a minimum. -						volume = 0; - -						// Mark the current volume level as set to prevent incoming events -						// changing it to 0, so that we can return to it when unmuting. -						p->mVolumeSet = true; -					} - -					if(volume == 0) -					{ -						mute = true; -					} - -					LL_DEBUGS("Voice") << "Setting volume/mute for avatar " << p->mAvatarID << " to " << volume << (mute?"/true":"/false") << LL_ENDL; -					 -					// SLIM SDK: Send both volume and mute commands. -					 -					// Send a "volume for me" command for the user. -					stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantVolumeForMe.1\">" -						<< "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>" -						<< "<ParticipantURI>" << p->mURI << "</ParticipantURI>" -						<< "<Volume>" << volume << "</Volume>" -						<< "</Request>\n\n\n"; - -					if(!mAudioSession->mIsP2P) -					{ -						// Send a "mute for me" command for the user -						// Doesn't work in P2P sessions -						stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantMuteForMe.1\">" -							<< "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>" -							<< "<ParticipantURI>" << p->mURI << "</ParticipantURI>" -							<< "<Mute>" << (mute?"1":"0") << "</Mute>" -							<< "<Scope>Audio</Scope>" -							<< "</Request>\n\n\n"; -					} -				} -				 -				p->mVolumeDirty = false; -			} -		} -	} -			 -	buildLocalAudioUpdates(stream); -	 -	if(!stream.str().empty()) +	else  	{ -		writeString(stream.str()); +		return false;  	} -	 -	// Friends list updates can be huge, especially on the first voice login of an account with lots of friends. -	// Batching them all together can choke SLVoice, so send them in separate writes. -	sendFriendsListUpdates();  } -void LLVoiceClient::buildSetCaptureDevice(std::ostringstream &stream) +void LLVoiceClient::tuningSetMicVolume(float volume)  { -	if(mCaptureDeviceDirty) -	{ -		LL_DEBUGS("Voice") << "Setting input device = \"" << mCaptureDevice << "\"" << LL_ENDL; -	 -		stream  -		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetCaptureDevice.1\">" -			<< "<CaptureDeviceSpecifier>" << mCaptureDevice << "</CaptureDeviceSpecifier>" -		<< "</Request>" -		<< "\n\n\n"; -		 -		mCaptureDeviceDirty = false; -	} +	if (mVoiceModule) mVoiceModule->tuningSetMicVolume(volume);  } -void LLVoiceClient::buildSetRenderDevice(std::ostringstream &stream) -{ -	if(mRenderDeviceDirty) -	{ -		LL_DEBUGS("Voice") << "Setting output device = \"" << mRenderDevice << "\"" << LL_ENDL; - -		stream -		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetRenderDevice.1\">" -			<< "<RenderDeviceSpecifier>" << mRenderDevice << "</RenderDeviceSpecifier>" -		<< "</Request>" -		<< "\n\n\n"; -		mRenderDeviceDirty = false; -	} -} - -void LLVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream) +void LLVoiceClient::tuningSetSpeakerVolume(float volume)  { -	buildSetCaptureDevice(stream); - -	buildSetRenderDevice(stream); - -	if(mPTTDirty) -	{ -		mPTTDirty = false; - -		// Send a local mute command. -		// NOTE that the state of "PTT" is the inverse of "local mute". -		//   (i.e. when PTT is true, we send a mute command with "false", and vice versa) -		 -		LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter " << (mPTT?"false":"true") << LL_ENDL; - -		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">" -			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" -			<< "<Value>" << (mPTT?"false":"true") << "</Value>" -			<< "</Request>\n\n\n"; -		 -	} - -	if(mSpeakerMuteDirty) -	{ -		const char *muteval = ((mSpeakerVolume <= scale_speaker_volume(0))?"true":"false"); - -		mSpeakerMuteDirty = false; - -		LL_INFOS("Voice") << "Setting speaker mute to " << muteval  << LL_ENDL; -		 -		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalSpeaker.1\">" -			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" -			<< "<Value>" << muteval << "</Value>" -			<< "</Request>\n\n\n";	 -		 -	} -	 -	if(mSpeakerVolumeDirty) -	{ -		mSpeakerVolumeDirty = false; - -		LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume  << LL_ENDL; - -		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalSpeakerVolume.1\">" -			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" -			<< "<Value>" << mSpeakerVolume << "</Value>" -			<< "</Request>\n\n\n"; -			 -	} -	 -	if(mMicVolumeDirty) -	{ -		mMicVolumeDirty = false; - -		LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume  << LL_ENDL; - -		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalMicVolume.1\">" -			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" -			<< "<Value>" << mMicVolume << "</Value>" -			<< "</Request>\n\n\n";				 -	} - -	 +	if (mVoiceModule) mVoiceModule->tuningSetSpeakerVolume(volume);  } -void LLVoiceClient::checkFriend(const LLUUID& id) +float LLVoiceClient::tuningGetEnergy(void)  { -	std::string name; -	buddyListEntry *buddy = findBuddy(id); - -	// Make sure we don't add a name before it's been looked up. -	if(gCacheName->getFullName(id, name)) +	if (mVoiceModule)   	{ - -		const LLRelationship* relationInfo = LLAvatarTracker::instance().getBuddyInfo(id); -		bool canSeeMeOnline = false; -		if(relationInfo && relationInfo->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS)) -			canSeeMeOnline = true; -		 -		// When we get here, mNeedsSend is true and mInSLFriends is false.  Change them as necessary. -		 -		if(buddy) -		{ -			// This buddy is already in both lists. - -			if(name != buddy->mDisplayName) -			{ -				// The buddy is in the list with the wrong name.  Update it with the correct name. -				LL_WARNS("Voice") << "Buddy " << id << " has wrong name (\"" << buddy->mDisplayName << "\" should be \"" << name << "\"), updating."<< LL_ENDL; -				buddy->mDisplayName = name; -				buddy->mNeedsNameUpdate = true;		// This will cause the buddy to be resent. -			} -		} -		else -		{ -			// This buddy was not in the vivox list, needs to be added. -			buddy = addBuddy(sipURIFromID(id), name); -			buddy->mUUID = id; -		} -		 -		// In all the above cases, the buddy is in the SL friends list (which is how we got here). -		buddy->mInSLFriends = true; -		buddy->mCanSeeMeOnline = canSeeMeOnline; -		buddy->mNameResolved = true; -		 +		return mVoiceModule->tuningGetEnergy();  	}  	else  	{ -		// This name hasn't been looked up yet.  Don't do anything with this buddy list entry until it has. -		if(buddy) -		{ -			buddy->mNameResolved = false; -		} -		 -		// Initiate a lookup. -		// The "lookup completed" callback will ensure that the friends list is rechecked after it completes. -		lookupName(id); +		return 0.0;  	}  } -void LLVoiceClient::clearAllLists() -{ -	// FOR TESTING ONLY -	 -	// This will send the necessary commands to delete ALL buddies, autoaccept rules, and block rules SLVoice tells us about. -	buddyListMap::iterator buddy_it; -	for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();) -	{ -		buddyListEntry *buddy = buddy_it->second; -		buddy_it++; -		 -		std::ostringstream stream; -		if(buddy->mInVivoxBuddies) -		{ -			// delete this entry from the vivox buddy list -			buddy->mInVivoxBuddies = false; -			LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL; -			stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.BuddyDelete.1\">" -				<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" -				<< "<BuddyURI>" << buddy->mURI << "</BuddyURI>" -				<< "</Request>\n\n\n";		 -		} - -		if(buddy->mHasBlockListEntry) -		{ -			// Delete the associated block list entry (so the block list doesn't fill up with junk) -			buddy->mHasBlockListEntry = false; -			stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteBlockRule.1\">" -				<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" -				<< "<BlockMask>" << buddy->mURI << "</BlockMask>" -				<< "</Request>\n\n\n";								 -		} -		if(buddy->mHasAutoAcceptListEntry) -		{ -			// Delete the associated auto-accept list entry (so the auto-accept list doesn't fill up with junk) -			buddy->mHasAutoAcceptListEntry = false; -			stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteAutoAcceptRule.1\">" -				<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" -				<< "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>" -				<< "</Request>\n\n\n"; -		} +//------------------------------------------------ +// devices -		writeString(stream.str()); - -	} -} - -void LLVoiceClient::sendFriendsListUpdates() +bool LLVoiceClient::deviceSettingsAvailable()  { -	if(mBuddyListMapPopulated && mBlockRulesListReceived && mAutoAcceptRulesListReceived && mFriendsListDirty) -	{ -		mFriendsListDirty = false; -		 -		if(0) -		{ -			// FOR TESTING ONLY -- clear all buddy list, block list, and auto-accept list entries. -			clearAllLists(); -			return; -		} -		 -		LL_INFOS("Voice") << "Checking vivox buddy list against friends list..." << LL_ENDL; -		 -		buddyListMap::iterator buddy_it; -		for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++) -		{ -			// reset the temp flags in the local buddy list -			buddy_it->second->mInSLFriends = false; -		} -		 -		// correlate with the friends list -		{ -			LLCollectAllBuddies collect; -			LLAvatarTracker::instance().applyFunctor(collect); -			LLCollectAllBuddies::buddy_map_t::const_iterator it = collect.mOnline.begin(); -			LLCollectAllBuddies::buddy_map_t::const_iterator end = collect.mOnline.end(); -			 -			for ( ; it != end; ++it) -			{ -				checkFriend(it->second); -			} -			it = collect.mOffline.begin(); -			end = collect.mOffline.end(); -			for ( ; it != end; ++it) -			{ -				checkFriend(it->second); -			} -		} -				 -		LL_INFOS("Voice") << "Sending friend list updates..." << LL_ENDL; - -		for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();) -		{ -			buddyListEntry *buddy = buddy_it->second; -			buddy_it++; -			 -			// Ignore entries that aren't resolved yet. -			if(buddy->mNameResolved) -			{ -				std::ostringstream stream; - -				if(buddy->mInSLFriends && (!buddy->mInVivoxBuddies || buddy->mNeedsNameUpdate)) -				{					 -					if(mNumberOfAliases > 0) -					{ -						// Add (or update) this entry in the vivox buddy list -						buddy->mInVivoxBuddies = true; -						buddy->mNeedsNameUpdate = false; -						LL_DEBUGS("Voice") << "add/update " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL; -						stream  -							<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.BuddySet.1\">" -								<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" -								<< "<BuddyURI>" << buddy->mURI << "</BuddyURI>" -								<< "<DisplayName>" << buddy->mDisplayName << "</DisplayName>" -								<< "<BuddyData></BuddyData>"	// Without this, SLVoice doesn't seem to parse the command. -								<< "<GroupID>0</GroupID>" -							<< "</Request>\n\n\n";	 -					} -				} -				else if(!buddy->mInSLFriends) -				{ -					// This entry no longer exists in your SL friends list.  Remove all traces of it from the Vivox buddy list. - 					if(buddy->mInVivoxBuddies) -					{ -						// delete this entry from the vivox buddy list -						buddy->mInVivoxBuddies = false; -						LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL; -						stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.BuddyDelete.1\">" -							<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" -							<< "<BuddyURI>" << buddy->mURI << "</BuddyURI>" -							<< "</Request>\n\n\n";		 -					} - -					if(buddy->mHasBlockListEntry) -					{ -						// Delete the associated block list entry, if any -						buddy->mHasBlockListEntry = false; -						stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteBlockRule.1\">" -							<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" -							<< "<BlockMask>" << buddy->mURI << "</BlockMask>" -							<< "</Request>\n\n\n";								 -					} -					if(buddy->mHasAutoAcceptListEntry) -					{ -						// Delete the associated auto-accept list entry, if any -						buddy->mHasAutoAcceptListEntry = false; -						stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteAutoAcceptRule.1\">" -							<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" -							<< "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>" -							<< "</Request>\n\n\n"; -					} -				} -				 -				if(buddy->mInSLFriends) -				{ - -					if(buddy->mCanSeeMeOnline) -					{ -						// Buddy should not be blocked. - -						// If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent. -						 -						// If the buddy has a block list entry, delete it. -						if(buddy->mHasBlockListEntry) -						{ -							buddy->mHasBlockListEntry = false; -							stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteBlockRule.1\">" -								<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" -								<< "<BlockMask>" << buddy->mURI << "</BlockMask>" -								<< "</Request>\n\n\n";		 -							 -							 -							// If we just deleted a block list entry, add an auto-accept entry. -							if(!buddy->mHasAutoAcceptListEntry) -							{ -								buddy->mHasAutoAcceptListEntry = true;								 -								stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.CreateAutoAcceptRule.1\">" -									<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" -									<< "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>" -									<< "<AutoAddAsBuddy>0</AutoAddAsBuddy>" -									<< "</Request>\n\n\n"; -							} -						} -					} -					else -					{ -						// Buddy should be blocked. -						 -						// If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent. - -						// If this buddy has an autoaccept entry, delete it -						if(buddy->mHasAutoAcceptListEntry) -						{ -							buddy->mHasAutoAcceptListEntry = false; -							stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteAutoAcceptRule.1\">" -								<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" -								<< "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>" -								<< "</Request>\n\n\n"; -						 -							// If we just deleted an auto-accept entry, add a block list entry. -							if(!buddy->mHasBlockListEntry) -							{ -								buddy->mHasBlockListEntry = true; -								stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.CreateBlockRule.1\">" -									<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" -									<< "<BlockMask>" << buddy->mURI << "</BlockMask>" -									<< "<PresenceOnly>1</PresenceOnly>" -									<< "</Request>\n\n\n";								 -							} -						} -					} - -					if(!buddy->mInSLFriends && !buddy->mInVivoxBuddies) -					{ -						// Delete this entry from the local buddy list.  This should NOT invalidate the iterator, -						// since it has already been incremented to the next entry. -						deleteBuddy(buddy->mURI); -					} - -				} -				writeString(stream.str()); -			} -		} -	} -} - -///////////////////////////// -// Response/Event handlers - -void LLVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID) -{	 -	if(statusCode != 0) -	{ -		LL_WARNS("Voice") << "Connector.Create response failure: " << statusString << LL_ENDL; -		setState(stateConnectorFailed); -	} -	else -	{ -		// Connector created, move forward. -		LL_INFOS("Voice") << "Connector.Create succeeded, Vivox SDK version is " << versionID << LL_ENDL; -		mAPIVersion = versionID; -		mConnectorHandle = connectorHandle; -		if(getState() == stateConnectorStarting) -		{ -			setState(stateConnectorStarted); -		} -	} -} - -void LLVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases) -{  -	LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL; -	 -	// Status code of 20200 means "bad password".  We may want to special-case that at some point. -	 -	if ( statusCode == 401 ) -	{ -		// Login failure which is probably caused by the delay after a user's password being updated. -		LL_INFOS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; -		setState(stateLoginRetry); -	} -	else if(statusCode != 0) -	{ -		LL_WARNS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; -		setState(stateLoginFailed); -	} -	else -	{ -		// Login succeeded, move forward. -		mAccountHandle = accountHandle; -		mNumberOfAliases = numberOfAliases; -		// This needs to wait until the AccountLoginStateChangeEvent is received. -//		if(getState() == stateLoggingIn) -//		{ -//			setState(stateLoggedIn); -//		} -	} -} - -void LLVoiceClient::sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle) -{	 -	sessionState *session = findSessionBeingCreatedByURI(requestId); -	 -	if(session) -	{ -		session->mCreateInProgress = false; -	} -	 -	if(statusCode != 0) -	{ -		LL_WARNS("Voice") << "Session.Create response failure (" << statusCode << "): " << statusString << LL_ENDL; -		if(session) -		{ -			session->mErrorStatusCode = statusCode;		 -			session->mErrorStatusString = statusString; -			if(session == mAudioSession) -			{ -				setState(stateJoinSessionFailed); -			} -			else -			{ -				reapSession(session); -			} -		} -	} -	else -	{ -		LL_INFOS("Voice") << "Session.Create response received (success), session handle is " << sessionHandle << LL_ENDL; -		if(session) -		{ -			setSessionHandle(session, sessionHandle); -		} -	} -} - -void LLVoiceClient::sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle) -{	 -	sessionState *session = findSessionBeingCreatedByURI(requestId); -	 -	if(session) -	{ -		session->mCreateInProgress = false; -	} -	 -	if(statusCode != 0) +	if (mVoiceModule)   	{ -		LL_WARNS("Voice") << "SessionGroup.AddSession response failure (" << statusCode << "): " << statusString << LL_ENDL; -		if(session) -		{ -			session->mErrorStatusCode = statusCode;		 -			session->mErrorStatusString = statusString; -			if(session == mAudioSession) -			{ -				setState(stateJoinSessionFailed); -			} -			else -			{ -				reapSession(session); -			} -		} +		return mVoiceModule->deviceSettingsAvailable();  	}  	else  	{ -		LL_DEBUGS("Voice") << "SessionGroup.AddSession response received (success), session handle is " << sessionHandle << LL_ENDL; -		if(session) -		{ -			setSessionHandle(session, sessionHandle); -		} +		return false;  	}  } -void LLVoiceClient::sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString) +void LLVoiceClient::refreshDeviceLists(bool clearCurrentList)  { -	sessionState *session = findSession(requestId); -	if(statusCode != 0) -	{ -		LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL; -		if(session) -		{ -			session->mMediaConnectInProgress = false; -			session->mErrorStatusCode = statusCode;		 -			session->mErrorStatusString = statusString; -			if(session == mAudioSession) -				setState(stateJoinSessionFailed); -		} -	} -	else -	{ -		LL_DEBUGS("Voice") << "Session.Connect response received (success)" << LL_ENDL; -	} -} - -void LLVoiceClient::logoutResponse(int statusCode, std::string &statusString) -{	 -	if(statusCode != 0) -	{ -		LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL; -		// Should this ever fail?  do we care if it does? -	} +	if (mVoiceModule) mVoiceModule->refreshDeviceLists(clearCurrentList);  } -void LLVoiceClient::connectorShutdownResponse(int statusCode, std::string &statusString) +void LLVoiceClient::setCaptureDevice(const std::string& name)  { -	if(statusCode != 0) -	{ -		LL_WARNS("Voice") << "Connector.InitiateShutdown response failure: " << statusString << LL_ENDL; -		// Should this ever fail?  do we care if it does? -	} +	if (mVoiceModule) mVoiceModule->setCaptureDevice(name); -	mConnected = false; -	 -	if(getState() == stateConnectorStopping) -	{ -		setState(stateConnectorStopped); -	}  } -void LLVoiceClient::sessionAddedEvent( -		std::string &uriString,  -		std::string &alias,  -		std::string &sessionHandle,  -		std::string &sessionGroupHandle,  -		bool isChannel,  -		bool incoming, -		std::string &nameString, -		std::string &applicationString) +void LLVoiceClient::setRenderDevice(const std::string& name)  { -	sessionState *session = NULL; - -	LL_INFOS("Voice") << "session " << uriString << ", alias " << alias << ", name " << nameString << " handle " << sessionHandle << LL_ENDL; -	 -	session = addSession(uriString, sessionHandle); -	if(session) -	{ -		session->mGroupHandle = sessionGroupHandle; -		session->mIsChannel = isChannel; -		session->mIncoming = incoming; -		session->mAlias = alias; -			 -		// Generate a caller UUID -- don't need to do this for channels -		if(!session->mIsChannel) -		{ -			if(IDFromName(session->mSIPURI, session->mCallerID)) -			{ -				// Normal URI(base64-encoded UUID)  -			} -			else if(!session->mAlias.empty() && IDFromName(session->mAlias, session->mCallerID)) -			{ -				// Wrong URI, but an alias is available.  Stash the incoming URI as an alternate -				session->mAlternateSIPURI = session->mSIPURI; -				 -				// and generate a proper URI from the ID. -				setSessionURI(session, sipURIFromID(session->mCallerID)); -			} -			else -			{ -				LL_INFOS("Voice") << "Could not generate caller id from uri, using hash of uri " << session->mSIPURI << LL_ENDL; -				setUUIDFromStringHash(session->mCallerID, session->mSIPURI); -				session->mSynthesizedCallerID = true; -				 -				// Can't look up the name in this case -- we have to extract it from the URI. -				std::string namePortion = nameFromsipURI(session->mSIPURI); -				if(namePortion.empty()) -				{ -					// Didn't seem to be a SIP URI, just use the whole provided name. -					namePortion = nameString; -				} -				 -				// Some incoming names may be separated with an underscore instead of a space.  Fix this. -				LLStringUtil::replaceChar(namePortion, '_', ' '); -				 -				// Act like we just finished resolving the name (this stores it in all the right places) -				avatarNameResolved(session->mCallerID, namePortion); -			} -		 -			LL_INFOS("Voice") << "caller ID: " << session->mCallerID << LL_ENDL; - -			if(!session->mSynthesizedCallerID) -			{ -				// If we got here, we don't have a proper name.  Initiate a lookup. -				lookupName(session->mCallerID); -			} -		} -	} +	if (mVoiceModule) mVoiceModule->setRenderDevice(name);	  } -void LLVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle) +const LLVoiceDeviceList& LLVoiceClient::getCaptureDevices()  { -	LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL; -	 -#if USE_SESSION_GROUPS -	if(mMainSessionGroupHandle.empty()) +	static LLVoiceDeviceList nullCaptureDevices; +	if (mVoiceModule)   	{ -		// This is the first (i.e. "main") session group.  Save its handle. -		mMainSessionGroupHandle = sessionGroupHandle; +		return mVoiceModule->getCaptureDevices();  	}  	else  	{ -		LL_DEBUGS("Voice") << "Already had a session group handle " << mMainSessionGroupHandle << LL_ENDL; -	} -#endif -} - -void LLVoiceClient::joinedAudioSession(sessionState *session) -{ -	if(mAudioSession != session) -	{ -		sessionState *oldSession = mAudioSession; - -		mAudioSession = session; -		mAudioSessionChanged = true; - -		// The old session may now need to be deleted. -		reapSession(oldSession); -	} -	 -	// This is the session we're joining. -	if(getState() == stateJoiningSession) -	{ -		setState(stateSessionJoined); -		 -		// SLIM SDK: we don't always receive a participant state change for ourselves when joining a channel now. -		// Add the current user as a participant here. -		participantState *participant = session->addParticipant(sipURIFromName(mAccountName)); -		if(participant) -		{ -			participant->mIsSelf = true; -			lookupName(participant->mAvatarID); - -			LL_INFOS("Voice") << "added self as participant \"" << participant->mAccountName  -					<< "\" (" << participant->mAvatarID << ")"<< LL_ENDL; -		} -		 -		if(!session->mIsChannel) -		{ -			// this is a p2p session.  Make sure the other end is added as a participant. -			participantState *participant = session->addParticipant(session->mSIPURI); -			if(participant) -			{ -				if(participant->mAvatarIDValid) -				{ -					lookupName(participant->mAvatarID); -				} -				else if(!session->mName.empty()) -				{ -					participant->mDisplayName = session->mName; -					avatarNameResolved(participant->mAvatarID, session->mName); -				} -				 -				// TODO: Question: Do we need to set up mAvatarID/mAvatarIDValid here? -				LL_INFOS("Voice") << "added caller as participant \"" << participant->mAccountName  -						<< "\" (" << participant->mAvatarID << ")"<< LL_ENDL; -			} -		} +		return nullCaptureDevices;  	}  } -void LLVoiceClient::sessionRemovedEvent( -	std::string &sessionHandle,  -	std::string &sessionGroupHandle) -{ -	LL_INFOS("Voice") << "handle " << sessionHandle << LL_ENDL; -	 -	sessionState *session = findSession(sessionHandle); -	if(session) -	{ -		leftAudioSession(session); - -		// This message invalidates the session's handle.  Set it to empty. -		setSessionHandle(session); -		 -		// This also means that the session's session group is now empty. -		// Terminate the session group so it doesn't leak. -		sessionGroupTerminateSendMessage(session); -		 -		// Reset the media state (we now have no info) -		session->mMediaStreamState = streamStateUnknown; -		session->mTextStreamState = streamStateUnknown; -		 -		// Conditionally delete the session -		reapSession(session); -	} -	else -	{ -		LL_WARNS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL; -	} -} -void LLVoiceClient::reapSession(sessionState *session) +const LLVoiceDeviceList& LLVoiceClient::getRenderDevices()  { -	if(session) +	static LLVoiceDeviceList nullRenderDevices;	 +	if (mVoiceModule)   	{ -		if(!session->mHandle.empty()) -		{ -			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (non-null session handle)" << LL_ENDL; -		} -		else if(session->mCreateInProgress) -		{ -			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (create in progress)" << LL_ENDL; -		} -		else if(session->mMediaConnectInProgress) -		{ -			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (connect in progress)" << LL_ENDL; -		} -		else if(session == mAudioSession) -		{ -			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the current session)" << LL_ENDL; -		} -		else if(session == mNextAudioSession) -		{ -			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the next session)" << LL_ENDL; -		} -		else -		{ -			// TODO: Question: Should we check for queued text messages here? -			// We don't have a reason to keep tracking this session, so just delete it. -			LL_DEBUGS("Voice") << "deleting session " << session->mSIPURI << LL_ENDL; -			deleteSession(session); -			session = NULL; -		}	 +		return mVoiceModule->getRenderDevices();  	}  	else  	{ -//		LL_DEBUGS("Voice") << "session is NULL" << LL_ENDL; -	} -} - -// Returns true if the session seems to indicate we've moved to a region on a different voice server -bool LLVoiceClient::sessionNeedsRelog(sessionState *session) -{ -	bool result = false; -	 -	if(session != NULL) -	{ -		// Only make this check for spatial channels (so it won't happen for group or p2p calls) -		if(session->mIsSpatial) -		{ -			std::string::size_type atsign; -			 -			atsign = session->mSIPURI.find("@"); -			 -			if(atsign != std::string::npos) -			{ -				std::string urihost = session->mSIPURI.substr(atsign + 1); -				if(stricmp(urihost.c_str(), mVoiceSIPURIHostName.c_str())) -				{ -					// The hostname in this URI is different from what we expect.  This probably means we need to relog. -					 -					// We could make a ProvisionVoiceAccountRequest and compare the result with the current values of -					// mVoiceSIPURIHostName and mVoiceAccountServerURI to be really sure, but this is a pretty good indicator. -					 -					result = true; -				} -			} -		} -	} -	 -	return result; -} - -void LLVoiceClient::leftAudioSession( -	sessionState *session) -{ -	if(mAudioSession == session) -	{ -		switch(getState()) -		{ -			case stateJoiningSession: -			case stateSessionJoined: -			case stateRunning: -			case stateLeavingSession: -			case stateJoinSessionFailed: -			case stateJoinSessionFailedWaiting: -				// normal transition -				LL_DEBUGS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL; -				setState(stateSessionTerminated); -			break; -			 -			case stateSessionTerminated: -				// this will happen sometimes -- there are cases where we send the terminate and then go straight to this state. -				LL_WARNS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL; -			break; -			 -			default: -				LL_WARNS("Voice") << "unexpected SessionStateChangeEvent (left session) in state " << state2string(getState()) << LL_ENDL; -				setState(stateSessionTerminated); -			break; -		} +		return nullRenderDevices;  	}  } -void LLVoiceClient::accountLoginStateChangeEvent( -		std::string &accountHandle,  -		int statusCode,  -		std::string &statusString,  -		int state) -{ -	LL_DEBUGS("Voice") << "state is " << state << LL_ENDL; -	/* -		According to Mike S., status codes for this event are: -		login_state_logged_out=0, -        login_state_logged_in = 1, -        login_state_logging_in = 2, -        login_state_logging_out = 3, -        login_state_resetting = 4, -        login_state_error=100	 -	*/ -	 -	switch(state) -	{ -		case 1: -		if(getState() == stateLoggingIn) -		{ -			setState(stateLoggedIn); -		} -		break; - -		case 3: -			// The user is in the process of logging out. -			setState(stateLoggingOut); -		break; -		case 0: -			// The user has been logged out.   -			setState(stateLoggedOut); -		break; -		 -		default: -			//Used to be a commented out warning -			LL_DEBUGS("Voice") << "unknown state: " << state << LL_ENDL; -		break; -	} -} +//-------------------------------------------------- +// participants -void LLVoiceClient::mediaStreamUpdatedEvent( -	std::string &sessionHandle,  -	std::string &sessionGroupHandle,  -	int statusCode,  -	std::string &statusString,  -	int state,  -	bool incoming) +void LLVoiceClient::getParticipantList(std::set<LLUUID> &participants)  { -	sessionState *session = findSession(sessionHandle); -	 -	LL_DEBUGS("Voice") << "session " << sessionHandle << ", status code " << statusCode << ", string \"" << statusString << "\"" << LL_ENDL; -	 -	if(session) +	if (mVoiceModule)   	{ -		// We know about this session -		 -		// Save the state for later use -		session->mMediaStreamState = state; -		 -		switch(statusCode) -		{ -			case 0: -			case 200: -				// generic success -				// Don't change the saved error code (it may have been set elsewhere) -			break; -			default: -				// save the status code for later -				session->mErrorStatusCode = statusCode; -			break; -		} -		 -		switch(state) -		{ -			case streamStateIdle: -				// Standard "left audio session" -				session->mVoiceEnabled = false; -				session->mMediaConnectInProgress = false; -				leftAudioSession(session); -			break; - -			case streamStateConnected: -				session->mVoiceEnabled = true; -				session->mMediaConnectInProgress = false; -				joinedAudioSession(session); -			break; -			 -			case streamStateRinging: -				if(incoming) -				{ -					// Send the voice chat invite to the GUI layer -					// *TODO: Question: Should we correlate with the mute list here? -					session->mIMSessionID = LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID); -					session->mVoiceInvitePending = true; -					if(session->mName.empty()) -					{ -						lookupName(session->mCallerID); -					} -					else -					{ -						// Act like we just finished resolving the name -						avatarNameResolved(session->mCallerID, session->mName); -					} -				} -			break; -			 -			default: -				LL_WARNS("Voice") << "unknown state " << state << LL_ENDL; -			break; -			 -		} -		 +	  mVoiceModule->getParticipantList(participants);  	}  	else  	{ -		LL_WARNS("Voice") << "session " << sessionHandle << "not found"<< LL_ENDL; +	  participants = std::set<LLUUID>();  	}  } -void LLVoiceClient::textStreamUpdatedEvent( -	std::string &sessionHandle,  -	std::string &sessionGroupHandle,  -	bool enabled, -	int state,  -	bool incoming) +bool LLVoiceClient::isParticipant(const LLUUID &speaker_id)  { -	sessionState *session = findSession(sessionHandle); -	 -	if(session) -	{ -		// Save the state for later use -		session->mTextStreamState = state; -		 -		// We know about this session -		switch(state) -		{ -			case 0:	// We see this when the text stream closes -				LL_DEBUGS("Voice") << "stream closed" << LL_ENDL; -			break; -			 -			case 1:	// We see this on an incoming call from the Connector -				// Try to send any text messages queued for this session. -				sendQueuedTextMessages(session); - -				// Send the text chat invite to the GUI layer -				// TODO: Question: Should we correlate with the mute list here? -				session->mTextInvitePending = true; -				if(session->mName.empty()) -				{ -					lookupName(session->mCallerID); -				} -				else -				{ -					// Act like we just finished resolving the name -					avatarNameResolved(session->mCallerID, session->mName); -				} -			break; - -			default: -				LL_WARNS("Voice") << "unknown state " << state << LL_ENDL; -			break; -			 -		} -	} +  if(mVoiceModule) +    { +      return mVoiceModule->isParticipant(speaker_id); +    } +  return false;  } -void LLVoiceClient::participantAddedEvent( -		std::string &sessionHandle,  -		std::string &sessionGroupHandle,  -		std::string &uriString,  -		std::string &alias,  -		std::string &nameString,  -		std::string &displayNameString,  -		int participantType) -{ -	sessionState *session = findSession(sessionHandle); -	if(session) -	{ -		participantState *participant = session->addParticipant(uriString); -		if(participant) -		{ -			participant->mAccountName = nameString; - -			LL_DEBUGS("Voice") << "added participant \"" << participant->mAccountName  -					<< "\" (" << participant->mAvatarID << ")"<< LL_ENDL; -			if(participant->mAvatarIDValid) -			{ -				// Initiate a lookup -				lookupName(participant->mAvatarID); -			} -			else -			{ -				// If we don't have a valid avatar UUID, we need to fill in the display name to make the active speakers floater work. -				std::string namePortion = nameFromsipURI(uriString); -				if(namePortion.empty()) -				{ -					// Problem with the SIP URI, fall back to the display name -					namePortion = displayNameString; -				} -				if(namePortion.empty()) -				{ -					// Problems with both of the above, fall back to the account name -					namePortion = nameString; -				} -				 -				// Set the display name (which is a hint to the active speakers window not to do its own lookup) -				participant->mDisplayName = namePortion; -				avatarNameResolved(participant->mAvatarID, namePortion); -			} -		} -	} -} - -void LLVoiceClient::participantRemovedEvent( -		std::string &sessionHandle,  -		std::string &sessionGroupHandle,  -		std::string &uriString,  -		std::string &alias,  -		std::string &nameString) -{ -	sessionState *session = findSession(sessionHandle); -	if(session) -	{ -		participantState *participant = session->findParticipant(uriString); -		if(participant) -		{ -			session->removeParticipant(participant); -		} -		else -		{ -			LL_DEBUGS("Voice") << "unknown participant " << uriString << LL_ENDL; -		} -	} -	else -	{ -		LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL; -	} -} +//-------------------------------------------------- +// text chat -void LLVoiceClient::participantUpdatedEvent( -		std::string &sessionHandle,  -		std::string &sessionGroupHandle,  -		std::string &uriString,  -		std::string &alias,  -		bool isModeratorMuted,  -		bool isSpeaking,  -		int volume,  -		F32 energy) +BOOL LLVoiceClient::isSessionTextIMPossible(const LLUUID& id)  { -	sessionState *session = findSession(sessionHandle); -	if(session) +	if (mVoiceModule)   	{ -		participantState *participant = session->findParticipant(uriString); -		 -		if(participant) -		{ -			participant->mIsSpeaking = isSpeaking; -			participant->mIsModeratorMuted = isModeratorMuted; - -			// SLIM SDK: convert range: ensure that energy is set to zero if is_speaking is false -			if (isSpeaking) -			{ -				participant->mSpeakingTimeout.reset(); -				participant->mPower = energy; -			} -			else -			{ -				participant->mPower = 0.0f; -			} - -			// Ignore incoming volume level if it has been explicitly set, or there -			//  is a volume or mute change pending. -			if ( !participant->mVolumeSet && !participant->mVolumeDirty) -			{ -				participant->mVolume = (F32)volume * VOLUME_SCALE_VIVOX; -			} - -			// *HACK: mantipov: added while working on EXT-3544 -			/* -			Sometimes LLVoiceClient::participantUpdatedEvent callback is called BEFORE  -			LLViewerChatterBoxSessionAgentListUpdates::post() sometimes AFTER. -			 -			participantUpdatedEvent updates voice participant state in particular participantState::mIsModeratorMuted -			Originally we wanted to update session Speaker Manager to fire LLSpeakerVoiceModerationEvent to fix the EXT-3544 bug. -			Calling of the LLSpeakerMgr::update() method was added into LLIMMgr::processAgentListUpdates. -			 -			But in case participantUpdatedEvent() is called after LLViewerChatterBoxSessionAgentListUpdates::post() -			voice participant mIsModeratorMuted is changed after speakers are updated in Speaker Manager -			and event is not fired. - -			So, we have to call LLSpeakerMgr::update() here. In any case it is better than call it -			in LLCallFloater::draw() -			*/ -			LLVoiceChannel* voice_cnl = LLVoiceChannel::getCurrentVoiceChannel(); - -			// ignore session ID of local chat -			if (voice_cnl && voice_cnl->getSessionID().notNull()) -			{ -				LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(voice_cnl->getSessionID()); -				if (speaker_manager) -				{ -					speaker_manager->update(true); -				} -			} -		} -		else -		{ -			LL_WARNS("Voice") << "unknown participant: " << uriString << LL_ENDL; -		} +		return mVoiceModule->isSessionTextIMPossible(id);  	}  	else  	{ -		LL_INFOS("Voice") << "unknown session " << sessionHandle << LL_ENDL; -	} +		return FALSE; +	}	  } -void LLVoiceClient::buddyPresenceEvent( -		std::string &uriString,  -		std::string &alias,  -		std::string &statusString, -		std::string &applicationString) +BOOL LLVoiceClient::isSessionCallBackPossible(const LLUUID& id)  { -	buddyListEntry *buddy = findBuddy(uriString); -	 -	if(buddy) +	if (mVoiceModule)   	{ -		LL_DEBUGS("Voice") << "Presence event for " << buddy->mDisplayName << " status \"" << statusString << "\", application \"" << applicationString << "\""<< LL_ENDL; -		LL_DEBUGS("Voice") << "before: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL; - -		if(applicationString.empty()) -		{ -			// This presence event is from a client that doesn't set up the Application string.  Do things the old-skool way. -			// NOTE: this will be needed to support people who aren't on the 3010-class SDK yet. - -			if ( stricmp("Unknown", statusString.c_str())== 0)  -			{ -				// User went offline with a non-SLim-enabled viewer. -				buddy->mOnlineSL = false; -			} -			else if ( stricmp("Online", statusString.c_str())== 0)  -			{ -				// User came online with a non-SLim-enabled viewer. -				buddy->mOnlineSL = true; -			} -			else -			{ -				// If the user is online through SLim, their status will be "Online-slc", "Away", or something else. -				// NOTE: we should never see this unless someone is running an OLD version of SLim -- the versions that should be in use now all set the application string. -				buddy->mOnlineSLim = true; -			}  -		} -		else if(applicationString.find("SecondLifeViewer") != std::string::npos) -		{ -			// This presence event is from a viewer that sets the application string -			if ( stricmp("Unknown", statusString.c_str())== 0)  -			{ -				// Viewer says they're offline -				buddy->mOnlineSL = false; -			} -			else -			{ -				// Viewer says they're online -				buddy->mOnlineSL = true; -			} -		} -		else -		{ -			// This presence event is from something which is NOT the SL viewer (assume it's SLim). -			if ( stricmp("Unknown", statusString.c_str())== 0)  -			{ -				// SLim says they're offline -				buddy->mOnlineSLim = false; -			} -			else -			{ -				// SLim says they're online -				buddy->mOnlineSLim = true; -			} -		}  - -		LL_DEBUGS("Voice") << "after: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL; -		 -		// HACK -- increment the internal change serial number in the LLRelationship (without changing the actual status), so the UI notices the change. -		LLAvatarTracker::instance().setBuddyOnline(buddy->mUUID,LLAvatarTracker::instance().isBuddyOnline(buddy->mUUID)); - -		notifyFriendObservers(); +		return mVoiceModule->isSessionCallBackPossible(id);  	}  	else  	{ -		LL_DEBUGS("Voice") << "Presence for unknown buddy " << uriString << LL_ENDL; +		return FALSE;  	}	  } -void LLVoiceClient::messageEvent( -		std::string &sessionHandle,  -		std::string &uriString,  -		std::string &alias,  -		std::string &messageHeader,  -		std::string &messageBody, -		std::string &applicationString) -{ -	LL_DEBUGS("Voice") << "Message event, session " << sessionHandle << " from " << uriString << LL_ENDL; -//	LL_DEBUGS("Voice") << "    header " << messageHeader << ", body: \n" << messageBody << LL_ENDL; -	 -	if(messageHeader.find("text/html") != std::string::npos) -	{ -		std::string message; - -		{ -			const std::string startMarker = "<body"; -			const std::string startMarker2 = ">"; -			const std::string endMarker = "</body>"; -			const std::string startSpan = "<span"; -			const std::string endSpan = "</span>"; -			std::string::size_type start; -			std::string::size_type end; -			 -			// Default to displaying the raw string, so the message gets through. -			message = messageBody; - -			// Find the actual message text within the XML fragment -			start = messageBody.find(startMarker); -			start = messageBody.find(startMarker2, start); -			end = messageBody.find(endMarker); - -			if(start != std::string::npos) -			{ -				start += startMarker2.size(); -				 -				if(end != std::string::npos) -					end -= start; -					 -				message.assign(messageBody, start, end); -			} -			else  -			{ -				// Didn't find a <body>, try looking for a <span> instead. -				start = messageBody.find(startSpan); -				start = messageBody.find(startMarker2, start); -				end = messageBody.find(endSpan); -				 -				if(start != std::string::npos) -				{ -					start += startMarker2.size(); -					 -					if(end != std::string::npos) -						end -= start; -					 -					message.assign(messageBody, start, end); -				}			 -			} -		}	 -		 -//		LL_DEBUGS("Voice") << "    raw message = \n" << message << LL_ENDL; - -		// strip formatting tags -		{ -			std::string::size_type start; -			std::string::size_type end; -			 -			while((start = message.find('<')) != std::string::npos) -			{ -				if((end = message.find('>', start + 1)) != std::string::npos) -				{ -					// Strip out the tag -					message.erase(start, (end + 1) - start); -				} -				else -				{ -					// Avoid an infinite loop -					break; -				} -			} -		} -		 -		// Decode ampersand-escaped chars -		{ -			std::string::size_type mark = 0; - -			// The text may contain text encoded with <, >, and & -			mark = 0; -			while((mark = message.find("<", mark)) != std::string::npos) -			{ -				message.replace(mark, 4, "<"); -				mark += 1; -			} -			 -			mark = 0; -			while((mark = message.find(">", mark)) != std::string::npos) -			{ -				message.replace(mark, 4, ">"); -				mark += 1; -			} -			 -			mark = 0; -			while((mark = message.find("&", mark)) != std::string::npos) -			{ -				message.replace(mark, 5, "&"); -				mark += 1; -			} -		} -		 -		// strip leading/trailing whitespace (since we always seem to get a couple newlines) -		LLStringUtil::trim(message); -		 -//		LL_DEBUGS("Voice") << "    stripped message = \n" << message << LL_ENDL; -		 -		sessionState *session = findSession(sessionHandle); -		if(session) -		{ -			bool is_busy = gAgent.getBusy(); -			bool is_muted = LLMuteList::getInstance()->isMuted(session->mCallerID, session->mName, LLMute::flagTextChat); -			bool is_linden = LLMuteList::getInstance()->isLinden(session->mName); -			bool quiet_chat = false; -			LLChat chat; - -			chat.mMuted = is_muted && !is_linden; -			 -			if(!chat.mMuted) -			{ -				chat.mFromID = session->mCallerID; -				chat.mFromName = session->mName; -				chat.mSourceType = CHAT_SOURCE_AGENT; - -				if(is_busy && !is_linden) -				{ -					quiet_chat = true; -					// TODO: Question: Return busy mode response here?  Or maybe when session is started instead? -				} -								 -				LL_DEBUGS("Voice") << "adding message, name " << session->mName << " session " << session->mIMSessionID << ", target " << session->mCallerID << LL_ENDL; -				gIMMgr->addMessage(session->mIMSessionID, -						session->mCallerID, -						session->mName.c_str(), -						message.c_str(), -						LLStringUtil::null,		// default arg -						IM_NOTHING_SPECIAL,		// default arg -						0,						// default arg -						LLUUID::null,			// default arg -						LLVector3::zero,		// default arg -						true);					// prepend name and make it a link to the user's profile -			} -		}		 -	} -} - -void LLVoiceClient::sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType) +BOOL LLVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message)  { -	sessionState *session = findSession(sessionHandle); -	 -	if(session) +	if (mVoiceModule)   	{ -		participantState *participant = session->findParticipant(uriString); -		if(participant) -		{ -			if (!stricmp(notificationType.c_str(), "Typing")) -			{ -				// Other end started typing -				// TODO: The proper way to add a typing notification seems to be LLIMMgr::processIMTypingStart(). -				// It requires an LLIMInfo for the message, which we don't have here. -			} -			else if (!stricmp(notificationType.c_str(), "NotTyping")) -			{ -				// Other end stopped typing -				// TODO: The proper way to remove a typing notification seems to be LLIMMgr::processIMTypingStop(). -				// It requires an LLIMInfo for the message, which we don't have here. -			} -			else -			{ -				LL_DEBUGS("Voice") << "Unknown notification type " << notificationType << "for participant " << uriString << " in session " << session->mSIPURI << LL_ENDL; -			} -		} -		else -		{ -			LL_DEBUGS("Voice") << "Unknown participant " << uriString << " in session " << session->mSIPURI << LL_ENDL; -		} +		return mVoiceModule->sendTextMessage(participant_id, message);  	}  	else  	{ -		LL_DEBUGS("Voice") << "Unknown session handle " << sessionHandle << LL_ENDL; -	} -} - -void LLVoiceClient::subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType) -{ -	buddyListEntry *buddy = findBuddy(buddyURI); -	 -	if(!buddy) -	{ -		// Couldn't find buddy by URI, try converting the alias... -		if(!alias.empty()) -		{ -			LLUUID id; -			if(IDFromName(alias, id)) -			{ -				buddy = findBuddy(id); -			} -		} -	} -	 -	if(buddy) -	{ -		std::ostringstream stream; -		 -		if(buddy->mCanSeeMeOnline) -		{ -			// Sending the response will create an auto-accept rule -			buddy->mHasAutoAcceptListEntry = true; -		} -		else -		{ -			// Sending the response will create a block rule -			buddy->mHasBlockListEntry = true; -		} -		 -		if(buddy->mInSLFriends) -		{ -			buddy->mInVivoxBuddies = true; -		} -		 -		stream -			<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.SendSubscriptionReply.1\">" -				<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" -				<< "<BuddyURI>" << buddy->mURI << "</BuddyURI>" -				<< "<RuleType>" << (buddy->mCanSeeMeOnline?"Allow":"Hide") << "</RuleType>" -				<< "<AutoAccept>"<< (buddy->mInSLFriends?"1":"0")<< "</AutoAccept>" -				<< "<SubscriptionHandle>" << subscriptionHandle << "</SubscriptionHandle>" -			<< "</Request>" -			<< "\n\n\n"; -			 -		writeString(stream.str()); -	} -} - -void LLVoiceClient::auxAudioPropertiesEvent(F32 energy) -{ -	LL_DEBUGS("Voice") << "got energy " << energy << LL_ENDL; -	mTuningEnergy = energy; -} - -void LLVoiceClient::buddyListChanged() -{ -	// This is called after we receive a BuddyAndGroupListChangedEvent. -	mBuddyListMapPopulated = true; -	mFriendsListDirty = true; -} - -void LLVoiceClient::muteListChanged() -{ -	// The user's mute list has been updated.  Go through the current participant list and sync it with the mute list. -	if(mAudioSession) -	{ -		participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin(); -		 -		for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) -		{ -			participantState *p = iter->second; -			 -			// Check to see if this participant is on the mute list already -			if(p->updateMuteState()) -				mAudioSession->mMuteDirty = true; -		} -	} -} - -void LLVoiceClient::updateFriends(U32 mask) -{ -	if(mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::POWERS)) -	{ -		// Just resend the whole friend list to the daemon -		mFriendsListDirty = true; -	} -} - -///////////////////////////// -// Managing list of participants -LLVoiceClient::participantState::participantState(const std::string &uri) :  -	 mURI(uri),  -	 mPTT(false),  -	 mIsSpeaking(false),  -	 mIsModeratorMuted(false),  -	 mLastSpokeTimestamp(0.f),  -	 mPower(0.f),  -	 mVolume(VOLUME_DEFAULT), -	 mOnMuteList(false), -	 mVolumeSet(false), -	 mVolumeDirty(false), -	 mAvatarIDValid(false), -	 mIsSelf(false) -{ -} - -LLVoiceClient::participantState *LLVoiceClient::sessionState::addParticipant(const std::string &uri) -{ -	participantState *result = NULL; -	bool useAlternateURI = false; -	 -	// Note: this is mostly the body of LLVoiceClient::sessionState::findParticipant(), but since we need to know if it -	// matched the alternate SIP URI (so we can add it properly), we need to reproduce it here. -	{ -		participantMap::iterator iter = mParticipantsByURI.find(&uri); - -		if(iter == mParticipantsByURI.end()) -		{ -			if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI)) -			{ -				// This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant. -				// Use mSIPURI instead, since it will be properly encoded. -				iter = mParticipantsByURI.find(&(mSIPURI)); -				useAlternateURI = true; -			} -		} - -		if(iter != mParticipantsByURI.end()) -		{ -			result = iter->second; -		} -	} -		 -	if(!result) -	{ -		// participant isn't already in one list or the other. -		result = new participantState(useAlternateURI?mSIPURI:uri); -		mParticipantsByURI.insert(participantMap::value_type(&(result->mURI), result)); -		mParticipantsChanged = true; -		 -		// Try to do a reverse transform on the URI to get the GUID back. -		{ -			LLUUID id; -			if(IDFromName(result->mURI, id)) -			{ -				result->mAvatarIDValid = true; -				result->mAvatarID = id; - -				if(result->updateMuteState()) -					mMuteDirty = true; -			} -			else -			{ -				// Create a UUID by hashing the URI, but do NOT set mAvatarIDValid. -				// This tells both code in LLVoiceClient and code in llfloateractivespeakers.cpp that the ID will not be in the name cache. -				setUUIDFromStringHash(result->mAvatarID, uri); -			} -		} -		 -		mParticipantsByUUID.insert(participantUUIDMap::value_type(&(result->mAvatarID), result)); - -		if (LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID, result->mVolume)) -		{ -			result->mVolumeDirty = true; -			mVolumeDirty = true; -		} - -		LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL; -	} -	 -	return result; -} - -bool LLVoiceClient::participantState::updateMuteState() -{ -	bool result = false; -	 -	if(mAvatarIDValid) -	{ -		bool isMuted = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat); -		if(mOnMuteList != isMuted) -		{ -			mOnMuteList = isMuted; -			mVolumeDirty = true; -			result = true; -		} -	} -	return result; -} - -bool LLVoiceClient::participantState::isAvatar() -{ -	return mAvatarIDValid; -} - -void LLVoiceClient::sessionState::removeParticipant(LLVoiceClient::participantState *participant) -{ -	if(participant) -	{ -		participantMap::iterator iter = mParticipantsByURI.find(&(participant->mURI)); -		participantUUIDMap::iterator iter2 = mParticipantsByUUID.find(&(participant->mAvatarID)); -		 -		LL_DEBUGS("Voice") << "participant \"" << participant->mURI <<  "\" (" << participant->mAvatarID << ") removed." << LL_ENDL; -		 -		if(iter == mParticipantsByURI.end()) -		{ -			LL_ERRS("Voice") << "Internal error: participant " << participant->mURI << " not in URI map" << LL_ENDL; -		} -		else if(iter2 == mParticipantsByUUID.end()) -		{ -			LL_ERRS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL; -		} -		else if(iter->second != iter2->second) -		{ -			LL_ERRS("Voice") << "Internal error: participant mismatch!" << LL_ENDL; -		} -		else -		{ -			mParticipantsByURI.erase(iter); -			mParticipantsByUUID.erase(iter2); -			 -			delete participant; -			mParticipantsChanged = true; -		} -	} -} - -void LLVoiceClient::sessionState::removeAllParticipants() -{ -	LL_DEBUGS("Voice") << "called" << LL_ENDL; - -	while(!mParticipantsByURI.empty()) -	{ -		removeParticipant(mParticipantsByURI.begin()->second); -	} -	 -	if(!mParticipantsByUUID.empty()) -	{ -		LL_ERRS("Voice") << "Internal error: empty URI map, non-empty UUID map" << LL_ENDL; -	} -} - -LLVoiceClient::participantMap *LLVoiceClient::getParticipantList(void) -{ -	participantMap *result = NULL; -	if(mAudioSession) -	{ -		result = &(mAudioSession->mParticipantsByURI); -	} -	return result; -} - -void LLVoiceClient::getParticipantsUUIDSet(std::set<LLUUID>& participant_uuids) -{ -	if (NULL == mAudioSession) return; - -	participantUUIDMap::const_iterator it = mAudioSession->mParticipantsByUUID.begin(), -		it_end = mAudioSession->mParticipantsByUUID.end(); -	for (; it != it_end; ++it) -	{ -		participant_uuids.insert((*(*it).first)); -	} -} - -LLVoiceClient::participantState *LLVoiceClient::sessionState::findParticipant(const std::string &uri) -{ -	participantState *result = NULL; -	 -	participantMap::iterator iter = mParticipantsByURI.find(&uri); - -	if(iter == mParticipantsByURI.end()) -	{ -		if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI)) -		{ -			// This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant. -			// Look up the other URI -			iter = mParticipantsByURI.find(&(mSIPURI)); -		} -	} - -	if(iter != mParticipantsByURI.end()) -	{ -		result = iter->second; -	} -		 -	return result; -} - -LLVoiceClient::participantState* LLVoiceClient::sessionState::findParticipantByID(const LLUUID& id) -{ -	participantState * result = NULL; -	participantUUIDMap::iterator iter = mParticipantsByUUID.find(&id); - -	if(iter != mParticipantsByUUID.end()) -	{ -		result = iter->second; -	} - -	return result; -} - -LLVoiceClient::participantState* LLVoiceClient::findParticipantByID(const LLUUID& id) -{ -	participantState * result = NULL; -	 -	if(mAudioSession) -	{ -		result = mAudioSession->findParticipantByID(id); -	} -	 -	return result; +		return FALSE; +	}	  } - -void LLVoiceClient::parcelChanged() +void LLVoiceClient::endUserIMSession(const LLUUID& participant_id)  { -	if(getState() >= stateNoChannel) +	if (mVoiceModule)   	{ -		// If the user is logged in, start a channel lookup. -		LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL; - -		std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest"); -		LLSD data; -		LLHTTPClient::post( -			url, -			data, -			new LLVoiceClientCapResponder); -	} -	else -	{ -		// The transition to stateNoChannel needs to kick this off again. -		LL_INFOS("Voice") << "not logged in yet, deferring" << LL_ENDL; +		mVoiceModule->endUserIMSession(participant_id);  	}  } -void LLVoiceClient::switchChannel( -	std::string uri, -	bool spatial, -	bool no_reconnect, -	bool is_p2p, -	std::string hash) -{ -	bool needsSwitch = false; -	 -	LL_DEBUGS("Voice")  -		<< "called in state " << state2string(getState())  -		<< " with uri \"" << uri << "\""  -		<< (spatial?", spatial is true":", spatial is false") -		<< LL_ENDL; -	 -	switch(getState()) -	{ -		case stateJoinSessionFailed: -		case stateJoinSessionFailedWaiting: -		case stateNoChannel: -			// Always switch to the new URI from these states. -			needsSwitch = true; -		break; - -		default: -			if(mSessionTerminateRequested) -			{ -				// If a terminate has been requested, we need to compare against where the URI we're already headed to. -				if(mNextAudioSession) -				{ -					if(mNextAudioSession->mSIPURI != uri) -						needsSwitch = true; -				} -				else -				{ -					// mNextAudioSession is null -- this probably means we're on our way back to spatial. -					if(!uri.empty()) -					{ -						// We do want to process a switch in this case. -						needsSwitch = true; -					} -				} -			} -			else -			{ -				// Otherwise, compare against the URI we're in now. -				if(mAudioSession) -				{ -					if(mAudioSession->mSIPURI != uri) -					{ -						needsSwitch = true; -					} -				} -				else -				{ -					if(!uri.empty()) -					{ -						// mAudioSession is null -- it's not clear what case would cause this. -						// For now, log it as a warning and see if it ever crops up. -						LL_WARNS("Voice") << "No current audio session." << LL_ENDL; -					} -				} -			} -		break; -	} -	 -	if(needsSwitch) -	{ -		if(uri.empty()) -		{ -			// Leave any channel we may be in -			LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL; - -			sessionState *oldSession = mNextAudioSession; -			mNextAudioSession = NULL; - -			// The old session may now need to be deleted. -			reapSession(oldSession); - -			notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); -		} -		else -		{ -			LL_DEBUGS("Voice") << "switching to channel " << uri << LL_ENDL; - -			mNextAudioSession = addSession(uri); -			mNextAudioSession->mHash = hash; -			mNextAudioSession->mIsSpatial = spatial; -			mNextAudioSession->mReconnect = !no_reconnect; -			mNextAudioSession->mIsP2P = is_p2p; -		} -		 -		if(getState() <= stateNoChannel) -		{ -			// We're already set up to join a channel, just needed to fill in the session URI -		} -		else -		{ -			// State machine will come around and rejoin if uri/handle is not empty. -			sessionTerminate(); -		} -	} -} +//---------------------------------------------- +// channels -void LLVoiceClient::joinSession(sessionState *session) +bool LLVoiceClient::inProximalChannel()  { -	mNextAudioSession = session; -	 -	if(getState() <= stateNoChannel) +	if (mVoiceModule)   	{ -		// We're already set up to join a channel, just needed to fill in the session handle +		return mVoiceModule->inProximalChannel();  	}  	else  	{ -		// State machine will come around and rejoin if uri/handle is not empty. -		sessionTerminate(); +		return false;  	}  } @@ -5454,1822 +344,417 @@ void LLVoiceClient::setNonSpatialChannel(  	const std::string &uri,  	const std::string &credentials)  { -	switchChannel(uri, false, false, false, credentials); +	if (mVoiceModule) mVoiceModule->setNonSpatialChannel(uri, credentials);  }  void LLVoiceClient::setSpatialChannel(  	const std::string &uri,  	const std::string &credentials)  { -	mSpatialSessionURI = uri; -	mSpatialSessionCredentials = credentials; -	mAreaVoiceDisabled = mSpatialSessionURI.empty(); - -	LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL; -	 -	if((mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial))) -	{ -		// User is in a non-spatial chat or joining a non-spatial chat.  Don't switch channels. -		LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL; -	} -	else -	{ -		switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials); -	} +	if (mVoiceModule) mVoiceModule->setSpatialChannel(uri, credentials);  } -void LLVoiceClient::callUser(const LLUUID &uuid) +void LLVoiceClient::leaveNonSpatialChannel()  { -	std::string userURI = sipURIFromID(uuid); - -	switchChannel(userURI, false, true, true); +	if (mVoiceModule) mVoiceModule->leaveNonSpatialChannel();  } -LLVoiceClient::sessionState* LLVoiceClient::startUserIMSession(const LLUUID &uuid) -{ -	// Figure out if a session with the user already exists -	sessionState *session = findSession(uuid); -	if(!session) -	{ -		// No session with user, need to start one. -		std::string uri = sipURIFromID(uuid); -		session = addSession(uri); - -		llassert(session); -		if (!session) return NULL; - -		session->mIsSpatial = false; -		session->mReconnect = false;	 -		session->mIsP2P = true; -		session->mCallerID = uuid; -	} -	 -	if(session->mHandle.empty()) -	{ -		// Session isn't active -- start it up. -		sessionCreateSendMessage(session, false, true); -	} -	else -	{	 -		// Session is already active -- start up text. -		sessionTextConnectSendMessage(session); -	} -	 -	return session; -} - -bool LLVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message) +void LLVoiceClient::leaveChannel(void)  { -	bool result = false; - -	// Attempt to locate the indicated session -	sessionState *session = startUserIMSession(participant_id); -	if(session) -	{ -		// found the session, attempt to send the message -		session->mTextMsgQueue.push(message); -		 -		// Try to send queued messages (will do nothing if the session is not open yet) -		sendQueuedTextMessages(session); - -		// The message is queued, so we succeed. -		result = true; -	}	 -	else -	{ -		LL_DEBUGS("Voice") << "Session not found for participant ID " << participant_id << LL_ENDL; -	} -	 -	return result; +	if (mVoiceModule) mVoiceModule->leaveChannel();  } -void LLVoiceClient::sendQueuedTextMessages(sessionState *session) +std::string LLVoiceClient::getCurrentChannel()  { -	if(session->mTextStreamState == 1) +	if (mVoiceModule)   	{ -		if(!session->mTextMsgQueue.empty()) -		{ -			std::ostringstream stream; -			 -			while(!session->mTextMsgQueue.empty()) -			{ -				std::string message = session->mTextMsgQueue.front(); -				session->mTextMsgQueue.pop(); -				stream -				<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SendMessage.1\">" -					<< "<SessionHandle>" << session->mHandle << "</SessionHandle>" -					<< "<MessageHeader>text/HTML</MessageHeader>" -					<< "<MessageBody>" << message << "</MessageBody>" -				<< "</Request>" -				<< "\n\n\n"; -			}		 -			writeString(stream.str()); -		} +		return mVoiceModule->getCurrentChannel();  	}  	else  	{ -		// Session isn't connected yet, defer until later. +		return std::string();  	}  } -void LLVoiceClient::endUserIMSession(const LLUUID &uuid) -{ -	// Figure out if a session with the user exists -	sessionState *session = findSession(uuid); -	if(session) -	{ -		// found the session -		if(!session->mHandle.empty()) -		{ -			sessionTextDisconnectSendMessage(session); -		} -	}	 -	else -	{ -		LL_DEBUGS("Voice") << "Session not found for participant ID " << uuid << LL_ENDL; -	} -} -bool LLVoiceClient::answerInvite(std::string &sessionHandle) -{ -	// this is only ever used to answer incoming p2p call invites. -	 -	sessionState *session = findSession(sessionHandle); -	if(session) -	{ -		session->mIsSpatial = false; -		session->mReconnect = false;	 -		session->mIsP2P = true; +//--------------------------------------- +// invitations -		joinSession(session); -		return true; -	} -	 -	return false; -} - -bool LLVoiceClient::isOnlineSIP(const LLUUID &id) +void LLVoiceClient::callUser(const LLUUID &uuid)  { -	bool result = false; -	buddyListEntry *buddy = findBuddy(id); -	if(buddy) -	{ -		result = buddy->mOnlineSLim; -		LL_DEBUGS("Voice") << "Buddy " << buddy->mDisplayName << " is SIP " << (result?"online":"offline") << LL_ENDL; -	} - -	if(!result) -	{ -		// This user isn't on the buddy list or doesn't show online status through the buddy list, but could be a participant in an existing session if they initiated a text IM. -		sessionState *session = findSession(id); -		if(session && !session->mHandle.empty()) -		{ -			if((session->mTextStreamState != streamStateUnknown) || (session->mMediaStreamState > streamStateIdle)) -			{ -				LL_DEBUGS("Voice") << "Open session with " << id << " found, returning SIP online state" << LL_ENDL; -				// we have a p2p text session open with this user, so by definition they're online. -				result = true; -			} -		} -	} -	 -	return result; +	if (mVoiceModule) mVoiceModule->callUser(uuid);  } -// Returns true if the indicated participant in the current audio session is really an SL avatar. -// Currently this will be false only for PSTN callers into group chats, and PSTN p2p calls. -bool LLVoiceClient::isParticipantAvatar(const LLUUID &id) +bool LLVoiceClient::answerInvite(std::string &channelHandle)  { -	bool result = true;  -	sessionState *session = findSession(id); -	 -	if(session != NULL) +	if (mVoiceModule)   	{ -		// this is a p2p session with the indicated caller, or the session with the specified UUID. -		if(session->mSynthesizedCallerID) -			result = false; +		return mVoiceModule->answerInvite(channelHandle);  	}  	else  	{ -		// Didn't find a matching session -- check the current audio session for a matching participant -		if(mAudioSession != NULL) -		{ -			participantState *participant = findParticipantByID(id); -			if(participant != NULL) -			{ -				result = participant->isAvatar(); -			} -		} +		return false;  	} -	 -	return result;  } -// Returns true if calling back the session URI after the session has closed is possible. -// Currently this will be false only for PSTN P2P calls.		 -bool LLVoiceClient::isSessionCallBackPossible(const LLUUID &session_id) +void LLVoiceClient::declineInvite(std::string &channelHandle)  { -	bool result = true;  -	sessionState *session = findSession(session_id); -	 -	if(session != NULL) -	{ -		result = session->isCallBackPossible(); -	} -	 -	return result; +	if (mVoiceModule) mVoiceModule->declineInvite(channelHandle);  } -// Returns true if the session can accepte text IM's. -// Currently this will be false only for PSTN P2P calls. -bool LLVoiceClient::isSessionTextIMPossible(const LLUUID &session_id) -{ -	bool result = true;  -	sessionState *session = findSession(session_id); -	 -	if(session != NULL) -	{ -		result = session->isTextIMPossible(); -	} -	 -	return result; -} -		 -void LLVoiceClient::declineInvite(std::string &sessionHandle) -{ -	sessionState *session = findSession(sessionHandle); -	if(session) -	{ -		sessionMediaDisconnectSendMessage(session); -	} -} +//------------------------------------------ +// Volume/gain -void LLVoiceClient::leaveNonSpatialChannel() -{ -	LL_DEBUGS("Voice")  -		<< "called in state " << state2string(getState())  -		<< LL_ENDL; -	 -	// Make sure we don't rejoin the current session.	 -	sessionState *oldNextSession = mNextAudioSession; -	mNextAudioSession = NULL; -	 -	// Most likely this will still be the current session at this point, but check it anyway. -	reapSession(oldNextSession); -	 -	verifySessionState(); -	 -	sessionTerminate(); -} - -std::string LLVoiceClient::getCurrentChannel() -{ -	std::string result; -	 -	if((getState() == stateRunning) && !mSessionTerminateRequested) -	{ -		result = getAudioSessionURI(); -	} -	 -	return result; -} - -bool LLVoiceClient::inProximalChannel() -{ -	bool result = false; -	 -	if((getState() == stateRunning) && !mSessionTerminateRequested) -	{ -		result = inSpatialChannel(); -	} -	 -	return result; -} - -std::string LLVoiceClient::sipURIFromID(const LLUUID &id) -{ -	std::string result; -	result = "sip:"; -	result += nameFromID(id); -	result += "@"; -	result += mVoiceSIPURIHostName; -	 -	return result; -} - -std::string LLVoiceClient::sipURIFromAvatar(LLVOAvatar *avatar) -{ -	std::string result; -	if(avatar) -	{ -		result = "sip:"; -		result += nameFromID(avatar->getID()); -		result += "@"; -		result += mVoiceSIPURIHostName; -	} -	 -	return result; -} - -std::string LLVoiceClient::nameFromAvatar(LLVOAvatar *avatar) -{ -	std::string result; -	if(avatar) -	{ -		result = nameFromID(avatar->getID()); -	}	 -	return result; -} - -std::string LLVoiceClient::nameFromID(const LLUUID &uuid) -{ -	std::string result; -	 -	if (uuid.isNull()) { -		//VIVOX, the uuid emtpy look for the mURIString and return that instead. -		//result.assign(uuid.mURIStringName); -		LLStringUtil::replaceChar(result, '_', ' '); -		return result; -	} -	// Prepending this apparently prevents conflicts with reserved names inside the vivox and diamondware code. -	result = "x"; -	 -	// Base64 encode and replace the pieces of base64 that are less compatible  -	// with e-mail local-parts. -	// See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet" -	result += LLBase64::encode(uuid.mData, UUID_BYTES); -	LLStringUtil::replaceChar(result, '+', '-'); -	LLStringUtil::replaceChar(result, '/', '_'); -	 -	// If you need to transform a GUID to this form on the Mac OS X command line, this will do so: -	// echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-') -	 -	// The reverse transform can be done with: -	// echo 'x5mkTKmxDTuGnjWyC__WfMg==' |cut -b 2- -|tr '_-' '/+' |openssl base64 -d|xxd -p -	 -	return result; -} - -bool LLVoiceClient::IDFromName(const std::string inName, LLUUID &uuid) -{ -	bool result = false; -	 -	// SLIM SDK: The "name" may actually be a SIP URI such as: "sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com" -	// If it is, convert to a bare name before doing the transform. -	std::string name = nameFromsipURI(inName); -	 -	// Doesn't look like a SIP URI, assume it's an actual name. -	if(name.empty()) -		name = inName; - -	// This will only work if the name is of the proper form. -	// As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is: -	// "xFnPP04IpREWNkuw1cOXlhw==" -	 -	if((name.size() == 25) && (name[0] == 'x') && (name[23] == '=') && (name[24] == '=')) -	{ -		// The name appears to have the right form. -		// Reverse the transforms done by nameFromID -		std::string temp = name; -		LLStringUtil::replaceChar(temp, '-', '+'); -		LLStringUtil::replaceChar(temp, '_', '/'); - -		U8 rawuuid[UUID_BYTES + 1];  -		int len = apr_base64_decode_binary(rawuuid, temp.c_str() + 1); -		if(len == UUID_BYTES) -		{ -			// The decode succeeded.  Stuff the bits into the result's UUID -			memcpy(uuid.mData, rawuuid, UUID_BYTES); -			result = true; -		} -	}  -	 -	if(!result) -	{ -		// VIVOX:  not a standard account name, just copy the URI name mURIString field -		// and hope for the best.  bpj -		uuid.setNull();  // VIVOX, set the uuid field to nulls -	} -	 -	return result; -} - -std::string LLVoiceClient::displayNameFromAvatar(LLVOAvatar *avatar) -{ -	return avatar->getFullname(); -} - -std::string LLVoiceClient::sipURIFromName(std::string &name) -{ -	std::string result; -	result = "sip:"; -	result += name; -	result += "@"; -	result += mVoiceSIPURIHostName; - -//	LLStringUtil::toLower(result); - -	return result; -} - -std::string LLVoiceClient::nameFromsipURI(const std::string &uri) -{ -	std::string result; - -	std::string::size_type sipOffset, atOffset; -	sipOffset = uri.find("sip:"); -	atOffset = uri.find("@"); -	if((sipOffset != std::string::npos) && (atOffset != std::string::npos)) -	{ -		result = uri.substr(sipOffset + 4, atOffset - (sipOffset + 4)); -	} -	 -	return result; -} - -bool LLVoiceClient::inSpatialChannel(void) -{ -	bool result = false; -	 -	if(mAudioSession) -		result = mAudioSession->mIsSpatial; -		 -	return result; -} - -std::string LLVoiceClient::getAudioSessionURI() +void LLVoiceClient::setVoiceVolume(F32 volume)  { -	std::string result; -	 -	if(mAudioSession) -		result = mAudioSession->mSIPURI; -		 -	return result; +	if (mVoiceModule) mVoiceModule->setVoiceVolume(volume);  } -std::string LLVoiceClient::getAudioSessionHandle() +void LLVoiceClient::setMicGain(F32 volume)  { -	std::string result; -	 -	if(mAudioSession) -		result = mAudioSession->mHandle; -		 -	return result; +	if (mVoiceModule) mVoiceModule->setMicGain(volume);  } -///////////////////////////// -// Sending updates of current state +//------------------------------------------ +// enable/disable voice features -void LLVoiceClient::enforceTether(void) +bool LLVoiceClient::voiceEnabled()  { -	LLVector3d tethered	= mCameraRequestedPosition; - -	// constrain 'tethered' to within 50m of mAvatarPosition. +	if (mVoiceModule)   	{ -		F32 max_dist = 50.0f; -		LLVector3d camera_offset = mCameraRequestedPosition - mAvatarPosition; -		F32 camera_distance = (F32)camera_offset.magVec(); -		if(camera_distance > max_dist) -		{ -			tethered = mAvatarPosition +  -				(max_dist / camera_distance) * camera_offset; -		} +		return mVoiceModule->voiceEnabled();  	} -	 -	if(dist_vec(mCameraPosition, tethered) > 0.1) +	else  	{ -		mCameraPosition = tethered; -		mSpatialCoordsDirty = true; +		return false;  	}  } -void LLVoiceClient::updatePosition(void) +void LLVoiceClient::setVoiceEnabled(bool enabled)  { -	if(gVoiceClient) -	{ -		LLViewerRegion *region = gAgent.getRegion(); -		if(region && isAgentAvatarValid()) -		{ -			LLMatrix3 rot; -			LLVector3d pos; - -			// TODO: If camera and avatar velocity are actually used by the voice system, we could compute them here... -			// They're currently always set to zero. - -			// Send the current camera position to the voice code -			rot.setRows(LLViewerCamera::getInstance()->getAtAxis(), LLViewerCamera::getInstance()->getLeftAxis (),  LLViewerCamera::getInstance()->getUpAxis());		 -			pos = gAgent.getRegion()->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin()); -			 -			gVoiceClient->setCameraPosition( -					pos,				// position -					LLVector3::zero, 	// velocity -					rot);				// rotation matrix -					 -			// Send the current avatar position to the voice code -			rot = gAgentAvatarp->getRootJoint()->getWorldRotation().getMatrix3(); -	 -			pos = gAgentAvatarp->getPositionGlobal(); -			// TODO: Can we get the head offset from outside the LLVOAvatar? -//			pos += LLVector3d(mHeadOffset); -			pos += LLVector3d(0.f, 0.f, 1.f); -		 -			gVoiceClient->setAvatarPosition( -					pos,				// position -					LLVector3::zero, 	// velocity -					rot);				// rotation matrix -		} -	} +	if (mVoiceModule) mVoiceModule->setVoiceEnabled(enabled);  } -void LLVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot) -{ -	mCameraRequestedPosition = position; -	 -	if(mCameraVelocity != velocity) -	{ -		mCameraVelocity = velocity; -		mSpatialCoordsDirty = true; -	} -	 -	if(mCameraRot != rot) -	{ -		mCameraRot = rot; -		mSpatialCoordsDirty = true; -	} -} - -void LLVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot) +void LLVoiceClient::setLipSyncEnabled(BOOL enabled)  { -	if(dist_vec(mAvatarPosition, position) > 0.1) -	{ -		mAvatarPosition = position; -		mSpatialCoordsDirty = true; -	} -	 -	if(mAvatarVelocity != velocity) -	{ -		mAvatarVelocity = velocity; -		mSpatialCoordsDirty = true; -	} -	 -	if(mAvatarRot != rot) -	{ -		mAvatarRot = rot; -		mSpatialCoordsDirty = true; -	} +	if (mVoiceModule) mVoiceModule->setLipSyncEnabled(enabled);  } -bool LLVoiceClient::channelFromRegion(LLViewerRegion *region, std::string &name) +BOOL LLVoiceClient::lipSyncEnabled()  { -	bool result = false; -	 -	if(region) +	if (mVoiceModule)   	{ -		name = region->getName(); +		return mVoiceModule->lipSyncEnabled();  	} -	 -	if(!name.empty()) -		result = true; -	 -	return result; -} - -void LLVoiceClient::leaveChannel(void) -{ -	if(getState() == stateRunning) +	else  	{ -		LL_DEBUGS("Voice") << "leaving channel for teleport/logout" << LL_ENDL; -		mChannelName.clear(); -		sessionTerminate(); +		return false;  	}  }  void LLVoiceClient::setMuteMic(bool muted)  { -	mMuteMic = muted; +	if (mVoiceModule) mVoiceModule->setMuteMic(muted);  } -bool LLVoiceClient::getMuteMic() const -{ -	return mMuteMic; -} + +// ---------------------------------------------- +// PTT  void LLVoiceClient::setUserPTTState(bool ptt)  { -	mUserPTTState = ptt; +	if (mVoiceModule) mVoiceModule->setUserPTTState(ptt);  }  bool LLVoiceClient::getUserPTTState()  { -	return mUserPTTState; -} - -void LLVoiceClient::toggleUserPTTState(void) -{ -	mUserPTTState = !mUserPTTState; -} - -void LLVoiceClient::setVoiceEnabled(bool enabled) -{ -	if (enabled != mVoiceEnabled) +	if (mVoiceModule)   	{ -		mVoiceEnabled = enabled; -		LLVoiceClientStatusObserver::EStatusType status; - -		if (enabled) -		{ -			LLVoiceChannel::getCurrentVoiceChannel()->activate(); -			status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED; -		} -		else -		{ -			// Turning voice off looses your current channel -- this makes sure the UI isn't out of sync when you re-enable it. -			LLVoiceChannel::getCurrentVoiceChannel()->deactivate(); -			status = LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED; -		} - -		notifyStatusObservers(status); -	} -} - -bool LLVoiceClient::voiceEnabled() -{ -	return gSavedSettings.getBOOL("EnableVoiceChat") && !gSavedSettings.getBOOL("CmdLineDisableVoice"); -} - -//AD *TODO: investigate possible merge of voiceWorking() and voiceEnabled() into one non-static method -bool LLVoiceClient::voiceWorking() -{ -	//Added stateSessionTerminated state to avoid problems with call in parcels with disabled voice (EXT-4758) -	// Condition with joining spatial num was added to take into account possible problems with connection to voice -	// server(EXT-4313). See bug descriptions and comments for MAX_NORMAL_JOINING_SPATIAL_NUM for more info. -	return (mSpatialJoiningNum < MAX_NORMAL_JOINING_SPATIAL_NUM) && (stateLoggedIn <= mState) && (mState <= stateSessionTerminated); -} - -void LLVoiceClient::setLipSyncEnabled(BOOL enabled) -{ -	mLipSyncEnabled = enabled; -} - -BOOL LLVoiceClient::lipSyncEnabled() -{ -	    -	if ( mVoiceEnabled && stateDisabled != getState() ) -	{ -		return mLipSyncEnabled; +		return mVoiceModule->getUserPTTState();  	}  	else  	{ -		return FALSE; +		return false;  	}  }  void LLVoiceClient::setUsePTT(bool usePTT)  { -	if(usePTT && !mUsePTT) -	{ -		// When the user turns on PTT, reset the current state. -		mUserPTTState = false; -	} -	mUsePTT = usePTT; +	if (mVoiceModule) mVoiceModule->setUsePTT(usePTT);  }  void LLVoiceClient::setPTTIsToggle(bool PTTIsToggle)  { -	if(!PTTIsToggle && mPTTIsToggle) -	{ -		// When the user turns off toggle, reset the current state. -		mUserPTTState = false; -	} -	 -	mPTTIsToggle = PTTIsToggle; +	if (mVoiceModule) mVoiceModule->setPTTIsToggle(PTTIsToggle);  }  bool LLVoiceClient::getPTTIsToggle()  { -	return mPTTIsToggle; -} - -void LLVoiceClient::setPTTKey(std::string &key) -{ -	if(key == "MiddleMouse") +	if (mVoiceModule)   	{ -		mPTTIsMiddleMouse = true; +		return mVoiceModule->getPTTIsToggle();  	} -	else -	{ -		mPTTIsMiddleMouse = false; -		if(!LLKeyboard::keyFromString(key, &mPTTKey)) -		{ -			// If the call failed, don't match any key. -			key = KEY_NONE; -		} +	else { +		return false;  	} -} -void LLVoiceClient::setEarLocation(S32 loc) -{ -	if(mEarLocation != loc) -	{ -		LL_DEBUGS("Voice") << "Setting mEarLocation to " << loc << LL_ENDL; -		 -		mEarLocation = loc; -		mSpatialCoordsDirty = true; -	}  } -void LLVoiceClient::setVoiceVolume(F32 volume) +void LLVoiceClient::inputUserControlState(bool down)  { -	int scaled_volume = scale_speaker_volume(volume);	 - -	if(scaled_volume != mSpeakerVolume) -	{ -		int min_volume = scale_speaker_volume(0); -		if((scaled_volume == min_volume) || (mSpeakerVolume == min_volume)) -		{ -			mSpeakerMuteDirty = true; -		} - -		mSpeakerVolume = scaled_volume; -		mSpeakerVolumeDirty = true; -	} +	if (mVoiceModule) mVoiceModule->inputUserControlState(down);	  } -void LLVoiceClient::setMicGain(F32 volume) +void LLVoiceClient::toggleUserPTTState(void)  { -	int scaled_volume = scale_mic_volume(volume); -	 -	if(scaled_volume != mMicVolume) -	{ -		mMicVolume = scaled_volume; -		mMicVolumeDirty = true; -	} +	if (mVoiceModule) mVoiceModule->toggleUserPTTState();  }  void LLVoiceClient::keyDown(KEY key, MASK mask)  {	 -	if (gKeyboard->getKeyRepeated(key)) -	{ -		// ignore auto-repeat keys -		return; -	} - -	if(!mPTTIsMiddleMouse) -	{ -		bool down = (mPTTKey != KEY_NONE) -			&& gKeyboard->getKeyDown(mPTTKey); -		inputUserControlState(down); -	} +	if (mVoiceModule) mVoiceModule->keyDown(key, mask);  }  void LLVoiceClient::keyUp(KEY key, MASK mask)  { -	if(!mPTTIsMiddleMouse) -	{ -		bool down = (mPTTKey != KEY_NONE) -			&& gKeyboard->getKeyDown(mPTTKey); -		inputUserControlState(down); -	} -} -void LLVoiceClient::inputUserControlState(bool down) -{ -	if(mPTTIsToggle) -	{ -		if(down) // toggle open-mic state on 'down' -		{ -			toggleUserPTTState(); -		} -	} -	else // set open-mic state as an absolute -	{ -		setUserPTTState(down); -	} +	if (mVoiceModule) mVoiceModule->keyUp(key, mask);  }  void LLVoiceClient::middleMouseState(bool down)  { -	if(mPTTIsMiddleMouse) -	{ -		inputUserControlState(down); -	} +	if (mVoiceModule) mVoiceModule->middleMouseState(down);  } -///////////////////////////// -// Accessors for data related to nearby speakers -BOOL LLVoiceClient::getVoiceEnabled(const LLUUID& id) -{ -	BOOL result = FALSE; -	participantState *participant = findParticipantByID(id); -	if(participant) -	{ -		// I'm not sure what the semantics of this should be. -		// For now, if we have any data about the user that came through the chat channel, assume they're voice-enabled. -		result = TRUE; -	} -	 -	return result; -} -BOOL LLVoiceClient::getIsSpeaking(const LLUUID& id) -{ -	BOOL result = FALSE; - -	participantState *participant = findParticipantByID(id); -	if(participant) -	{ -		if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT) -		{ -			participant->mIsSpeaking = FALSE; -		} -		result = participant->mIsSpeaking; -	} -	 -	return result; -} +//------------------------------------------- +// nearby speaker accessors -BOOL LLVoiceClient::getIsModeratorMuted(const LLUUID& id) +BOOL LLVoiceClient::getVoiceEnabled(const LLUUID& id)  { -	BOOL result = FALSE; - -	participantState *participant = findParticipantByID(id); -	if(participant) +	if (mVoiceModule)   	{ -		result = participant->mIsModeratorMuted; -	} -	 -	return result; -} - -F32 LLVoiceClient::getCurrentPower(const LLUUID& id) -{		 -	F32 result = 0; -	participantState *participant = findParticipantByID(id); -	if(participant) +		return mVoiceModule->getVoiceEnabled(id); +	}  +	else  	{ -		result = participant->mPower; +		return FALSE;  	} -	 -	return result;  } -  std::string LLVoiceClient::getDisplayName(const LLUUID& id)  { -	std::string result; -	participantState *participant = findParticipantByID(id); -	if(participant) -	{ -		result = participant->mDisplayName; -	} -	 -	return result; -} - - -BOOL LLVoiceClient::getUsingPTT(const LLUUID& id) -{ -	BOOL result = FALSE; - -	participantState *participant = findParticipantByID(id); -	if(participant) -	{ -		// I'm not sure what the semantics of this should be. -		// Does "using PTT" mean they're configured with a push-to-talk button? -		// For now, we know there's no PTT mechanism in place, so nobody is using it. -	} -	 -	return result; -} - -BOOL LLVoiceClient::getOnMuteList(const LLUUID& id) -{ -	BOOL result = FALSE; -	 -	participantState *participant = findParticipantByID(id); -	if(participant) -	{ -		result = participant->mOnMuteList; -	} - -	return result; -} - -// External accessors. -F32 LLVoiceClient::getUserVolume(const LLUUID& id) -{ -	// Minimum volume will be returned for users with voice disabled -	F32 result = VOLUME_MIN; -	 -	participantState *participant = findParticipantByID(id); -	if(participant) +	if (mVoiceModule)   	{ -		result = participant->mVolume; - -		// Enable this when debugging voice slider issues.  It's way to spammy even for debug-level logging. -		// LL_DEBUGS("Voice") << "mVolume = " << result <<  " for " << id << LL_ENDL; +		return mVoiceModule->getDisplayName(id);  	} - -	return result; -} - -void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume) -{ -	if(mAudioSession) -	{ -		participantState *participant = findParticipantByID(id); -		if (participant && !participant->mIsSelf) -		{ -			if (!is_approx_equal(volume, VOLUME_DEFAULT)) -			{ -				// Store this volume setting for future sessions if it has been -				// changed from the default -				LLSpeakerVolumeStorage::getInstance()->storeSpeakerVolume(id, volume); -			} -			else -			{ -				// Remove stored volume setting if it is returned to the default -				LLSpeakerVolumeStorage::getInstance()->removeSpeakerVolume(id); -			} - -			participant->mVolume = llclamp(volume, VOLUME_MIN, VOLUME_MAX); -			participant->mVolumeDirty = true; -			mAudioSession->mVolumeDirty = true; -		} -	} -} - -std::string LLVoiceClient::getGroupID(const LLUUID& id) -{ -	std::string result; - -	participantState *participant = findParticipantByID(id); -	if(participant) -	{ -		result = participant->mGroupID; -	} -	 -	return result; -} - -BOOL LLVoiceClient::getAreaVoiceDisabled() -{ -	return mAreaVoiceDisabled; -} - -void LLVoiceClient::recordingLoopStart(int seconds, int deltaFramesPerControlFrame) -{ -//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Start)" << LL_ENDL; -	 -	if(!mMainSessionGroupHandle.empty()) -	{ -		std::ostringstream stream; -		stream -		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">" -		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>" -		<< "<RecordingControlType>Start</RecordingControlType>"  -		<< "<DeltaFramesPerControlFrame>" << deltaFramesPerControlFrame << "</DeltaFramesPerControlFrame>" -		<< "<Filename>" << "" << "</Filename>" -		<< "<EnableAudioRecordingEvents>false</EnableAudioRecordingEvents>" -		<< "<LoopModeDurationSeconds>" << seconds << "</LoopModeDurationSeconds>" -		<< "</Request>\n\n\n"; - - -		writeString(stream.str()); -	} -} - -void LLVoiceClient::recordingLoopSave(const std::string& filename) -{ -//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Flush)" << LL_ENDL; - -	if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) -	{ -		std::ostringstream stream; -		stream -		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">" -		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>" -		<< "<RecordingControlType>Flush</RecordingControlType>"  -		<< "<Filename>" << filename << "</Filename>" -		<< "</Request>\n\n\n"; - -		writeString(stream.str()); -	} -} - -void LLVoiceClient::recordingStop() -{ -//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Stop)" << LL_ENDL; - -	if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) -	{ -		std::ostringstream stream; -		stream -		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">" -		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>" -		<< "<RecordingControlType>Stop</RecordingControlType>"  -		<< "</Request>\n\n\n"; - -		writeString(stream.str()); -	} -} - -void LLVoiceClient::filePlaybackStart(const std::string& filename) -{ -//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Start)" << LL_ENDL; - -	if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) -	{ -		std::ostringstream stream; -		stream -		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlPlayback.1\">" -		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>" -		<< "<RecordingControlType>Start</RecordingControlType>"  -		<< "<Filename>" << filename << "</Filename>" -		<< "</Request>\n\n\n"; - -		writeString(stream.str()); -	} -} - -void LLVoiceClient::filePlaybackStop() -{ -//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Stop)" << LL_ENDL; - -	if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) -	{ -		std::ostringstream stream; -		stream -		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlPlayback.1\">" -		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>" -		<< "<RecordingControlType>Stop</RecordingControlType>"  -		<< "</Request>\n\n\n"; - -		writeString(stream.str()); -	} -} - -void LLVoiceClient::filePlaybackSetPaused(bool paused) -{ -	// TODO: Implement once Vivox gives me a sample -} - -void LLVoiceClient::filePlaybackSetMode(bool vox, float speed) -{ -	// TODO: Implement once Vivox gives me a sample -} - -LLVoiceClient::sessionState::sessionState() : -	mErrorStatusCode(0), -	mMediaStreamState(streamStateUnknown), -	mTextStreamState(streamStateUnknown), -	mCreateInProgress(false), -	mMediaConnectInProgress(false), -	mVoiceInvitePending(false), -	mTextInvitePending(false), -	mSynthesizedCallerID(false), -	mIsChannel(false), -	mIsSpatial(false), -	mIsP2P(false), -	mIncoming(false), -	mVoiceEnabled(false), -	mReconnect(false), -	mVolumeDirty(false), -	mMuteDirty(false), -	mParticipantsChanged(false) -{ -} - -LLVoiceClient::sessionState::~sessionState() -{ -	removeAllParticipants(); -} - -bool LLVoiceClient::sessionState::isCallBackPossible() -{ -	// This may change to be explicitly specified by vivox in the future... -	// Currently, only PSTN P2P calls cannot be returned. -	// Conveniently, this is also the only case where we synthesize a caller UUID. -	return !mSynthesizedCallerID; -} - -bool LLVoiceClient::sessionState::isTextIMPossible() -{ -	// This may change to be explicitly specified by vivox in the future... -	return !mSynthesizedCallerID; -} - - -LLVoiceClient::sessionIterator LLVoiceClient::sessionsBegin(void) -{ -	return mSessions.begin(); -} - -LLVoiceClient::sessionIterator LLVoiceClient::sessionsEnd(void) -{ -	return mSessions.end(); -} - - -LLVoiceClient::sessionState *LLVoiceClient::findSession(const std::string &handle) -{ -	sessionState *result = NULL; -	sessionMap::iterator iter = mSessionsByHandle.find(&handle); -	if(iter != mSessionsByHandle.end()) -	{ -		result = iter->second; -	} -	 -	return result; -} - -LLVoiceClient::sessionState *LLVoiceClient::findSessionBeingCreatedByURI(const std::string &uri) -{	 -	sessionState *result = NULL; -	for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) +	else  	{ -		sessionState *session = *iter; -		if(session->mCreateInProgress && (session->mSIPURI == uri)) -		{ -			result = session; -			break; -		} +	  return std::string();  	} -	 -	return result;  } -LLVoiceClient::sessionState *LLVoiceClient::findSession(const LLUUID &participant_id) +bool LLVoiceClient::isVoiceWorking()  { -	sessionState *result = NULL; -	 -	for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) +	if (mVoiceModule)   	{ -		sessionState *session = *iter; -		if((session->mCallerID == participant_id) || (session->mIMSessionID == participant_id)) -		{ -			result = session; -			break; -		} +		return mVoiceModule->isVoiceWorking();  	} -	 -	return result; +	return false;  } -LLVoiceClient::sessionState *LLVoiceClient::addSession(const std::string &uri, const std::string &handle) +BOOL LLVoiceClient::isParticipantAvatar(const LLUUID& id)  { -	sessionState *result = NULL; -	 -	if(handle.empty()) -	{ -		// No handle supplied. -		// Check whether there's already a session with this URI -		for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) -		{ -			sessionState *s = *iter; -			if((s->mSIPURI == uri) || (s->mAlternateSIPURI == uri)) -			{ -				// TODO: I need to think about this logic... it's possible that this case should raise an internal error. -				result = s; -				break; -			} -		} -	} -	else // (!handle.empty()) -	{ -		// Check for an existing session with this handle -		sessionMap::iterator iter = mSessionsByHandle.find(&handle); -		 -		if(iter != mSessionsByHandle.end()) -		{ -			result = iter->second; -		} -	} - -	if(!result) +	if (mVoiceModule)   	{ -		// No existing session found. -		 -		LL_DEBUGS("Voice") << "adding new session: handle " << handle << " URI " << uri << LL_ENDL; -		result = new sessionState(); -		result->mSIPURI = uri; -		result->mHandle = handle; -		 -		mSessions.insert(result); - -		if(!result->mHandle.empty()) -		{ -			mSessionsByHandle.insert(sessionMap::value_type(&(result->mHandle), result)); -		} +		return mVoiceModule->isParticipantAvatar(id);  	}  	else  	{ -		// Found an existing session -		 -		if(uri != result->mSIPURI) -		{ -			// TODO: Should this be an internal error? -			LL_DEBUGS("Voice") << "changing uri from " << result->mSIPURI << " to " << uri << LL_ENDL; -			setSessionURI(result, uri); -		} - -		if(handle != result->mHandle) -		{ -			if(handle.empty()) -			{ -				// There's at least one race condition where where addSession was clearing an existing session handle, which caused things to break. -				LL_DEBUGS("Voice") << "NOT clearing handle " << result->mHandle << LL_ENDL; -			} -			else -			{ -				// TODO: Should this be an internal error? -				LL_DEBUGS("Voice") << "changing handle from " << result->mHandle << " to " << handle << LL_ENDL; -				setSessionHandle(result, handle); -			} -		} -		 -		LL_DEBUGS("Voice") << "returning existing session: handle " << handle << " URI " << uri << LL_ENDL; -	} - -	verifySessionState(); -		 -	return result; -} - -void LLVoiceClient::setSessionHandle(sessionState *session, const std::string &handle) -{ -	// Have to remove the session from the handle-indexed map before changing the handle, or things will break badly. -	 -	if(!session->mHandle.empty()) -	{ -		// Remove session from the map if it should have been there. -		sessionMap::iterator iter = mSessionsByHandle.find(&(session->mHandle)); -		if(iter != mSessionsByHandle.end()) -		{ -			if(iter->second != session) -			{ -				LL_ERRS("Voice") << "Internal error: session mismatch!" << LL_ENDL; -			} - -			mSessionsByHandle.erase(iter); -		} -		else -		{ -			LL_ERRS("Voice") << "Internal error: session handle not found in map!" << LL_ENDL; -		} -	} -			 -	session->mHandle = handle; - -	if(!handle.empty()) -	{ -		mSessionsByHandle.insert(sessionMap::value_type(&(session->mHandle), session)); +		return FALSE;  	} - -	verifySessionState();  } -void LLVoiceClient::setSessionURI(sessionState *session, const std::string &uri) +BOOL LLVoiceClient::isOnlineSIP(const LLUUID& id)  { -	// There used to be a map of session URIs to sessions, which made this complex.... -	session->mSIPURI = uri; - -	verifySessionState(); -} - -void LLVoiceClient::deleteSession(sessionState *session) -{ -	// Remove the session from the handle map -	if(!session->mHandle.empty()) -	{ -		sessionMap::iterator iter = mSessionsByHandle.find(&(session->mHandle)); -		if(iter != mSessionsByHandle.end()) -		{ -			if(iter->second != session) -			{ -				LL_ERRS("Voice") << "Internal error: session mismatch" << LL_ENDL; -			} -			mSessionsByHandle.erase(iter); -		} -	} - -	// Remove the session from the URI map -	mSessions.erase(session); -	 -	// At this point, the session should be unhooked from all lists and all state should be consistent. -	verifySessionState(); - -	// If this is the current audio session, clean up the pointer which will soon be dangling. -	if(mAudioSession == session) +	if (mVoiceModule)   	{ -		mAudioSession = NULL; -		mAudioSessionChanged = true; +		return mVoiceModule->isOnlineSIP(id);  	} - -	// ditto for the next audio session -	if(mNextAudioSession == session) +	else  	{ -		mNextAudioSession = NULL; +		return FALSE;  	} - -	// delete the session -	delete session;  } -void LLVoiceClient::deleteAllSessions() +BOOL LLVoiceClient::getIsSpeaking(const LLUUID& id)  { -	LL_DEBUGS("Voice") << "called" << LL_ENDL; - -	while(!mSessions.empty()) +	if (mVoiceModule)   	{ -		deleteSession(*(sessionsBegin())); +		return mVoiceModule->getIsSpeaking(id);  	} -	 -	if(!mSessionsByHandle.empty()) +	else  	{ -		LL_ERRS("Voice") << "Internal error: empty session map, non-empty handle map" << LL_ENDL; +		return FALSE;  	}  } -void LLVoiceClient::verifySessionState(void) +BOOL LLVoiceClient::getIsModeratorMuted(const LLUUID& id)  { -	// This is mostly intended for debugging problems with session state management. -	LL_DEBUGS("Voice") << "Total session count: " << mSessions.size() << " , session handle map size: " << mSessionsByHandle.size() << LL_ENDL; - -	for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) -	{ -		sessionState *session = *iter; - -		LL_DEBUGS("Voice") << "session " << session << ": handle " << session->mHandle << ", URI " << session->mSIPURI << LL_ENDL; -		 -		if(!session->mHandle.empty()) -		{ -			// every session with a non-empty handle needs to be in the handle map -			sessionMap::iterator i2 = mSessionsByHandle.find(&(session->mHandle)); -			if(i2 == mSessionsByHandle.end()) -			{ -				LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " not found in session map)" << LL_ENDL; -			} -			else -			{ -				if(i2->second != session) -				{ -					LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " in session map points to another session)" << LL_ENDL; -				} -			} -		} -	} -		 -	// check that every entry in the handle map points to a valid session in the session set -	for(sessionMap::iterator iter = mSessionsByHandle.begin(); iter != mSessionsByHandle.end(); iter++) +	if (mVoiceModule)   	{ -		sessionState *session = iter->second; -		sessionIterator i2 = mSessions.find(session); -		if(i2 == mSessions.end()) -		{ -			LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " not found in session map)" << LL_ENDL; -		} -		else -		{ -			if(session->mHandle != (*i2)->mHandle) -			{ -				LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " points to session with different handle " << (*i2)->mHandle << ")" << LL_ENDL; -			} -		} +		return mVoiceModule->getIsModeratorMuted(id);  	} -} - -LLVoiceClient::buddyListEntry::buddyListEntry(const std::string &uri) : -	mURI(uri) -{ -	mOnlineSL = false; -	mOnlineSLim = false; -	mCanSeeMeOnline = true; -	mHasBlockListEntry = false; -	mHasAutoAcceptListEntry = false; -	mNameResolved = false; -	mInVivoxBuddies = false; -	mInSLFriends = false; -	mNeedsNameUpdate = false; -} - -void LLVoiceClient::processBuddyListEntry(const std::string &uri, const std::string &displayName) -{ -	buddyListEntry *buddy = addBuddy(uri, displayName); -	buddy->mInVivoxBuddies = true;	 -} - -LLVoiceClient::buddyListEntry *LLVoiceClient::addBuddy(const std::string &uri) -{ -	std::string empty; -	buddyListEntry *buddy = addBuddy(uri, empty); -	if(buddy->mDisplayName.empty()) +	else  	{ -		buddy->mNameResolved = false; +		return FALSE;  	} -	return buddy;  } -LLVoiceClient::buddyListEntry *LLVoiceClient::addBuddy(const std::string &uri, const std::string &displayName) -{ -	buddyListEntry *result = NULL; -	buddyListMap::iterator iter = mBuddyListMap.find(&uri); -	 -	if(iter != mBuddyListMap.end()) -	{ -		// Found a matching buddy already in the map. -		LL_DEBUGS("Voice") << "adding existing buddy " << uri << LL_ENDL; -		result = iter->second; -	} - -	if(!result) +F32 LLVoiceClient::getCurrentPower(const LLUUID& id) +{		 +	if (mVoiceModule)   	{ -		// participant isn't already in one list or the other. -		LL_DEBUGS("Voice") << "adding new buddy " << uri << LL_ENDL; -		result = new buddyListEntry(uri); -		result->mDisplayName = displayName; - -		if(IDFromName(uri, result->mUUID))  -		{ -			// Extracted UUID from name successfully. -		} -		else -		{ -			LL_DEBUGS("Voice") << "Couldn't find ID for buddy " << uri << " (\"" << displayName << "\")" << LL_ENDL; -		} - -		mBuddyListMap.insert(buddyListMap::value_type(&(result->mURI), result)); +		return mVoiceModule->getCurrentPower(id);  	} -	 -	return result; -} - -LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddy(const std::string &uri) -{ -	buddyListEntry *result = NULL; -	buddyListMap::iterator iter = mBuddyListMap.find(&uri); -	if(iter != mBuddyListMap.end()) +	else  	{ -		result = iter->second; +		return 0.0;  	} -	 -	return result;  } -LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddy(const LLUUID &id) +BOOL LLVoiceClient::getOnMuteList(const LLUUID& id)  { -	buddyListEntry *result = NULL; -	buddyListMap::iterator iter; - -	for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++) +	if (mVoiceModule)   	{ -		if(iter->second->mUUID == id) -		{ -			result = iter->second; -			break; -		} +		return mVoiceModule->getOnMuteList(id);  	} -	 -	return result; -} - -LLVoiceClient::buddyListEntry *LLVoiceClient::findBuddyByDisplayName(const std::string &name) -{ -	buddyListEntry *result = NULL; -	buddyListMap::iterator iter; - -	for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++) +	else  	{ -		if(iter->second->mDisplayName == name) -		{ -			result = iter->second; -			break; -		} +		return FALSE;  	} -	 -	return result;  } -void LLVoiceClient::deleteBuddy(const std::string &uri) +F32 LLVoiceClient::getUserVolume(const LLUUID& id)  { -	buddyListMap::iterator iter = mBuddyListMap.find(&uri); -	if(iter != mBuddyListMap.end()) +	if (mVoiceModule)   	{ -		LL_DEBUGS("Voice") << "deleting buddy " << uri << LL_ENDL; -		buddyListEntry *buddy = iter->second; -		mBuddyListMap.erase(iter); -		delete buddy; +		return mVoiceModule->getUserVolume(id);  	}  	else  	{ -		LL_DEBUGS("Voice") << "attempt to delete nonexistent buddy " << uri << LL_ENDL; +		return 0.0;  	} -	  } -void LLVoiceClient::deleteAllBuddies(void) +void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume)  { -	while(!mBuddyListMap.empty()) -	{ -		deleteBuddy(*(mBuddyListMap.begin()->first)); -	} -	 -	// Don't want to correlate with friends list when we've emptied the buddy list. -	mBuddyListMapPopulated = false; -	 -	// Don't want to correlate with friends list when we've reset the block rules. -	mBlockRulesListReceived = false; -	mAutoAcceptRulesListReceived = false; +	if (mVoiceModule) mVoiceModule->setUserVolume(id, volume);  } -void LLVoiceClient::deleteAllBlockRules(void) -{ -	// Clear the block list entry flags from all local buddy list entries -	buddyListMap::iterator buddy_it; -	for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++) -	{ -		buddy_it->second->mHasBlockListEntry = false; -	} -} +//-------------------------------------------------- +// status observers -void LLVoiceClient::deleteAllAutoAcceptRules(void) +void LLVoiceClient::addObserver(LLVoiceClientStatusObserver* observer)  { -	// Clear the auto-accept list entry flags from all local buddy list entries -	buddyListMap::iterator buddy_it; -	for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++) -	{ -		buddy_it->second->mHasAutoAcceptListEntry = false; -	} +	if (mVoiceModule) mVoiceModule->addObserver(observer);  } -void LLVoiceClient::addBlockRule(const std::string &blockMask, const std::string &presenceOnly) +void LLVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer)  { -	buddyListEntry *buddy = NULL; - -	// blockMask is the SIP URI of a friends list entry -	buddyListMap::iterator iter = mBuddyListMap.find(&blockMask); -	if(iter != mBuddyListMap.end()) -	{ -		LL_DEBUGS("Voice") << "block list entry for " << blockMask << LL_ENDL; -		buddy = iter->second; -	} - -	if(buddy == NULL) -	{ -		LL_DEBUGS("Voice") << "block list entry for unknown buddy " << blockMask << LL_ENDL; -		buddy = addBuddy(blockMask); -	} -	 -	if(buddy != NULL) -	{ -		buddy->mHasBlockListEntry = true; -	} +	if (mVoiceModule) mVoiceModule->removeObserver(observer);  } -void LLVoiceClient::addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy) -{ -	buddyListEntry *buddy = NULL; - -	// blockMask is the SIP URI of a friends list entry -	buddyListMap::iterator iter = mBuddyListMap.find(&autoAcceptMask); -	if(iter != mBuddyListMap.end()) -	{ -		LL_DEBUGS("Voice") << "auto-accept list entry for " << autoAcceptMask << LL_ENDL; -		buddy = iter->second; -	} - -	if(buddy == NULL) -	{ -		LL_DEBUGS("Voice") << "auto-accept list entry for unknown buddy " << autoAcceptMask << LL_ENDL; -		buddy = addBuddy(autoAcceptMask); -	} - -	if(buddy != NULL) -	{ -		buddy->mHasAutoAcceptListEntry = true; -	} -} - -void LLVoiceClient::accountListBlockRulesResponse(int statusCode, const std::string &statusString) +void LLVoiceClient::addObserver(LLFriendObserver* observer)  { -	// Block list entries were updated via addBlockRule() during parsing.  Just flag that we're done. -	mBlockRulesListReceived = true; +	if (mVoiceModule) mVoiceModule->addObserver(observer);  } -void LLVoiceClient::accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString) +void LLVoiceClient::removeObserver(LLFriendObserver* observer)  { -	// Block list entries were updated via addBlockRule() during parsing.  Just flag that we're done. -	mAutoAcceptRulesListReceived = true; +	if (mVoiceModule) mVoiceModule->removeObserver(observer);  }  void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer)  { -	mParticipantObservers.insert(observer); +	if (mVoiceModule) mVoiceModule->addObserver(observer);  }  void LLVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer)  { -	mParticipantObservers.erase(observer); +	if (mVoiceModule) mVoiceModule->removeObserver(observer);  } -void LLVoiceClient::notifyParticipantObservers() +std::string LLVoiceClient::sipURIFromID(const LLUUID &id)  { -	for (observer_set_t::iterator it = mParticipantObservers.begin(); -		it != mParticipantObservers.end(); -		) +	if (mVoiceModule)   	{ -		LLVoiceClientParticipantObserver* observer = *it; -		observer->onChange(); -		// In case onChange() deleted an entry. -		it = mParticipantObservers.upper_bound(observer); +		return mVoiceModule->sipURIFromID(id);  	} -} - -void LLVoiceClient::addObserver(LLVoiceClientStatusObserver* observer) -{ -	mStatusObservers.insert(observer); -} - -void LLVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer) -{ -	mStatusObservers.erase(observer); -} - -void LLVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status) -{ -	if(mAudioSession) -	{ -		if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN) -		{ -			switch(mAudioSession->mErrorStatusCode) -			{ -				case 20713:		status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; 		break; -				case 20714:		status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; 	break; -				case 20715: -					//invalid channel, we may be using a set of poorly cached -					//info -					status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; -					break; -				case 1009: -					//invalid username and password -					status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; -					break; -			} - -			// Reset the error code to make sure it won't be reused later by accident. -			mAudioSession->mErrorStatusCode = 0; -		} -		else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL) -		{ -			switch(mAudioSession->mErrorStatusCode) -			{ -				case 404:	// NOT_FOUND -				case 480:	// TEMPORARILY_UNAVAILABLE -				case 408:	// REQUEST_TIMEOUT -					// call failed because other user was not available -					// treat this as an error case -					status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; - -					// Reset the error code to make sure it won't be reused later by accident. -					mAudioSession->mErrorStatusCode = 0; -				break; -			} -		} -	} -		 -	LL_DEBUGS("Voice")  -		<< " " << LLVoiceClientStatusObserver::status2string(status)   -		<< ", session URI " << getAudioSessionURI()  -		<< (inSpatialChannel()?", proximal is true":", proximal is false") -	<< LL_ENDL; - -	for (status_observer_set_t::iterator it = mStatusObservers.begin(); -		it != mStatusObservers.end(); -		) -	{ -		LLVoiceClientStatusObserver* observer = *it; -		observer->onChange(status, getAudioSessionURI(), inSpatialChannel()); -		// In case onError() deleted an entry. -		it = mStatusObservers.upper_bound(observer); -	} - -} - -void LLVoiceClient::addObserver(LLFriendObserver* observer) -{ -	mFriendObservers.insert(observer); -} - -void LLVoiceClient::removeObserver(LLFriendObserver* observer) -{ -	mFriendObservers.erase(observer); -} - -void LLVoiceClient::notifyFriendObservers() -{ -	for (friend_observer_set_t::iterator it = mFriendObservers.begin(); -		it != mFriendObservers.end(); -		) +	else  	{ -		LLFriendObserver* observer = *it; -		it++; -		// The only friend-related thing we notify on is online/offline transitions. -		observer->changed(LLFriendObserver::ONLINE); +		return std::string();  	}  } -void LLVoiceClient::lookupName(const LLUUID &id) -{ -	BOOL is_group = FALSE; -	gCacheName->get(id, is_group, &LLVoiceClient::onAvatarNameLookup); -} -//static -void LLVoiceClient::onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group) -{ -	if(gVoiceClient) -	{ -		std::string name = llformat("%s %s", first.c_str(), last.c_str()); -		gVoiceClient->avatarNameResolved(id, name); -	} -} +/////////////////// +// version checking -void LLVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name) +class LLViewerRequiredVoiceVersion : public LLHTTPNode  { -	// If the avatar whose name just resolved is on our friends list, resync the friends list. -	if(LLAvatarTracker::instance().getBuddyInfo(id) != NULL) -	{ -		mFriendsListDirty = true; -	} -	 -	// Iterate over all sessions. -	for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) +	static BOOL sAlertedUser; +	virtual void post( +					  LLHTTPNode::ResponsePtr response, +					  const LLSD& context, +					  const LLSD& input) const  	{ -		sessionState *session = *iter; - -		// Check for this user as a participant in this session -		participantState *participant = session->findParticipantByID(id); -		if(participant) -		{ -			// Found -- fill in the name -			participant->mAccountName = name; -			// and post a "participants updated" message to listeners later. -			session->mParticipantsChanged = true; -		} -		 -		// Check whether this is a p2p session whose caller name just resolved -		if(session->mCallerID == id) +		//You received this messsage (most likely on region cross or +		//teleport) +		if ( input.has("body") && input["body"].has("major_version") )  		{ -			// this session's "caller ID" just resolved.  Fill in the name. -			session->mName = name; -			if(session->mTextInvitePending) -			{ -				session->mTextInvitePending = false; - -				// We don't need to call gIMMgr->addP2PSession() here.  The first incoming message will create the panel.				 -			} -			if(session->mVoiceInvitePending) +			int major_voice_version = +			input["body"]["major_version"].asInteger(); +			// 			int minor_voice_version = +			// 				input["body"]["minor_version"].asInteger(); +			LLVoiceVersionInfo versionInfo = LLVoiceClient::getInstance()->getVersion(); +			 +			if (major_voice_version > 1)  			{ -				session->mVoiceInvitePending = false; - -				gIMMgr->inviteToSession( -					session->mIMSessionID, -					session->mName, -					session->mCallerID,  -					session->mName,  -					IM_SESSION_P2P_INVITE,  -					LLIMMgr::INVITATION_TYPE_VOICE, -					session->mHandle, -					session->mSIPURI); +				if (!sAlertedUser) +				{ +					//sAlertedUser = TRUE; +					LLNotificationsUtil::add("VoiceVersionMismatch"); +					gSavedSettings.setBOOL("EnableVoiceChat", FALSE); // toggles listener +				}  			} -			  		}  	} -} +};  class LLViewerParcelVoiceInfo : public LLHTTPNode  {  	virtual void post( -		LLHTTPNode::ResponsePtr response, -		const LLSD& context, -		const LLSD& input) const +					  LLHTTPNode::ResponsePtr response, +					  const LLSD& context, +					  const LLSD& input) const  	{  		//the parcel you are in has changed something about its  		//voice information - +		  		//this is a misnomer, as it can also be when you are not in  		//a parcel at all.  Should really be something like  		//LLViewerVoiceInfoChanged.....  		if ( input.has("body") )  		{  			LLSD body = input["body"]; - +			  			//body has "region_name" (str), "parcel_local_id"(int),  			//"voice_credentials" (map). - +			  			//body["voice_credentials"] has "channel_uri" (str),  			//body["voice_credentials"] has "channel_credentials" (str) - +			  			//if we really wanted to be extra careful,  			//we'd check the supplied  			//local parcel id to make sure it's for the same parcel @@ -7279,7 +764,7 @@ class LLViewerParcelVoiceInfo : public LLHTTPNode  				LLSD voice_credentials = body["voice_credentials"];  				std::string uri;  				std::string credentials; - +				  				if ( voice_credentials.has("channel_uri") )  				{  					uri = voice_credentials["channel_uri"].asString(); @@ -7287,51 +772,96 @@ class LLViewerParcelVoiceInfo : public LLHTTPNode  				if ( voice_credentials.has("channel_credentials") )  				{  					credentials = -						voice_credentials["channel_credentials"].asString(); +					voice_credentials["channel_credentials"].asString();  				} - -				gVoiceClient->setSpatialChannel(uri, credentials); +				 +				LLVoiceClient::getInstance()->setSpatialChannel(uri, credentials);  			}  		}  	}  }; -class LLViewerRequiredVoiceVersion : public LLHTTPNode +const std::string LLSpeakerVolumeStorage::SETTINGS_FILE_NAME = "volume_settings.xml"; + +LLSpeakerVolumeStorage::LLSpeakerVolumeStorage()  { -	static BOOL sAlertedUser; -	virtual void post( -		LLHTTPNode::ResponsePtr response, -		const LLSD& context, -		const LLSD& input) const +	load(); +} + +LLSpeakerVolumeStorage::~LLSpeakerVolumeStorage() +{ +	save(); +} + +void LLSpeakerVolumeStorage::storeSpeakerVolume(const LLUUID& speaker_id, F32 volume) +{ +	mSpeakersData[speaker_id] = volume; +} + +S32 LLSpeakerVolumeStorage::getSpeakerVolume(const LLUUID& speaker_id) +{ +	// Return value of -1 indicates no level is stored for this speaker +	S32 ret_val = -1; +	speaker_data_map_t::const_iterator it = mSpeakersData.find(speaker_id); +	 +	if (it != mSpeakersData.end())  	{ -		//You received this messsage (most likely on region cross or -		//teleport) -		if ( input.has("body") && input["body"].has("major_version") ) -		{ -			int major_voice_version = -				input["body"]["major_version"].asInteger(); -// 			int minor_voice_version = -// 				input["body"]["minor_version"].asInteger(); +		F32 f_val = it->second; +		// volume can amplify by as much as 4x! +		S32 ivol = (S32)(400.f * f_val * f_val); +		ret_val = llclamp(ivol, 0, 400); +	} +	return ret_val; +} -			if (gVoiceClient && -				(major_voice_version > VOICE_MAJOR_VERSION) ) -			{ -				if (!sAlertedUser) -				{ -					//sAlertedUser = TRUE; -					LLNotificationsUtil::add("VoiceVersionMismatch"); -					gSavedSettings.setBOOL("EnableVoiceChat", FALSE); // toggles listener -				} -			} +void LLSpeakerVolumeStorage::load() +{ +	// load per-resident voice volume information +	std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SETTINGS_FILE_NAME); + +	LLSD settings_llsd; +	llifstream file; +	file.open(filename); +	if (file.is_open()) +	{ +		LLSDSerialize::fromXML(settings_llsd, file); +	} + +	for (LLSD::map_const_iterator iter = settings_llsd.beginMap(); +		iter != settings_llsd.endMap(); ++iter) +	{ +		mSpeakersData.insert(std::make_pair(LLUUID(iter->first), (F32)iter->second.asReal())); +	} +} + +void LLSpeakerVolumeStorage::save() +{ +	// If we quit from the login screen we will not have an SL account +	// name.  Don't try to save, otherwise we'll dump a file in +	// C:\Program Files\SecondLife\ or similar. JC +	std::string user_dir = gDirUtilp->getLindenUserDir(); +	if (!user_dir.empty()) +	{ +		std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SETTINGS_FILE_NAME); +		LLSD settings_llsd; + +		for(speaker_data_map_t::const_iterator iter = mSpeakersData.begin(); iter != mSpeakersData.end(); ++iter) +		{ +			settings_llsd[iter->first.asString()] = iter->second;  		} + +		llofstream file; +		file.open(filename); +		LLSDSerialize::toPrettyXML(settings_llsd, file);  	} -}; +} +  BOOL LLViewerRequiredVoiceVersion::sAlertedUser = FALSE;  LLHTTPRegistration<LLViewerParcelVoiceInfo> -    gHTTPRegistrationMessageParcelVoiceInfo( -		"/message/ParcelVoiceInfo"); +gHTTPRegistrationMessageParcelVoiceInfo( +										"/message/ParcelVoiceInfo");  LLHTTPRegistration<LLViewerRequiredVoiceVersion> -    gHTTPRegistrationMessageRequiredVoiceVersion( -		"/message/RequiredVoiceVersion"); +gHTTPRegistrationMessageRequiredVoiceVersion( +											 "/message/RequiredVoiceVersion"); diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index a29c386182..f1a7d3dbec 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -17,8 +17,7 @@   * There are special exceptions to the terms and conditions of the GPL as   * it is applied to this Source Code. View the full text of the exception   * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception   *    * By copying, modifying or distributing this software, you acknowledge   * that you have read and understood your obligations described above, @@ -33,7 +32,6 @@  #define LL_VOICE_CLIENT_H  class LLVOAvatar; -class LLVivoxProtocolParser;  #include "lliopipe.h"  #include "llpumpio.h" @@ -42,9 +40,14 @@ class LLVivoxProtocolParser;  #include "v3math.h"  #include "llframetimer.h"  #include "llviewerregion.h" -#include "m3math.h"			// LLMatrix3 +#include "llcallingcard.h"   // for LLFriendObserver +#include "llsecapi.h" + +// devices + +typedef std::vector<std::string> LLVoiceDeviceList;	 + -class LLFriendObserver;  class LLVoiceClientParticipantObserver  {  public: @@ -52,6 +55,9 @@ public:  	virtual void onChange() = 0;  }; + +/////////////////////////////////// +/// @class LLVoiceClientStatusObserver  class LLVoiceClientStatusObserver  {  public: @@ -65,11 +71,7 @@ public:  		STATUS_JOINED,  		STATUS_LEFT_CHANNEL,  		STATUS_VOICE_DISABLED, - -		// Adding STATUS_VOICE_ENABLED as pair status for STATUS_VOICE_DISABLED -		// See LLVoiceClient::setVoiceEnabled()  		STATUS_VOICE_ENABLED, -  		BEGIN_ERROR_STATUS,  		ERROR_CHANNEL_FULL,  		ERROR_CHANNEL_LOCKED, @@ -83,699 +85,367 @@ public:  	static std::string status2string(EStatusType inStatus);  }; -class LLVoiceClient: public LLSingleton<LLVoiceClient> +struct LLVoiceVersionInfo  { -	LOG_CLASS(LLVoiceClient); -	public: -		LLVoiceClient();	 -		~LLVoiceClient(); -		 -	public: -		static void init(LLPumpIO *pump);	// Call this once at application startup (creates connector) -		static void terminate();	// Call this to clean up during shutdown -						 -	protected: -		bool writeString(const std::string &str); - -	public: -		 -		static F32 OVERDRIVEN_POWER_LEVEL; +	std::string serverType; +	std::string serverVersion; +}; -		static const F32 VOLUME_MIN; -		static const F32 VOLUME_DEFAULT; -		static const F32 VOLUME_MAX; +////////////////////////////////// +/// @class LLVoiceModuleInterface +/// @brief Voice module interface +/// +/// Voice modules should provide an implementation for this interface. +///////////////////////////////// -		void updateSettings(); // call after loading settings and whenever they change +class LLVoiceModuleInterface +{ +public: +	LLVoiceModuleInterface() {} +	virtual ~LLVoiceModuleInterface() {} -		void getCaptureDevicesSendMessage(); -		void getRenderDevicesSendMessage(); -		 -		void clearCaptureDevices(); -		void addCaptureDevice(const std::string& name); -		void setCaptureDevice(const std::string& name); -		 -		void clearRenderDevices(); -		void addRenderDevice(const std::string& name); -		void setRenderDevice(const std::string& name); - -		void tuningStart(); -		void tuningStop(); -		bool inTuningMode(); -		bool inTuningStates(); -		 -		void tuningRenderStartSendMessage(const std::string& name, bool loop); -		void tuningRenderStopSendMessage(); - -		void tuningCaptureStartSendMessage(int duration); -		void tuningCaptureStopSendMessage(); -		 -		void tuningSetMicVolume(float volume); -		void tuningSetSpeakerVolume(float volume); -		float tuningGetEnergy(void); -				 -		// This returns true when it's safe to bring up the "device settings" dialog in the prefs. -		// i.e. when the daemon is running and connected, and the device lists are populated. -		bool deviceSettingsAvailable(); -		 -		// Requery the vivox daemon for the current list of input/output devices. -		// If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed -		// (use this if you want to know when it's done). -		// If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. -		void refreshDeviceLists(bool clearCurrentList = true); -		 -		// Call this if the connection to the daemon terminates unexpectedly.  It will attempt to reset everything and relaunch. -		void daemonDied(); - -		// Call this if we're just giving up on voice (can't provision an account, etc.).  It will clean up and go away. -		void giveUp(); -		 -		///////////////////////////// -		// Response/Event handlers -		void connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID); -		void loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases); -		void sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); -		void sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); -		void sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString); -		void logoutResponse(int statusCode, std::string &statusString); -		void connectorShutdownResponse(int statusCode, std::string &statusString); - -		void accountLoginStateChangeEvent(std::string &accountHandle, int statusCode, std::string &statusString, int state); -		void mediaStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, int statusCode, std::string &statusString, int state, bool incoming); -		void textStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, bool enabled, int state, bool incoming); -		void sessionAddedEvent(std::string &uriString, std::string &alias, std::string &sessionHandle, std::string &sessionGroupHandle, bool isChannel, bool incoming, std::string &nameString, std::string &applicationString); -		void sessionGroupAddedEvent(std::string &sessionGroupHandle); -		void sessionRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle); -		void participantAddedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString, std::string &displayNameString, int participantType); -		void participantRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString); -		void participantUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, bool isModeratorMuted, bool isSpeaking, int volume, F32 energy); -		void auxAudioPropertiesEvent(F32 energy); -		void buddyPresenceEvent(std::string &uriString, std::string &alias, std::string &statusString, std::string &applicationString); -		void messageEvent(std::string &sessionHandle, std::string &uriString, std::string &alias, std::string &messageHeader, std::string &messageBody, std::string &applicationString); -		void sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType); -		void subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType); -		 -		void buddyListChanged(); -		void muteListChanged(); -		void updateFriends(U32 mask); -		 -		///////////////////////////// -		// Sending updates of current state -static	void updatePosition(void); -		void setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot); -		void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot); -		bool channelFromRegion(LLViewerRegion *region, std::string &name); -		void leaveChannel(void);		// call this on logout or teleport begin - -		 -		void setMuteMic(bool muted);		// Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state. -		bool getMuteMic() const; -		void setUserPTTState(bool ptt); -		bool getUserPTTState(); -		void toggleUserPTTState(void); -		void inputUserControlState(bool down); // interpret any sort of up-down mic-open control input according to ptt-toggle prefs -		void setVoiceEnabled(bool enabled); -		static bool voiceEnabled(); -		// Checks is voice working judging from mState -		// Returns true if vivox has successfully logged in and is not in error state -		bool voiceWorking(); -		void setUsePTT(bool usePTT); -		void setPTTIsToggle(bool PTTIsToggle); -		bool getPTTIsToggle(); -		void setPTTKey(std::string &key); -		void setEarLocation(S32 loc); -		void setVoiceVolume(F32 volume); -		void setMicGain(F32 volume); -		void setUserVolume(const LLUUID& id, F32 volume); // sets volume for specified agent, from 0-1 (where .5 is nominal) -		void setLipSyncEnabled(BOOL enabled); -		BOOL lipSyncEnabled(); - -		// PTT key triggering -		void keyDown(KEY key, MASK mask); -		void keyUp(KEY key, MASK mask); -		void middleMouseState(bool down); - -		// Return the version of the Vivox library -		std::string getAPIVersion() const { return mAPIVersion; } -		 -		///////////////////////////// -		// Accessors for data related to nearby speakers -		BOOL getVoiceEnabled(const LLUUID& id);		// true if we've received data for this avatar -		BOOL getIsSpeaking(const LLUUID& id); -		BOOL getIsModeratorMuted(const LLUUID& id); -		F32 getCurrentPower(const LLUUID& id);		// "power" is related to "amplitude" in a defined way.  I'm just not sure what the formula is... -		BOOL getOnMuteList(const LLUUID& id); -		F32 getUserVolume(const LLUUID& id); -		std::string getDisplayName(const LLUUID& id); -		 -		// MBW -- XXX -- Not sure how to get this data out of the TVC -		BOOL getUsingPTT(const LLUUID& id); -		std::string getGroupID(const LLUUID& id);		// group ID if the user is in group chat (empty string if not applicable) - -		///////////////////////////// -		BOOL getAreaVoiceDisabled();		// returns true if the area the avatar is in is speech-disabled. -											// Use this to determine whether to show a "no speech" icon in the menu bar. -		 -		///////////////////////////// -		// Recording controls -		void recordingLoopStart(int seconds = 3600, int deltaFramesPerControlFrame = 200); -		void recordingLoopSave(const std::string& filename); -		void recordingStop(); -		 -		// Playback controls -		void filePlaybackStart(const std::string& filename); -		void filePlaybackStop(); -		void filePlaybackSetPaused(bool paused); -		void filePlaybackSetMode(bool vox = false, float speed = 1.0f); -		 -		 -		// This is used by the string-keyed maps below, to avoid storing the string twice. -		// The 'const std::string *' in the key points to a string actually stored in the object referenced by the map. -		// The add and delete operations for each map allocate and delete in the right order to avoid dangling references. -		// The default compare operation would just compare pointers, which is incorrect, so they must use this comparitor instead. -		struct stringMapComparitor -		{ -			bool operator()(const std::string* a, const std::string * b) const -			{ -				return a->compare(*b) < 0; -			} -		}; - -		struct uuidMapComparitor -		{ -			bool operator()(const LLUUID* a, const LLUUID * b) const -			{ -				return *a < *b; -			} -		}; -		 -		struct participantState -		{ -		public: -			participantState(const std::string &uri); - -			bool updateMuteState();	// true if mute state has changed -			bool isAvatar(); - -			std::string mURI; -			LLUUID mAvatarID; -			std::string mAccountName; -			std::string mDisplayName; -			LLFrameTimer mSpeakingTimeout; -			F32	mLastSpokeTimestamp; -			F32 mPower; -			F32 mVolume; -			std::string mGroupID; -			bool mPTT; -			bool mIsSpeaking; -			bool mIsModeratorMuted; -			bool mOnMuteList;		// true if this avatar is on the user's mute list (and should be muted) -			bool mVolumeSet;		// true if incoming volume messages should not change the volume -			bool mVolumeDirty;		// true if this participant needs a volume command sent (either mOnMuteList or mUserVolume has changed) -			bool mAvatarIDValid; -			bool mIsSelf; -		}; -		typedef std::map<const std::string *, participantState*, stringMapComparitor> participantMap; - -		typedef std::map<const LLUUID *, participantState*, uuidMapComparitor> participantUUIDMap; -	 -		enum streamState -		{ -			streamStateUnknown = 0, -			streamStateIdle = 1, -			streamStateConnected = 2, -			streamStateRinging = 3, -		}; -		 -		struct sessionState -		{ -		public: -			sessionState(); -			~sessionState(); - -			participantState *addParticipant(const std::string &uri); -			// Note: after removeParticipant returns, the participant* that was passed to it will have been deleted. -			// Take care not to use the pointer again after that. -			void removeParticipant(participantState *participant); -			void removeAllParticipants(); - -			participantState *findParticipant(const std::string &uri); -			participantState *findParticipantByID(const LLUUID& id); - -			bool isCallBackPossible(); -			bool isTextIMPossible(); - -			std::string mHandle; -			std::string mGroupHandle; -			std::string mSIPURI; -			std::string mAlias; -			std::string mName; -			std::string mAlternateSIPURI; -			std::string mHash;			// Channel password -			std::string mErrorStatusString; -			std::queue<std::string> mTextMsgQueue; -			 -			LLUUID		mIMSessionID; -			LLUUID		mCallerID; -			int			mErrorStatusCode; -			int			mMediaStreamState; -			int			mTextStreamState; -			bool		mCreateInProgress;	// True if a Session.Create has been sent for this session and no response has been received yet. -			bool		mMediaConnectInProgress;	// True if a Session.MediaConnect has been sent for this session and no response has been received yet. -			bool		mVoiceInvitePending;	// True if a voice invite is pending for this session (usually waiting on a name lookup) -			bool		mTextInvitePending;		// True if a text invite is pending for this session (usually waiting on a name lookup) -			bool		mSynthesizedCallerID;	// True if the caller ID is a hash of the SIP URI -- this means we shouldn't do a name lookup. -			bool		mIsChannel;	// True for both group and spatial channels (false for p2p, PSTN) -			bool		mIsSpatial;	// True for spatial channels -			bool		mIsP2P; -			bool		mIncoming; -			bool		mVoiceEnabled; -			bool		mReconnect;	// Whether we should try to reconnect to this session if it's dropped -			// Set to true when the mute state of someone in the participant list changes. -			// The code will have to walk the list to find the changed participant(s). -			bool		mVolumeDirty; -			bool		mMuteDirty; - -			bool		mParticipantsChanged; -			participantMap mParticipantsByURI; -			participantUUIDMap mParticipantsByUUID; -		}; - -		participantState *findParticipantByID(const LLUUID& id); -		participantMap *getParticipantList(void); -		void getParticipantsUUIDSet(std::set<LLUUID>& participant_uuids); -		 -		typedef std::map<const std::string*, sessionState*, stringMapComparitor> sessionMap; -		typedef std::set<sessionState*> sessionSet; -				 -		typedef sessionSet::iterator sessionIterator; -		sessionIterator sessionsBegin(void); -		sessionIterator sessionsEnd(void); - -		sessionState *findSession(const std::string &handle); -		sessionState *findSessionBeingCreatedByURI(const std::string &uri); -		sessionState *findSession(const LLUUID &participant_id); -		sessionState *findSessionByCreateID(const std::string &create_id); -		 -		sessionState *addSession(const std::string &uri, const std::string &handle = LLStringUtil::null); -		void setSessionHandle(sessionState *session, const std::string &handle = LLStringUtil::null); -		void setSessionURI(sessionState *session, const std::string &uri); -		void deleteSession(sessionState *session); -		void deleteAllSessions(void); +	virtual void init(LLPumpIO *pump)=0;	// Call this once at application startup (creates connector) +	virtual void terminate()=0;	// Call this to clean up during shutdown +	 +	virtual void updateSettings()=0; // call after loading settings and whenever they change +	 +	virtual bool isVoiceWorking()=0; // connected to a voice server and voice channel -		void verifySessionState(void); +	virtual const LLVoiceVersionInfo& getVersion()=0; +	 +	///////////////////// +	/// @name Tuning +	//@{ +	virtual void tuningStart()=0; +	virtual void tuningStop()=0; +	virtual bool inTuningMode()=0; +	 +	virtual void tuningSetMicVolume(float volume)=0; +	virtual void tuningSetSpeakerVolume(float volume)=0; +	virtual float tuningGetEnergy(void)=0; +	//@} +	 +	///////////////////// +	/// @name Devices +	//@{ +	// This returns true when it's safe to bring up the "device settings" dialog in the prefs. +	// i.e. when the daemon is running and connected, and the device lists are populated. +	virtual bool deviceSettingsAvailable()=0; +	 +	// Requery the vivox daemon for the current list of input/output devices. +	// If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed +	// (use this if you want to know when it's done). +	// If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. +	virtual void refreshDeviceLists(bool clearCurrentList = true)=0; +	 +	virtual void setCaptureDevice(const std::string& name)=0; +	virtual void setRenderDevice(const std::string& name)=0; +	 +	virtual LLVoiceDeviceList& getCaptureDevices()=0; +	virtual LLVoiceDeviceList& getRenderDevices()=0; +	 +	virtual void getParticipantList(std::set<LLUUID> &participants)=0; +	virtual bool isParticipant(const LLUUID& speaker_id)=0; +	//@} +	 +	//////////////////////////// +	/// @ name Channel stuff +	//@{ +	// returns true iff the user is currently in a proximal (local spatial) channel. +	// Note that gestures should only fire if this returns true. +	virtual bool inProximalChannel()=0; +	 +	virtual void setNonSpatialChannel(const std::string &uri, +									  const std::string &credentials)=0; +	 +	virtual void setSpatialChannel(const std::string &uri, +								   const std::string &credentials)=0; +	 +	virtual void leaveNonSpatialChannel()=0; +	 +	virtual void leaveChannel(void)=0;	 +	 +	// Returns the URI of the current channel, or an empty string if not currently in a channel. +	// NOTE that it will return an empty string if it's in the process of joining a channel. +	virtual std::string getCurrentChannel()=0; +	//@} +	 +	 +	////////////////////////// +	/// @name invitations +	//@{ +	// start a voice channel with the specified user +	virtual void callUser(const LLUUID &uuid)=0; +	virtual bool answerInvite(std::string &channelHandle)=0; +	virtual void declineInvite(std::string &channelHandle)=0; +	//@} +	 +	///////////////////////// +	/// @name Volume/gain +	//@{ +	virtual void setVoiceVolume(F32 volume)=0; +	virtual void setMicGain(F32 volume)=0; +	//@} +	 +	///////////////////////// +	/// @name enable disable voice and features +	//@{ +	virtual bool voiceEnabled()=0; +	virtual void setVoiceEnabled(bool enabled)=0; +	virtual void setLipSyncEnabled(BOOL enabled)=0; +	virtual BOOL lipSyncEnabled()=0;	 +	virtual void setMuteMic(bool muted)=0;		// Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state. +	//@} +	 +	//////////////////////// +	/// @name PTT +	//@{ +	virtual void setUserPTTState(bool ptt)=0; +	virtual bool getUserPTTState()=0; +	virtual void setUsePTT(bool usePTT)=0; +	virtual void setPTTIsToggle(bool PTTIsToggle)=0; +	virtual bool getPTTIsToggle()=0;	 +	virtual void toggleUserPTTState(void)=0; +	virtual void inputUserControlState(bool down)=0;  // interpret any sort of up-down mic-open control input according to ptt-toggle prefs +	 +	virtual void keyDown(KEY key, MASK mask)=0; +	virtual void keyUp(KEY key, MASK mask)=0; +	virtual void middleMouseState(bool down)=0; +	//@} +	 +	////////////////////////// +	/// @name nearby speaker accessors +	//@{ + + +	virtual BOOL getVoiceEnabled(const LLUUID& id)=0;		// true if we've received data for this avatar +	virtual std::string getDisplayName(const LLUUID& id)=0; +	virtual BOOL isOnlineSIP(const LLUUID &id)=0;	 +	virtual BOOL isParticipantAvatar(const LLUUID &id)=0; +	virtual BOOL getIsSpeaking(const LLUUID& id)=0; +	virtual BOOL getIsModeratorMuted(const LLUUID& id)=0; +	virtual F32 getCurrentPower(const LLUUID& id)=0;		// "power" is related to "amplitude" in a defined way.  I'm just not sure what the formula is... +	virtual BOOL getOnMuteList(const LLUUID& id)=0; +	virtual F32 getUserVolume(const LLUUID& id)=0; +	virtual void setUserVolume(const LLUUID& id, F32 volume)=0; // set's volume for specified agent, from 0-1 (where .5 is nominal)	 +	//@} +	 +	////////////////////////// +	/// @name text chat +	//@{ +	virtual BOOL isSessionTextIMPossible(const LLUUID& id)=0; +	virtual BOOL isSessionCallBackPossible(const LLUUID& id)=0; +	virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message)=0; +	virtual void endUserIMSession(const LLUUID &uuid)=0;	 +	//@} +	 +	// authorize the user +	virtual void userAuthorized(const std::string& user_id, +								const LLUUID &agentID)=0; +	 +	////////////////////////////// +	/// @name Status notification +	//@{ +	virtual void addObserver(LLVoiceClientStatusObserver* observer)=0; +	virtual void removeObserver(LLVoiceClientStatusObserver* observer)=0; +	virtual void addObserver(LLFriendObserver* observer)=0; +	virtual void removeObserver(LLFriendObserver* observer)=0;	 +	virtual void addObserver(LLVoiceClientParticipantObserver* observer)=0; +	virtual void removeObserver(LLVoiceClientParticipantObserver* observer)=0;	 +	//@} +	 +	virtual std::string sipURIFromID(const LLUUID &id)=0; +	//@} +	 +}; -		void joinedAudioSession(sessionState *session); -		void leftAudioSession(sessionState *session); -		// This is called in several places where the session _may_ need to be deleted. -		// It contains logic for whether to delete the session or keep it around. -		void reapSession(sessionState *session); -		 -		// Returns true if the session seems to indicate we've moved to a region on a different voice server -		bool sessionNeedsRelog(sessionState *session); -		 -		struct buddyListEntry -		{ -			buddyListEntry(const std::string &uri); -			std::string mURI; -			std::string mDisplayName; -			LLUUID	mUUID; -			bool mOnlineSL; -			bool mOnlineSLim; -			bool mCanSeeMeOnline; -			bool mHasBlockListEntry; -			bool mHasAutoAcceptListEntry; -			bool mNameResolved; -			bool mInSLFriends; -			bool mInVivoxBuddies; -			bool mNeedsNameUpdate; -		}; - -		typedef std::map<const std::string*, buddyListEntry*, stringMapComparitor> buddyListMap; -		 -		// This should be called when parsing a buddy list entry sent by SLVoice.		 -		void processBuddyListEntry(const std::string &uri, const std::string &displayName); - -		buddyListEntry *addBuddy(const std::string &uri); -		buddyListEntry *addBuddy(const std::string &uri, const std::string &displayName); -		buddyListEntry *findBuddy(const std::string &uri); -		buddyListEntry *findBuddy(const LLUUID &id); -		buddyListEntry *findBuddyByDisplayName(const std::string &name); -		void deleteBuddy(const std::string &uri); -		void deleteAllBuddies(void); - -		void deleteAllBlockRules(void); -		void addBlockRule(const std::string &blockMask, const std::string &presenceOnly); -		void deleteAllAutoAcceptRules(void); -		void addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy); -		void accountListBlockRulesResponse(int statusCode, const std::string &statusString);						 -		void accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString);						 -		 -		///////////////////////////// -		// session control messages -		void connectorCreate(); -		void connectorShutdown(); - -		void requestVoiceAccountProvision(S32 retries = 3); -		void userAuthorized( -			const std::string& firstName, -			const std::string& lastName, -			const LLUUID &agentID); -		void login( -			const std::string& account_name, -			const std::string& password, -			const std::string& voice_sip_uri_hostname, -			const std::string& voice_account_server_uri); -		void loginSendMessage(); -		void logout(); -		void logoutSendMessage(); - -		void accountListBlockRulesSendMessage(); -		void accountListAutoAcceptRulesSendMessage(); -		 -		void sessionGroupCreateSendMessage(); -		void sessionCreateSendMessage(sessionState *session, bool startAudio = true, bool startText = false); -		void sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio = true, bool startText = false); -		void sessionMediaConnectSendMessage(sessionState *session);		// just joins the audio session -		void sessionTextConnectSendMessage(sessionState *session);		// just joins the text session -		void sessionTerminateSendMessage(sessionState *session); -		void sessionGroupTerminateSendMessage(sessionState *session); -		void sessionMediaDisconnectSendMessage(sessionState *session); -		void sessionTextDisconnectSendMessage(sessionState *session); - -		// Pokes the state machine to leave the audio session next time around. -		void sessionTerminate();	 -		 -		// Pokes the state machine to shut down the connector and restart it. -		void requestRelog(); -		 -		// Does the actual work to get out of the audio session -		void leaveAudioSession(); -		 -		void addObserver(LLVoiceClientParticipantObserver* observer); -		void removeObserver(LLVoiceClientParticipantObserver* observer); +class LLVoiceClient: public LLSingleton<LLVoiceClient> +{ +	LOG_CLASS(LLVoiceClient); +public: +	LLVoiceClient();	 +	~LLVoiceClient(); -		void addObserver(LLVoiceClientStatusObserver* observer); -		void removeObserver(LLVoiceClientStatusObserver* observer); +	void init(LLPumpIO *pump);	// Call this once at application startup (creates connector) +	void terminate();	// Call this to clean up during shutdown +	 +	const LLVoiceVersionInfo getVersion(); +	 +static const F32 OVERDRIVEN_POWER_LEVEL; -		void addObserver(LLFriendObserver* observer); -		void removeObserver(LLFriendObserver* observer); -		 -		void lookupName(const LLUUID &id); -		static void onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group); -		void avatarNameResolved(const LLUUID &id, const std::string &name); -		 -		typedef std::vector<std::string> deviceList; +	void updateSettings(); // call after loading settings and whenever they change -		deviceList *getCaptureDevices(); -		deviceList *getRenderDevices(); -		 -		void setNonSpatialChannel( -			const std::string &uri, -			const std::string &credentials); -		void setSpatialChannel( -			const std::string &uri, -			const std::string &credentials); -		// start a voice session with the specified user -		void callUser(const LLUUID &uuid); -		 -		// Send a text message to the specified user, initiating the session if necessary. -		bool sendTextMessage(const LLUUID& participant_id, const std::string& message); -		 -		// close any existing text IM session with the specified user -		void endUserIMSession(const LLUUID &uuid); -		 -		bool answerInvite(std::string &sessionHandle); -		void declineInvite(std::string &sessionHandle); -		void leaveNonSpatialChannel(); +	bool isVoiceWorking(); // connected to a voice server and voice channel -		// Returns the URI of the current channel, or an empty string if not currently in a channel. -		// NOTE that it will return an empty string if it's in the process of joining a channel. -		std::string getCurrentChannel(); +	// tuning +	void tuningStart(); +	void tuningStop(); +	bool inTuningMode(); -		// returns true iff the user is currently in a proximal (local spatial) channel. -		// Note that gestures should only fire if this returns true. -		bool inProximalChannel(); - -		std::string sipURIFromID(const LLUUID &id); +	void tuningSetMicVolume(float volume); +	void tuningSetSpeakerVolume(float volume); +	float tuningGetEnergy(void); -		// Returns true if the indicated user is online via SIP presence according to SLVoice. -		// Note that we only get SIP presence data for other users that are in our vivox buddy list. -		bool isOnlineSIP(const LLUUID &id); - -		// Returns true if the indicated participant is really an SL avatar. -		// This should be used to control the state of the "profile" button. -		// Currently this will be false only for PSTN callers into group chats, and PSTN p2p calls. -		bool isParticipantAvatar(const LLUUID &id); -		 -		// Returns true if calling back the session URI after the session has closed is possible. -		// Currently this will be false only for PSTN P2P calls.		 -		// NOTE: this will return true if the session can't be found.  -		bool isSessionCallBackPossible(const LLUUID &session_id); -		 -		// Returns true if the session can accepte text IM's. -		// Currently this will be false only for PSTN P2P calls. -		// NOTE: this will return true if the session can't be found.  -		bool isSessionTextIMPossible(const LLUUID &session_id); -		 -	private: - -		// internal state for a simple state machine.  This is used to deal with the asynchronous nature of some of the messages. -		// Note: if you change this list, please make corresponding changes to LLVoiceClient::state2string(). -		enum state -		{ -			stateDisableCleanup, -			stateDisabled,				// Voice is turned off. -			stateStart,					// Class is initialized, socket is created -			stateDaemonLaunched,		// Daemon has been launched -			stateConnecting,			// connect() call has been issued -			stateConnected,				// connection to the daemon has been made, send some initial setup commands. -			stateIdle,					// socket is connected, ready for messaging -			stateMicTuningStart, -			stateMicTuningRunning,		 -			stateMicTuningStop, -			stateConnectorStart,		// connector needs to be started -			stateConnectorStarting,		// waiting for connector handle -			stateConnectorStarted,		// connector handle received -			stateLoginRetry,			// need to retry login (failed due to changing password) -			stateLoginRetryWait,		// waiting for retry timer -			stateNeedsLogin,			// send login request -			stateLoggingIn,				// waiting for account handle -			stateLoggedIn,				// account handle received -			stateCreatingSessionGroup,	// Creating the main session group -			stateNoChannel,				//  -			stateJoiningSession,		// waiting for session handle -			stateSessionJoined,			// session handle received -			stateRunning,				// in session, steady state -			stateLeavingSession,		// waiting for terminate session response -			stateSessionTerminated,		// waiting for terminate session response - -			stateLoggingOut,			// waiting for logout response -			stateLoggedOut,				// logout response received -			stateConnectorStopping,		// waiting for connector stop -			stateConnectorStopped,		// connector stop received -			 -			// We go to this state if the login fails because the account needs to be provisioned. -			 -			// error states.  No way to recover from these yet. -			stateConnectorFailed, -			stateConnectorFailedWaiting, -			stateLoginFailed, -			stateLoginFailedWaiting, -			stateJoinSessionFailed, -			stateJoinSessionFailedWaiting, - -			stateJail					// Go here when all else has failed.  Nothing will be retried, we're done. -		}; -		 -		state mState; -		bool mSessionTerminateRequested; -		bool mRelogRequested; -		// Number of times (in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine(). -		// The larger it is the greater is possibility there is a problem with connection to voice server. -		// Introduced while fixing EXT-4313. -		int mSpatialJoiningNum; -		 -		void setState(state inState); -		state getState(void)  { return mState; }; -		static std::string state2string(state inState); -		 -		void stateMachine(); -		static void idle(void *user_data); -		 -		LLHost mDaemonHost; -		LLSocket::ptr_t mSocket; -		bool mConnected; -		 -		void closeSocket(void); -		 -		LLPumpIO *mPump; -		friend class LLVivoxProtocolParser; -		 -		std::string mAccountName; -		std::string mAccountPassword; -		std::string mAccountDisplayName; -		std::string mAccountFirstName; -		std::string mAccountLastName; -				 -		bool mTuningMode; -		float mTuningEnergy; -		std::string mTuningAudioFile; -		int mTuningMicVolume; -		bool mTuningMicVolumeDirty; -		int mTuningSpeakerVolume; -		bool mTuningSpeakerVolumeDirty; -		state mTuningExitState;					// state to return to when we leave tuning mode. +	// devices +	 +	// This returns true when it's safe to bring up the "device settings" dialog in the prefs. +	// i.e. when the daemon is running and connected, and the device lists are populated. +	bool deviceSettingsAvailable(); -		std::string mSpatialSessionURI; -		std::string mSpatialSessionCredentials; +	// Requery the vivox daemon for the current list of input/output devices. +	// If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed +	// (use this if you want to know when it's done). +	// If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. +	void refreshDeviceLists(bool clearCurrentList = true); -		std::string mMainSessionGroupHandle; // handle of the "main" session group. -		 -		std::string mChannelName;			// Name of the channel to be looked up  -		bool mAreaVoiceDisabled; -		sessionState *mAudioSession;		// Session state for the current audio session -		bool mAudioSessionChanged;			// set to true when the above pointer gets changed, so observers can be notified. +	void setCaptureDevice(const std::string& name); +	void setRenderDevice(const std::string& name); -		sessionState *mNextAudioSession;	// Session state for the audio session we're trying to join +	const LLVoiceDeviceList& getCaptureDevices(); +	const LLVoiceDeviceList& getRenderDevices(); -//		std::string mSessionURI;			// URI of the session we're in. -//		std::string mSessionHandle;		// returned by ? -		 -		S32 mCurrentParcelLocalID;			// Used to detect parcel boundary crossings -		std::string mCurrentRegionName;		// Used to detect parcel boundary crossings -		 -		std::string mConnectorHandle;	// returned by "Create Connector" message -		std::string mAccountHandle;		// returned by login message		 -		int 		mNumberOfAliases; -		U32 mCommandCookie; +	//////////////////////////// +	// Channel stuff +	// +	 +	// returns true iff the user is currently in a proximal (local spatial) channel. +	// Note that gestures should only fire if this returns true. +	bool inProximalChannel(); +	void setNonSpatialChannel( +							  const std::string &uri, +							  const std::string &credentials); +	void setSpatialChannel( +						   const std::string &uri, +						   const std::string &credentials); +	void leaveNonSpatialChannel(); +	 +	// Returns the URI of the current channel, or an empty string if not currently in a channel. +	// NOTE that it will return an empty string if it's in the process of joining a channel. +	std::string getCurrentChannel(); +	// start a voice channel with the specified user +	void callUser(const LLUUID &uuid);	 +	bool answerInvite(std::string &channelHandle); +	void declineInvite(std::string &channelHandle);	 +	void leaveChannel(void);		// call this on logout or teleport begin +	 +	 +	///////////////////////////// +	// Sending updates of current state -		std::string mVoiceAccountServerURI; -		std::string mVoiceSIPURIHostName; -		 -		int mLoginRetryCount; -		 -		sessionMap mSessionsByHandle;				// Active sessions, indexed by session handle.  Sessions which are being initiated may not be in this map. -		sessionSet mSessions;						// All sessions, not indexed.  This is the canonical session list. -		 -		bool mBuddyListMapPopulated; -		bool mBlockRulesListReceived; -		bool mAutoAcceptRulesListReceived; -		buddyListMap mBuddyListMap; -		 -		deviceList mCaptureDevices; -		deviceList mRenderDevices; - -		std::string mCaptureDevice; -		std::string mRenderDevice; -		bool mCaptureDeviceDirty; -		bool mRenderDeviceDirty; -		 -		// This should be called when the code detects we have changed parcels. -		// It initiates the call to the server that gets the parcel channel. -		void parcelChanged(); -		 -	void switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = ""); -		void joinSession(sessionState *session); -		 -static 	std::string nameFromAvatar(LLVOAvatar *avatar); -static	std::string nameFromID(const LLUUID &id); -static	bool IDFromName(const std::string name, LLUUID &uuid); -static	std::string displayNameFromAvatar(LLVOAvatar *avatar); -		std::string sipURIFromAvatar(LLVOAvatar *avatar); -		std::string sipURIFromName(std::string &name); -		 -		// Returns the name portion of the SIP URI if the string looks vaguely like a SIP URI, or an empty string if not. -static	std::string nameFromsipURI(const std::string &uri);		 -		bool inSpatialChannel(void); -		std::string getAudioSessionURI(); -		std::string getAudioSessionHandle(); -				 -		void sendPositionalUpdate(void); -		 -		void buildSetCaptureDevice(std::ostringstream &stream); -		void buildSetRenderDevice(std::ostringstream &stream); -		void buildLocalAudioUpdates(std::ostringstream &stream); -		 -		void clearAllLists(); -		void checkFriend(const LLUUID& id); -		void sendFriendsListUpdates(); - -		// start a text IM session with the specified user -		// This will be asynchronous, the session may be established at a future time. -		sessionState* startUserIMSession(const LLUUID& uuid); -		void sendQueuedTextMessages(sessionState *session); -		 -		void enforceTether(void); -		 -		bool		mSpatialCoordsDirty; -		 -		LLVector3d	mCameraPosition; -		LLVector3d	mCameraRequestedPosition; -		LLVector3	mCameraVelocity; -		LLMatrix3	mCameraRot; - -		LLVector3d	mAvatarPosition; -		LLVector3	mAvatarVelocity; -		LLMatrix3	mAvatarRot; -		 -		bool		mPTTDirty; -		bool		mPTT; -		 -		bool		mUsePTT; -		bool		mPTTIsMiddleMouse; -		KEY			mPTTKey; -		bool		mPTTIsToggle; -		bool		mUserPTTState; -		bool		mMuteMic; -				 -		// Set to true when the friends list is known to have changed. -		bool		mFriendsListDirty; -		 -		enum -		{ -			earLocCamera = 0,		// ear at camera -			earLocAvatar,			// ear at avatar -			earLocMixed				// ear at avatar location/camera direction -		}; -		 -		S32			mEarLocation;   -		 -		bool		mSpeakerVolumeDirty; -		bool		mSpeakerMuteDirty; -		int			mSpeakerVolume; +	void setVoiceVolume(F32 volume); +	void setMicGain(F32 volume); +	void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal)		 +	bool voiceEnabled(); +	void setLipSyncEnabled(BOOL enabled); +	void setMuteMic(bool muted);		// Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state. +	void setUserPTTState(bool ptt); +	bool getUserPTTState(); +	void toggleUserPTTState(void); +	void inputUserControlState(bool down);  // interpret any sort of up-down mic-open control input according to ptt-toggle prefs	 +	void setVoiceEnabled(bool enabled); + +	void setUsePTT(bool usePTT); +	void setPTTIsToggle(bool PTTIsToggle); +	bool getPTTIsToggle();	 +	 +	BOOL lipSyncEnabled(); +	 +	// PTT key triggering +	void keyDown(KEY key, MASK mask); +	void keyUp(KEY key, MASK mask); +	void middleMouseState(bool down); +	 +	 +	///////////////////////////// +	// Accessors for data related to nearby speakers +	BOOL getVoiceEnabled(const LLUUID& id);		// true if we've received data for this avatar +	std::string getDisplayName(const LLUUID& id);	 +	BOOL isOnlineSIP(const LLUUID &id); +	BOOL isParticipantAvatar(const LLUUID &id); +	BOOL getIsSpeaking(const LLUUID& id); +	BOOL getIsModeratorMuted(const LLUUID& id); +	F32 getCurrentPower(const LLUUID& id);		// "power" is related to "amplitude" in a defined way.  I'm just not sure what the formula is... +	BOOL getOnMuteList(const LLUUID& id); +	F32 getUserVolume(const LLUUID& id); + +	///////////////////////////// +	BOOL getAreaVoiceDisabled();		// returns true if the area the avatar is in is speech-disabled. +													  // Use this to determine whether to show a "no speech" icon in the menu bar. +	void getParticipantList(std::set<LLUUID> &participants); +	bool isParticipant(const LLUUID& speaker_id); +	 +	////////////////////////// +	/// @name text chat +	//@{ +	BOOL isSessionTextIMPossible(const LLUUID& id); +	BOOL isSessionCallBackPossible(const LLUUID& id); +	BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message); +	void endUserIMSession(const LLUUID &uuid);	 +	//@} +	 -		int			mMicVolume; -		bool		mMicVolumeDirty; -		 -		bool		mVoiceEnabled; -		bool		mWriteInProgress; -		std::string mWriteString; -		 -		LLTimer		mUpdateTimer; +	void userAuthorized(const std::string& user_id, +			const LLUUID &agentID); +	 +	void addObserver(LLVoiceClientStatusObserver* observer); +	void removeObserver(LLVoiceClientStatusObserver* observer); +	void addObserver(LLFriendObserver* observer); +	void removeObserver(LLFriendObserver* observer); +	void addObserver(LLVoiceClientParticipantObserver* observer); +	void removeObserver(LLVoiceClientParticipantObserver* observer); +	 +	std::string sipURIFromID(const LLUUID &id);	 -		BOOL		mLipSyncEnabled; - -		std::string	mAPIVersion; - -		typedef std::set<LLVoiceClientParticipantObserver*> observer_set_t; -		observer_set_t mParticipantObservers; +protected: +	LLVoiceModuleInterface* mVoiceModule; +	LLPumpIO *m_servicePump; +}; -		void notifyParticipantObservers(); +/** + * Speaker volume storage helper class + **/ -		typedef std::set<LLVoiceClientStatusObserver*> status_observer_set_t; -		status_observer_set_t mStatusObservers; -		 -		void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status); +class LLSpeakerVolumeStorage : public LLSingleton<LLSpeakerVolumeStorage> +{ +	LOG_CLASS(LLSpeakerVolumeStorage); +public: -		typedef std::set<LLFriendObserver*> friend_observer_set_t; -		friend_observer_set_t mFriendObservers; -		void notifyFriendObservers(); +	/** +	 * Sets internal voluem level for specified user. +	 * +	 * @param[in] speaker_id - LLUUID of user to store volume level for +	 * @param[in] volume - external volume level to be stored for user. +	 */ +	void storeSpeakerVolume(const LLUUID& speaker_id, F32 volume); + +	/** +	 * Gets stored external volume level for specified speaker. +	 * +	 * If specified user is not found default level will be returned. It is equivalent of  +	 * external level 0.5 from the 0.0..1.0 range. +	 * Default external level is calculated as: internal = 400 * external^2 +	 * Maps 0.0 to 1.0 to internal values 0-400 with default 0.5 == 100 +	 * +	 * @param[in] speaker_id - LLUUID of user to get his volume level +	 */ +	S32 getSpeakerVolume(const LLUUID& speaker_id); + +private: +	friend class LLSingleton<LLSpeakerVolumeStorage>; +	LLSpeakerVolumeStorage(); +	~LLSpeakerVolumeStorage(); + +	const static std::string SETTINGS_FILE_NAME; + +	void load(); +	void save(); + +	typedef std::map<LLUUID, F32> speaker_data_map_t; +	speaker_data_map_t mSpeakersData;  }; -extern LLVoiceClient *gVoiceClient; -  #endif //LL_VOICE_CLIENT_H diff --git a/indra/newview/llvoicevivox.cpp b/indra/newview/llvoicevivox.cpp new file mode 100644 index 0000000000..2f3bd567da --- /dev/null +++ b/indra/newview/llvoicevivox.cpp @@ -0,0 +1,6967 @@ + /**  + * @file llvoicevivox.cpp + * @brief Implementation of LLVivoxVoiceClient class which is the interface to the voice client process. + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + *  + * Copyright (c) 2001-2010, Linden Research, Inc. + *  + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab.  Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + *  + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + *  + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + *  + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llvoicevivox.h" + +#include <boost/tokenizer.hpp> + +#include "llsdutil.h" + +#include "llvoavatarself.h" +#include "llbufferstream.h" +#include "llfile.h" +#ifdef LL_STANDALONE +# include "expat.h" +#else +# include "expat/expat.h" +#endif +#include "llcallbacklist.h" +#include "llviewerregion.h" +#include "llviewernetwork.h"		// for gGridChoice +#include "llbase64.h" +#include "llviewercontrol.h" +#include "llkeyboard.h" +#include "llappviewer.h"	// for gDisconnected, gDisableVoice +#include "llmutelist.h"  // to check for muted avatars +#include "llagent.h" +#include "llcachename.h" +#include "llimview.h" // for LLIMMgr +#include "llparcel.h" +#include "llviewerparcelmgr.h" +//#include "llfirstuse.h" +#include "llspeakers.h" +#include "llviewerwindow.h" +#include "llviewercamera.h" + +#include "llfloaterfriends.h"  //VIVOX, inorder to refresh communicate panel +#include "llviewernetwork.h" +#include "llnotificationsutil.h" + +// for base64 decoding +#include "apr_base64.h" + +// for SHA1 hash +#include "apr_sha1.h" + +// for MD5 hash +#include "llmd5.h" + +#define USE_SESSION_GROUPS 0 + +const F32 SPEAKING_TIMEOUT = 1.f; + +static const std::string VOICE_SERVER_TYPE = "Vivox"; + +// Don't retry connecting to the daemon more frequently than this: +const F32 CONNECT_THROTTLE_SECONDS = 1.0f; + +// Don't send positional updates more frequently than this: +const F32 UPDATE_THROTTLE_SECONDS = 0.1f; + +const F32 LOGIN_RETRY_SECONDS = 10.0f; +const int MAX_LOGIN_RETRIES = 12; + +// Defines the maximum number of times(in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine() +// which is treated as normal. If this number is exceeded we suspect there is a problem with connection +// to voice server (EXT-4313). When voice works correctly, there is from 1 to 15 times. 50 was chosen  +// to make sure we don't make mistake when slight connection problems happen- situation when connection to server is  +// blocked is VERY rare and it's better to sacrifice response time in this situation for the sake of stability. +const int MAX_NORMAL_JOINING_SPATIAL_NUM = 50; + + +static void setUUIDFromStringHash(LLUUID &uuid, const std::string &str) +{ +	LLMD5 md5_uuid; +	md5_uuid.update((const unsigned char*)str.data(), str.size()); +	md5_uuid.finalize(); +	md5_uuid.raw_digest(uuid.mData); +} + +static int scale_mic_volume(float volume) +{ +	// incoming volume has the range [0.0 ... 2.0], with 1.0 as the default.                                                 +	// Map it to Vivox levels as follows: 0.0 -> 30, 1.0 -> 50, 2.0 -> 70                                                    +	return 30 + (int)(volume * 20.0f); +} + +static int scale_speaker_volume(float volume) +{ +	// incoming volume has the range [0.0 ... 1.0], with 0.5 as the default.                                                 +	// Map it to Vivox levels as follows: 0.0 -> 30, 0.5 -> 50, 1.0 -> 70                                                    +	return 30 + (int)(volume * 40.0f); +	 +} + +class LLVivoxVoiceAccountProvisionResponder : +	public LLHTTPClient::Responder +{ +public: +	LLVivoxVoiceAccountProvisionResponder(int retries) +	{ +		mRetries = retries; +	} + +	virtual void error(U32 status, const std::string& reason) +	{ +		if ( mRetries > 0 ) +		{ +			LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, retrying.  status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL; +			LLVivoxVoiceClient::getInstance()->requestVoiceAccountProvision( +				mRetries - 1); +		} +		else +		{ +			LL_WARNS("Voice") << "ProvisionVoiceAccountRequest returned an error, too many retries (giving up).  status = " << status << ", reason = \"" << reason << "\"" << LL_ENDL; +			LLVivoxVoiceClient::getInstance()->giveUp(); +		} +	} + +	virtual void result(const LLSD& content) +	{ + +		std::string voice_sip_uri_hostname; +		std::string voice_account_server_uri; +		 +		LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response:" << ll_pretty_print_sd(content) << LL_ENDL; +		 +		if(content.has("voice_sip_uri_hostname")) +			voice_sip_uri_hostname = content["voice_sip_uri_hostname"].asString(); +		 +		// this key is actually misnamed -- it will be an entire URI, not just a hostname. +		if(content.has("voice_account_server_name")) +			voice_account_server_uri = content["voice_account_server_name"].asString(); +		 +		LLVivoxVoiceClient::getInstance()->login( +			content["username"].asString(), +			content["password"].asString(), +			voice_sip_uri_hostname, +			voice_account_server_uri); + +	} + +private: +	int mRetries; +}; + + + +/////////////////////////////////////////////////////////////////////////////////////////////// + +class LLVivoxVoiceClientMuteListObserver : public LLMuteListObserver +{ +	/* virtual */ void onChange()  { LLVivoxVoiceClient::getInstance()->muteListChanged();} +}; + +class LLVivoxVoiceClientFriendsObserver : public LLFriendObserver +{ +public: +	/* virtual */ void changed(U32 mask) { LLVivoxVoiceClient::getInstance()->updateFriends(mask);} +}; + +static LLVivoxVoiceClientMuteListObserver mutelist_listener; +static bool sMuteListListener_listening = false; + +static LLVivoxVoiceClientFriendsObserver *friendslist_listener = NULL; + +/////////////////////////////////////////////////////////////////////////////////////////////// + +class LLVivoxVoiceClientCapResponder : public LLHTTPClient::Responder +{ +public: +	LLVivoxVoiceClientCapResponder(void){}; + +	virtual void error(U32 status, const std::string& reason);	// called with bad status codes +	virtual void result(const LLSD& content); + +private: +}; + +void LLVivoxVoiceClientCapResponder::error(U32 status, const std::string& reason) +{ +	LL_WARNS("Voice") << "LLVivoxVoiceClientCapResponder::error(" +		<< status << ": " << reason << ")" +		<< LL_ENDL; +} + +void LLVivoxVoiceClientCapResponder::result(const LLSD& content) +{ +	LLSD::map_const_iterator iter; +	 +	LL_DEBUGS("Voice") << "ParcelVoiceInfoRequest response:" << ll_pretty_print_sd(content) << LL_ENDL; + +	if ( content.has("voice_credentials") ) +	{ +		LLSD voice_credentials = content["voice_credentials"]; +		std::string uri; +		std::string credentials; + +		if ( voice_credentials.has("channel_uri") ) +		{ +			uri = voice_credentials["channel_uri"].asString(); +		} +		if ( voice_credentials.has("channel_credentials") ) +		{ +			credentials = +				voice_credentials["channel_credentials"].asString(); +		} + +		LLVivoxVoiceClient::getInstance()->setSpatialChannel(uri, credentials); +	} +} + + + +#if LL_WINDOWS +static HANDLE sGatewayHandle = 0; + +static bool isGatewayRunning() +{ +	bool result = false; +	if(sGatewayHandle != 0)		 +	{ +		DWORD waitresult = WaitForSingleObject(sGatewayHandle, 0); +		if(waitresult != WAIT_OBJECT_0) +		{ +			result = true; +		}			 +	} +	return result; +} +static void killGateway() +{ +	if(sGatewayHandle != 0) +	{ +		TerminateProcess(sGatewayHandle,0); +	} +} + +#else // Mac and linux + +static pid_t sGatewayPID = 0; +static bool isGatewayRunning() +{ +	bool result = false; +	if(sGatewayPID != 0) +	{ +		// A kill with signal number 0 has no effect, just does error checking.  It should return an error if the process no longer exists. +		if(kill(sGatewayPID, 0) == 0) +		{ +			result = true; +		} +	} +	return result; +} + +static void killGateway() +{ +	if(sGatewayPID != 0) +	{ +		kill(sGatewayPID, SIGTERM); +	} +} + +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////// + +LLVivoxVoiceClient::LLVivoxVoiceClient() : +	mState(stateDisabled), +	mSessionTerminateRequested(false), +	mRelogRequested(false), +	mConnected(false), +	mPump(NULL), +	mSpatialJoiningNum(0), + +	mTuningMode(false), +	mTuningEnergy(0.0f), +	mTuningMicVolume(0), +	mTuningMicVolumeDirty(true), +	mTuningSpeakerVolume(0), +	mTuningSpeakerVolumeDirty(true), +	mTuningExitState(stateDisabled), + +	mAreaVoiceDisabled(false), +	mAudioSession(NULL), +	mAudioSessionChanged(false), +	mNextAudioSession(NULL), + +	mCurrentParcelLocalID(0), +	mNumberOfAliases(0), +	mCommandCookie(0), +	mLoginRetryCount(0), + +	mBuddyListMapPopulated(false), +	mBlockRulesListReceived(false), +	mAutoAcceptRulesListReceived(false), +	mCaptureDeviceDirty(false), +	mRenderDeviceDirty(false), +	mSpatialCoordsDirty(false), + +	mPTTDirty(true), +	mPTT(true), +	mUsePTT(true), +	mPTTIsMiddleMouse(false), +	mPTTKey(0), +	mPTTIsToggle(false), +	mUserPTTState(false), +	mMuteMic(false), +	mFriendsListDirty(true), + +	mEarLocation(0), +	mSpeakerVolumeDirty(true), +	mSpeakerMuteDirty(true), +	mMicVolume(0), +	mMicVolumeDirty(true), + +	mVoiceEnabled(false), +	mWriteInProgress(false), + +	mLipSyncEnabled(false) + + + +{	 +	mSpeakerVolume = scale_speaker_volume(0); + +	mVoiceVersion.serverVersion = ""; +	mVoiceVersion.serverType = VOICE_SERVER_TYPE; +	 +	//  gMuteListp isn't set up at this point, so we defer this until later. +//	gMuteListp->addObserver(&mutelist_listener); +	 +	 +#if LL_DARWIN || LL_LINUX || LL_SOLARIS +		// HACK: THIS DOES NOT BELONG HERE +		// When the vivox daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us. +		// This should cause us to ignore SIGPIPE and handle the error through proper channels. +		// This should really be set up elsewhere.  Where should it go? +		signal(SIGPIPE, SIG_IGN); +		 +		// Since we're now launching the gateway with fork/exec instead of system(), we need to deal with zombie processes. +		// Ignoring SIGCHLD should prevent zombies from being created.  Alternately, we could use wait(), but I'd rather not do that. +		signal(SIGCHLD, SIG_IGN); +#endif + +	// set up state machine +	setState(stateDisabled); +	 +	gIdleCallbacks.addFunction(idle, this); +} + +//--------------------------------------------------- + +LLVivoxVoiceClient::~LLVivoxVoiceClient() +{ +} + +//---------------------------------------------- + +void LLVivoxVoiceClient::init(LLPumpIO *pump) +{ +	// constructor will set up LLVoiceClient::getInstance() +	LLVivoxVoiceClient::getInstance()->mPump = pump; +} + +void LLVivoxVoiceClient::terminate() +{ + +//	leaveAudioSession(); +	logout(); +	// As of SDK version 4885, this should no longer be necessary.  It will linger after the socket close if it needs to. +	// ms_sleep(2000); +	connectorShutdown(); +	closeSocket();		// Need to do this now -- bad things happen if the destructor does it later. +	 +	// This will do unpleasant things on windows. +//	killGateway(); +	 + + +} + +const LLVoiceVersionInfo& LLVivoxVoiceClient::getVersion() +{ +	return mVoiceVersion; +} + +//--------------------------------------------------- + +void LLVivoxVoiceClient::updateSettings() +{ +	setVoiceEnabled(gSavedSettings.getBOOL("EnableVoiceChat")); +	setUsePTT(gSavedSettings.getBOOL("PTTCurrentlyEnabled")); +	std::string keyString = gSavedSettings.getString("PushToTalkButton"); +	setPTTKey(keyString); +	setPTTIsToggle(gSavedSettings.getBOOL("PushToTalkToggle")); +	setEarLocation(gSavedSettings.getS32("VoiceEarLocation")); + +	std::string inputDevice = gSavedSettings.getString("VoiceInputAudioDevice"); +	setCaptureDevice(inputDevice); +	std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice"); +	setRenderDevice(outputDevice); +	F32 mic_level = gSavedSettings.getF32("AudioLevelMic"); +	setMicGain(mic_level); +	setLipSyncEnabled(gSavedSettings.getBOOL("LipSyncEnabled")); +} + +///////////////////////////// +// utility functions + +bool LLVivoxVoiceClient::writeString(const std::string &str) +{ +	bool result = false; +	if(mConnected) +	{ +		apr_status_t err; +		apr_size_t size = (apr_size_t)str.size(); +		apr_size_t written = size; +	 +		//MARK: Turn this on to log outgoing XML +//		LL_DEBUGS("Voice") << "sending: " << str << LL_ENDL; + +		// check return code - sockets will fail (broken, etc.) +		err = apr_socket_send( +				mSocket->getSocket(), +				(const char*)str.data(), +				&written); +		 +		if(err == 0) +		{ +			// Success. +			result = true; +		} +		// TODO: handle partial writes (written is number of bytes written) +		// Need to set socket to non-blocking before this will work. +//		else if(APR_STATUS_IS_EAGAIN(err)) +//		{ +//			//  +//		} +		else +		{ +			// Assume any socket error means something bad.  For now, just close the socket. +			char buf[MAX_STRING]; +			LL_WARNS("Voice") << "apr error " << err << " ("<< apr_strerror(err, buf, MAX_STRING) << ") sending data to vivox daemon." << LL_ENDL; +			daemonDied(); +		} +	} +		 +	return result; +} + + +///////////////////////////// +// session control messages +void LLVivoxVoiceClient::connectorCreate() +{ +	std::ostringstream stream; +	std::string logpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, ""); +	std::string loglevel = "0"; +	 +	// Transition to stateConnectorStarted when the connector handle comes back. +	setState(stateConnectorStarting); + +	std::string savedLogLevel = gSavedSettings.getString("VivoxDebugLevel"); +		 +	if(savedLogLevel != "-1") +	{ +		LL_DEBUGS("Voice") << "creating connector with logging enabled" << LL_ENDL; +		loglevel = "10"; +	} +	 +	stream  +	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.Create.1\">" +		<< "<ClientName>V2 SDK</ClientName>" +		<< "<AccountManagementServer>" << mVoiceAccountServerURI << "</AccountManagementServer>" +		<< "<Mode>Normal</Mode>" +		<< "<Logging>" +			<< "<Folder>" << logpath << "</Folder>" +			<< "<FileNamePrefix>Connector</FileNamePrefix>" +			<< "<FileNameSuffix>.log</FileNameSuffix>" +			<< "<LogLevel>" << loglevel << "</LogLevel>" +		<< "</Logging>" +		<< "<Application>SecondLifeViewer.1</Application>" +	<< "</Request>\n\n\n"; +	 +	writeString(stream.str()); +} + +void LLVivoxVoiceClient::connectorShutdown() +{ +	setState(stateConnectorStopping); +	 +	if(!mConnectorHandle.empty()) +	{ +		std::ostringstream stream; +		stream +		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.InitiateShutdown.1\">" +			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" +		<< "</Request>" +		<< "\n\n\n"; +		 +		mConnectorHandle.clear(); +		 +		writeString(stream.str()); +	} +} + +void LLVivoxVoiceClient::userAuthorized(const std::string& user_id, const LLUUID &agentID) +{ + +	mAccountDisplayName = user_id; + +	LL_INFOS("Voice") << "name \"" << mAccountDisplayName << "\" , ID " << agentID << LL_ENDL; + +	mAccountName = nameFromID(agentID); +} + +void LLVivoxVoiceClient::requestVoiceAccountProvision(S32 retries) +{ +	if ( gAgent.getRegion() && mVoiceEnabled ) +	{ +		std::string url =  +			gAgent.getRegion()->getCapability( +				"ProvisionVoiceAccountRequest"); + +		if ( url == "" ) return; + +		LLHTTPClient::post( +			url, +			LLSD(), +			new LLVivoxVoiceAccountProvisionResponder(retries)); +	} +} + +void LLVivoxVoiceClient::login( +	const std::string& account_name, +	const std::string& password, +	const std::string& voice_sip_uri_hostname, +	const std::string& voice_account_server_uri) +{ +	mVoiceSIPURIHostName = voice_sip_uri_hostname; +	mVoiceAccountServerURI = voice_account_server_uri; + +	if(!mAccountHandle.empty()) +	{ +		// Already logged in. +		LL_WARNS("Voice") << "Called while already logged in." << LL_ENDL; +		 +		// Don't process another login. +		return; +	} +	else if ( account_name != mAccountName ) +	{ +		//TODO: error? +		LL_WARNS("Voice") << "Wrong account name! " << account_name +				<< " instead of " << mAccountName << LL_ENDL; +	} +	else +	{ +		mAccountPassword = password; +	} + +	std::string debugSIPURIHostName = gSavedSettings.getString("VivoxDebugSIPURIHostName"); +	 +	if( !debugSIPURIHostName.empty() ) +	{ +		mVoiceSIPURIHostName = debugSIPURIHostName; +	} +	 +	if( mVoiceSIPURIHostName.empty() ) +	{ +		// we have an empty account server name +		// so we fall back to hardcoded defaults + +		if(LLGridManager::getInstance()->isInProductionGrid()) +		{ +			// Use the release account server +			mVoiceSIPURIHostName = "bhr.vivox.com"; +		} +		else +		{ +			// Use the development account server +			mVoiceSIPURIHostName = "bhd.vivox.com"; +		} +	} +	 +	std::string debugAccountServerURI = gSavedSettings.getString("VivoxDebugVoiceAccountServerURI"); + +	if( !debugAccountServerURI.empty() ) +	{ +		mVoiceAccountServerURI = debugAccountServerURI; +	} +	 +	if( mVoiceAccountServerURI.empty() ) +	{ +		// If the account server URI isn't specified, construct it from the SIP URI hostname +		mVoiceAccountServerURI = "https://www." + mVoiceSIPURIHostName + "/api2/";		 +	} +} + +void LLVivoxVoiceClient::idle(void* user_data) +{ +	LLVivoxVoiceClient* self = (LLVivoxVoiceClient*)user_data; +	self->stateMachine(); +} + +std::string LLVivoxVoiceClient::state2string(LLVivoxVoiceClient::state inState) +{ +	std::string result = "UNKNOWN"; +	 +		// Prevent copy-paste errors when updating this list... +#define CASE(x)  case x:  result = #x;  break + +	switch(inState) +	{ +		CASE(stateDisableCleanup); +		CASE(stateDisabled); +		CASE(stateStart); +		CASE(stateDaemonLaunched); +		CASE(stateConnecting); +		CASE(stateConnected); +		CASE(stateIdle); +		CASE(stateMicTuningStart); +		CASE(stateMicTuningRunning); +		CASE(stateMicTuningStop); +		CASE(stateConnectorStart); +		CASE(stateConnectorStarting); +		CASE(stateConnectorStarted); +		CASE(stateLoginRetry); +		CASE(stateLoginRetryWait); +		CASE(stateNeedsLogin); +		CASE(stateLoggingIn); +		CASE(stateLoggedIn); +		CASE(stateCreatingSessionGroup); +		CASE(stateNoChannel); +		CASE(stateJoiningSession); +		CASE(stateSessionJoined); +		CASE(stateRunning); +		CASE(stateLeavingSession); +		CASE(stateSessionTerminated); +		CASE(stateLoggingOut); +		CASE(stateLoggedOut); +		CASE(stateConnectorStopping); +		CASE(stateConnectorStopped); +		CASE(stateConnectorFailed); +		CASE(stateConnectorFailedWaiting); +		CASE(stateLoginFailed); +		CASE(stateLoginFailedWaiting); +		CASE(stateJoinSessionFailed); +		CASE(stateJoinSessionFailedWaiting); +		CASE(stateJail); +	} + +#undef CASE +	 +	return result; +} + + + +void LLVivoxVoiceClient::setState(state inState) +{ +	LL_DEBUGS("Voice") << "entering state " << state2string(inState) << LL_ENDL; +	 +	mState = inState; +} + +void LLVivoxVoiceClient::stateMachine() +{ +	if(gDisconnected) +	{ +		// The viewer has been disconnected from the sim.  Disable voice. +		setVoiceEnabled(false); +	} +	 +	if(mVoiceEnabled) +	{ +		updatePosition(); +	} +	else if(mTuningMode) +	{ +		// Tuning mode is special -- it needs to launch SLVoice even if voice is disabled. +	} +	else +	{ +		if((getState() != stateDisabled) && (getState() != stateDisableCleanup)) +		{ +			// User turned off voice support.  Send the cleanup messages, close the socket, and reset. +			if(!mConnected) +			{ +				// if voice was turned off after the daemon was launched but before we could connect to it, we may need to issue a kill. +				LL_INFOS("Voice") << "Disabling voice before connection to daemon, terminating." << LL_ENDL; +				killGateway(); +			} +			 +			logout(); +			connectorShutdown(); +			 +			setState(stateDisableCleanup); +		} +	} +	 +	// Check for parcel boundary crossing +	{ +		LLViewerRegion *region = gAgent.getRegion(); +		LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); +		 +		if(region && parcel) +		{ +			S32 parcelLocalID = parcel->getLocalID(); +			std::string regionName = region->getName(); +			std::string capURI = region->getCapability("ParcelVoiceInfoRequest"); +		 +//			LL_DEBUGS("Voice") << "Region name = \"" << regionName << "\", parcel local ID = " << parcelLocalID << ", cap URI = \"" << capURI << "\"" << LL_ENDL; + +			// The region name starts out empty and gets filled in later.   +			// Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes. +			// If either is empty, wait for the next time around. +			if(!regionName.empty()) +			{ +				if(!capURI.empty()) +				{ +					if((parcelLocalID != mCurrentParcelLocalID) || (regionName != mCurrentRegionName)) +					{ +						// We have changed parcels.  Initiate a parcel channel lookup. +						mCurrentParcelLocalID = parcelLocalID; +						mCurrentRegionName = regionName; +						 +						parcelChanged(); +					} +				} +				else +				{ +					LL_WARNS_ONCE("Voice") << "region doesn't have ParcelVoiceInfoRequest capability.  This is normal for a short time after teleporting, but bad if it persists for very long." << LL_ENDL; +				} +			} +		} +	} + +	switch(getState()) +	{ +		//MARK: stateDisableCleanup +		case stateDisableCleanup: +			// Clean up and reset everything.  +			closeSocket(); +			deleteAllSessions(); +			deleteAllBuddies();		 +			 +			mConnectorHandle.clear(); +			mAccountHandle.clear(); +			mAccountPassword.clear(); +			mVoiceAccountServerURI.clear(); +			 +			setState(stateDisabled);	 +		break; +		 +		//MARK: stateDisabled +		case stateDisabled: +			if(mTuningMode || (mVoiceEnabled && !mAccountName.empty())) +			{ +				setState(stateStart); +			} +		break; +		 +		//MARK: stateStart +		case stateStart: +			if(gSavedSettings.getBOOL("CmdLineDisableVoice")) +			{ +				// Voice is locked out, we must not launch the vivox daemon. +				setState(stateJail); +			} +			else if(!isGatewayRunning()) +			{ +				if(true) +				{ +					// Launch the voice daemon +					 +					// *FIX:Mani - Using the executable dir instead  +					// of mAppRODataDir, the working directory from which the app +					// is launched. +					//std::string exe_path = gDirUtilp->getAppRODataDir(); +					std::string exe_path = gDirUtilp->getExecutableDir(); +					exe_path += gDirUtilp->getDirDelimiter(); +#if LL_WINDOWS +					exe_path += "SLVoice.exe"; +#elif LL_DARWIN +					exe_path += "../Resources/SLVoice"; +#else +					exe_path += "SLVoice"; +#endif +					// See if the vivox executable exists +					llstat s; +					if(!LLFile::stat(exe_path, &s)) +					{ +						// vivox executable exists.  Build the command line and launch the daemon. +						// SLIM SDK: these arguments are no longer necessary. +//						std::string args = " -p tcp -h -c"; +						std::string args; +						std::string cmd; +						std::string loglevel = gSavedSettings.getString("VivoxDebugLevel"); +						 +						if(loglevel.empty()) +						{ +							loglevel = "-1";	// turn logging off completely +						} +						 +						args += " -ll "; +						args += loglevel; +						 +						LL_DEBUGS("Voice") << "Args for SLVoice: " << args << LL_ENDL; + +#if LL_WINDOWS +						PROCESS_INFORMATION pinfo; +						STARTUPINFOA sinfo; +						 +						memset(&sinfo, 0, sizeof(sinfo)); +						 +						std::string exe_dir = gDirUtilp->getAppRODataDir(); +						cmd = "SLVoice.exe"; +						cmd += args; + +						// So retarded.  Windows requires that the second parameter to CreateProcessA be writable (non-const) string... +						char *args2 = new char[args.size() + 1]; +						strcpy(args2, args.c_str()); +						if(!CreateProcessA(exe_path.c_str(), args2, NULL, NULL, FALSE, 0, NULL, exe_dir.c_str(), &sinfo, &pinfo)) +						{ +//							DWORD dwErr = GetLastError(); +						} +						else +						{ +							// foo = pinfo.dwProcessId; // get your pid here if you want to use it later on +							// CloseHandle(pinfo.hProcess); // stops leaks - nothing else +							sGatewayHandle = pinfo.hProcess; +							CloseHandle(pinfo.hThread); // stops leaks - nothing else +						}		 +						 +						delete[] args2; +#else	// LL_WINDOWS +						// This should be the same for mac and linux +						{ +							std::vector<std::string> arglist; +							arglist.push_back(exe_path); +							 +							// Split the argument string into separate strings for each argument +							typedef boost::tokenizer<boost::char_separator<char> > tokenizer; +							boost::char_separator<char> sep(" "); +							tokenizer tokens(args, sep); +							tokenizer::iterator token_iter; + +							for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) +							{ +								arglist.push_back(*token_iter); +							} +							 +							// create an argv vector for the child process +							char **fakeargv = new char*[arglist.size() + 1]; +							int i; +							for(i=0; i < arglist.size(); i++) +								fakeargv[i] = const_cast<char*>(arglist[i].c_str()); + +							fakeargv[i] = NULL; +							 +							fflush(NULL); // flush all buffers before the child inherits them +							pid_t id = vfork(); +							if(id == 0) +							{ +								// child +								execv(exe_path.c_str(), fakeargv); +								 +								// If we reach this point, the exec failed. +								// Use _exit() instead of exit() per the vfork man page. +								_exit(0); +							} + +							// parent +							delete[] fakeargv; +							sGatewayPID = id; +						} +#endif	// LL_WINDOWS +						mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost").c_str(), gSavedSettings.getU32("VivoxVoicePort")); +					}	 +					else +					{ +						LL_INFOS("Voice") << exe_path << " not found." << LL_ENDL; +					}	 +				} +				else +				{		 +					// SLIM SDK: port changed from 44124 to 44125. +					// We can connect to a client gateway running on another host.  This is useful for testing. +					// To do this, launch the gateway on a nearby host like this: +					//  vivox-gw.exe -p tcp -i 0.0.0.0:44125 +					// and put that host's IP address here. +					mDaemonHost = LLHost(gSavedSettings.getString("VivoxVoiceHost"), gSavedSettings.getU32("VivoxVoicePort")); +				} + +				mUpdateTimer.start(); +				mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS); + +				setState(stateDaemonLaunched); +				 +				// Dirty the states we'll need to sync with the daemon when it comes up. +				mPTTDirty = true; +				mMicVolumeDirty = true; +				mSpeakerVolumeDirty = true; +				mSpeakerMuteDirty = true; +				// These only need to be set if they're not default (i.e. empty string). +				mCaptureDeviceDirty = !mCaptureDevice.empty(); +				mRenderDeviceDirty = !mRenderDevice.empty(); +				 +				mMainSessionGroupHandle.clear(); +			} +		break; + +		//MARK: stateDaemonLaunched +		case stateDaemonLaunched: +			if(mUpdateTimer.hasExpired()) +			{ +				LL_DEBUGS("Voice") << "Connecting to vivox daemon:" << mDaemonHost << LL_ENDL; + +				mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS); + +				if(!mSocket) +				{ +					mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);	 +				} +				 +				mConnected = mSocket->blockingConnect(mDaemonHost); +				if(mConnected) +				{ +					setState(stateConnecting); +				} +				else +				{ +					// If the connect failed, the socket may have been put into a bad state.  Delete it. +					closeSocket(); +				} +			} +		break; + +		//MARK: stateConnecting +		case stateConnecting: +		// Can't do this until we have the pump available. +		if(mPump) +		{ +			// MBW -- Note to self: pumps and pipes examples in +			//  indra/test/io.cpp +			//  indra/test/llpipeutil.{cpp|h} + +			// Attach the pumps and pipes +				 +			LLPumpIO::chain_t readChain; + +			readChain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket))); +			readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser())); + +			mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS); + +			setState(stateConnected); +		} + +		break; +		 +		//MARK: stateConnected +		case stateConnected: +			// Initial devices query +			getCaptureDevicesSendMessage(); +			getRenderDevicesSendMessage(); + +			mLoginRetryCount = 0; + +			setState(stateIdle); +		break; + +		//MARK: stateIdle +		case stateIdle: +			// This is the idle state where we're connected to the daemon but haven't set up a connector yet. +			if(mTuningMode) +			{ +				mTuningExitState = stateIdle; +				setState(stateMicTuningStart); +			} +			else if(!mVoiceEnabled) +			{ +				// We never started up the connector.  This will shut down the daemon. +				setState(stateConnectorStopped); +			} +			else if(!mAccountName.empty()) +			{ +				LLViewerRegion *region = gAgent.getRegion(); +				 +				if(region) +				{ +					if ( region->getCapability("ProvisionVoiceAccountRequest") != "" ) +					{ +						if ( mAccountPassword.empty() ) +						{ +							requestVoiceAccountProvision(); +						} +						setState(stateConnectorStart); +					} +					else +					{ +						LL_WARNS_ONCE("Voice") << "region doesn't have ProvisionVoiceAccountRequest capability!" << LL_ENDL; +					} +				} +			} +		break; + +		//MARK: stateMicTuningStart +		case stateMicTuningStart: +			if(mUpdateTimer.hasExpired()) +			{ +				if(mCaptureDeviceDirty || mRenderDeviceDirty) +				{ +					// These can't be changed while in tuning mode.  Set them before starting. +					std::ostringstream stream; +					 +					buildSetCaptureDevice(stream); +					buildSetRenderDevice(stream); + +					if(!stream.str().empty()) +					{ +						writeString(stream.str()); +					}				 + +					// This will come around again in the same state and start the capture, after the timer expires. +					mUpdateTimer.start(); +					mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); +				} +				else +				{ +					// duration parameter is currently unused, per Mike S. +					tuningCaptureStartSendMessage(10000); + +					setState(stateMicTuningRunning); +				} +			} +			 +		break; +		 +		//MARK: stateMicTuningRunning +		case stateMicTuningRunning: +			if(!mTuningMode || mCaptureDeviceDirty || mRenderDeviceDirty) +			{ +				// All of these conditions make us leave tuning mode. +				setState(stateMicTuningStop); +			} +			else +			{ +				// process mic/speaker volume changes +				if(mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty) +				{ +					std::ostringstream stream; +					 +					if(mTuningMicVolumeDirty) +					{ +						LL_INFOS("Voice") << "setting tuning mic level to " << mTuningMicVolume << LL_ENDL; +						stream +						<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetMicLevel.1\">" +						<< "<Level>" << mTuningMicVolume << "</Level>" +						<< "</Request>\n\n\n"; +					} +					 +					if(mTuningSpeakerVolumeDirty) +					{ +						stream +						<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetSpeakerLevel.1\">" +						<< "<Level>" << mTuningSpeakerVolume << "</Level>" +						<< "</Request>\n\n\n"; +					} +					 +					mTuningMicVolumeDirty = false; +					mTuningSpeakerVolumeDirty = false; + +					if(!stream.str().empty()) +					{ +						writeString(stream.str()); +					} +				} +			} +		break; +		 +		//MARK: stateMicTuningStop +		case stateMicTuningStop: +		{ +			// transition out of mic tuning +			tuningCaptureStopSendMessage(); +			 +			setState(mTuningExitState); +			 +			// if we exited just to change devices, this will keep us from re-entering too fast. +			mUpdateTimer.start(); +			mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); +			 +		} +		break; +												 +		//MARK: stateConnectorStart +		case stateConnectorStart: +			if(!mVoiceEnabled) +			{ +				// We were never logged in.  This will shut down the connector. +				setState(stateLoggedOut); +			} +			else if(!mVoiceAccountServerURI.empty()) +			{ +				connectorCreate(); +			} +		break; +		 +		//MARK: stateConnectorStarting +		case stateConnectorStarting:	// waiting for connector handle +			// connectorCreateResponse() will transition from here to stateConnectorStarted. +		break; +		 +		//MARK: stateConnectorStarted +		case stateConnectorStarted:		// connector handle received +			if(!mVoiceEnabled) +			{ +				// We were never logged in.  This will shut down the connector. +				setState(stateLoggedOut); +			} +			else +			{ +				// The connector is started.  Send a login message. +				setState(stateNeedsLogin); +			} +		break; +				 +		//MARK: stateLoginRetry +		case stateLoginRetry: +			if(mLoginRetryCount == 0) +			{ +				// First retry -- display a message to the user +				notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY); +			} +			 +			mLoginRetryCount++; +			 +			if(mLoginRetryCount > MAX_LOGIN_RETRIES) +			{ +				LL_WARNS("Voice") << "too many login retries, giving up." << LL_ENDL; +				setState(stateLoginFailed); +				LLSD args; +				std::stringstream errs; +				errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000"; +				args["HOSTID"] = errs.str(); +				if (LLGridManager::getInstance()->isSystemGrid()) +				{ +					LLNotificationsUtil::add("NoVoiceConnect", args);	 +				} +				else +				{ +					LLNotificationsUtil::add("NoVoiceConnect-GIAB", args);	 +				}				 +			} +			else +			{ +				LL_INFOS("Voice") << "will retry login in " << LOGIN_RETRY_SECONDS << " seconds." << LL_ENDL; +				mUpdateTimer.start(); +				mUpdateTimer.setTimerExpirySec(LOGIN_RETRY_SECONDS); +				setState(stateLoginRetryWait); +			} +		break; +		 +		//MARK: stateLoginRetryWait +		case stateLoginRetryWait: +			if(mUpdateTimer.hasExpired()) +			{ +				setState(stateNeedsLogin); +			} +		break; +		 +		//MARK: stateNeedsLogin +		case stateNeedsLogin: +			if(!mAccountPassword.empty()) +			{ +				setState(stateLoggingIn); +				loginSendMessage(); +			}		 +		break; +		 +		//MARK: stateLoggingIn +		case stateLoggingIn:			// waiting for account handle +			// loginResponse() will transition from here to stateLoggedIn. +		break; +		 +		//MARK: stateLoggedIn +		case stateLoggedIn:				// account handle received + +			notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN); + +			// request the current set of block rules (we'll need them when updating the friends list) +			accountListBlockRulesSendMessage(); +			 +			// request the current set of auto-accept rules +			accountListAutoAcceptRulesSendMessage(); +			 +			// Set up the mute list observer if it hasn't been set up already. +			if((!sMuteListListener_listening)) +			{ +				LLMuteList::getInstance()->addObserver(&mutelist_listener); +				sMuteListListener_listening = true; +			} + +			// Set up the friends list observer if it hasn't been set up already. +			if(friendslist_listener == NULL) +			{ +				friendslist_listener = new LLVivoxVoiceClientFriendsObserver; +				LLAvatarTracker::instance().addObserver(friendslist_listener); +			} +			 +			// Set the initial state of mic mute, local speaker volume, etc. +			{ +				std::ostringstream stream; +				 +				buildLocalAudioUpdates(stream); +				 +				if(!stream.str().empty()) +				{ +					writeString(stream.str()); +				} +			} +			 +#if USE_SESSION_GROUPS			 +			// create the main session group +			sessionGroupCreateSendMessage(); +			 +			setState(stateCreatingSessionGroup); +#else +			// Not using session groups -- skip the stateCreatingSessionGroup state. +			setState(stateNoChannel); + +			// Initial kick-off of channel lookup logic +			parcelChanged();		 +#endif +		break; +		 +		//MARK: stateCreatingSessionGroup +		case stateCreatingSessionGroup: +			if(mSessionTerminateRequested || !mVoiceEnabled) +			{ +				// *TODO: Question: is this the right way out of this state +				setState(stateSessionTerminated); +			} +			else if(!mMainSessionGroupHandle.empty()) +			{ +				setState(stateNoChannel); +				 +				// Start looped recording (needed for "panic button" anti-griefing tool) +				recordingLoopStart(); + +				// Initial kick-off of channel lookup logic +				parcelChanged();		 +			} +		break; +					 +		//MARK: stateNoChannel +		case stateNoChannel: +			 +			LL_DEBUGS("Voice") << "State No Channel" << LL_ENDL; +			mSpatialJoiningNum = 0; +			// Do this here as well as inside sendPositionalUpdate().   +			// Otherwise, if you log in but don't join a proximal channel (such as when your login location has voice disabled), your friends list won't sync. +			sendFriendsListUpdates(); +			 +			if(mSessionTerminateRequested || !mVoiceEnabled) +			{ +				// TODO: Question: Is this the right way out of this state? +				setState(stateSessionTerminated); +			} +			else if(mTuningMode) +			{ +				mTuningExitState = stateNoChannel; +				setState(stateMicTuningStart); +			} +			else if(sessionNeedsRelog(mNextAudioSession)) +			{ +				requestRelog(); +				setState(stateSessionTerminated); +			} +			else if(mNextAudioSession) +			{				 +				sessionState *oldSession = mAudioSession; + +				mAudioSession = mNextAudioSession; +				if(!mAudioSession->mReconnect)	 +				{ +					mNextAudioSession = NULL; +				} +				 +				// The old session may now need to be deleted. +				reapSession(oldSession); +				 +				if(!mAudioSession->mHandle.empty()) +				{ +					// Connect to a session by session handle + +					sessionMediaConnectSendMessage(mAudioSession); +				} +				else +				{ +					// Connect to a session by URI +					sessionCreateSendMessage(mAudioSession, true, false); +				} + +				notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING); +				setState(stateJoiningSession); +			} +			else if(!mSpatialSessionURI.empty()) +			{ +				// If we're not headed elsewhere and have a spatial URI, return to spatial. +				switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials); +			} +		break; + +		//MARK: stateJoiningSession +		case stateJoiningSession:		// waiting for session handle +		   +		  // If this is true we have problem with connection to voice server (EXT-4313). +		  // See descriptions of mSpatialJoiningNum and MAX_NORMAL_JOINING_SPATIAL_NUM. +		  if(mSpatialJoiningNum == MAX_NORMAL_JOINING_SPATIAL_NUM)  +		    { +		      // Notify observers to let them know there is problem with voice +		      notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); +		      llwarns << "There seems to be problem with connection to voice server. Disabling voice chat abilities." << llendl; +		    } +		   +		  // Increase mSpatialJoiningNum only for spatial sessions- it's normal to reach this case for +		  // example for p2p many times while waiting for response, so it can't be used to detect errors +		  if(mAudioSession && mAudioSession->mIsSpatial) +		    { +		      mSpatialJoiningNum++; +		    } +       +			// joinedAudioSession() will transition from here to stateSessionJoined. +			if(!mVoiceEnabled) +			{ +				// User bailed out during connect -- jump straight to teardown. +				setState(stateSessionTerminated); +			} +			else if(mSessionTerminateRequested) +			{ +				if(mAudioSession && !mAudioSession->mHandle.empty()) +				{ +					// Only allow direct exits from this state in p2p calls (for cancelling an invite). +					// Terminating a half-connected session on other types of calls seems to break something in the vivox gateway. +					if(mAudioSession->mIsP2P) +					{ +						sessionMediaDisconnectSendMessage(mAudioSession); +						setState(stateSessionTerminated); +					} +				} +			} +		break; +		 +		//MARK: stateSessionJoined +		case stateSessionJoined:		// session handle received + +		  mSpatialJoiningNum = 0; +			// It appears that I need to wait for BOTH the SessionGroup.AddSession response and the SessionStateChangeEvent with state 4 +			// before continuing from this state.  They can happen in either order, and if I don't wait for both, things can get stuck. +			// For now, the SessionGroup.AddSession response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined. +			// This is a cheap way to make sure both have happened before proceeding. +			if(mAudioSession && mAudioSession->mVoiceEnabled) +			{ +				// Dirty state that may need to be sync'ed with the daemon. +				mPTTDirty = true; +				mSpeakerVolumeDirty = true; +				mSpatialCoordsDirty = true; +				 +				setState(stateRunning); +				 +				// Start the throttle timer +				mUpdateTimer.start(); +				mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); + +				// Events that need to happen when a session is joined could go here. +				// Maybe send initial spatial data? +				notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED); + +			} +			else if(!mVoiceEnabled) +			{ +				// User bailed out during connect -- jump straight to teardown. +				setState(stateSessionTerminated); +			} +			else if(mSessionTerminateRequested) +			{ +				// Only allow direct exits from this state in p2p calls (for cancelling an invite). +				// Terminating a half-connected session on other types of calls seems to break something in the vivox gateway. +				if(mAudioSession && mAudioSession->mIsP2P) +				{ +					sessionMediaDisconnectSendMessage(mAudioSession); +					setState(stateSessionTerminated); +				} +			} +		break; +		 +		//MARK: stateRunning +		case stateRunning:				// steady state +			// Disabling voice or disconnect requested. +			if(!mVoiceEnabled || mSessionTerminateRequested) +			{ +				leaveAudioSession(); +			} +			else +			{ +				 +				// Figure out whether the PTT state needs to change +				{ +					bool newPTT; +					if(mUsePTT) +					{ +						// If configured to use PTT, track the user state. +						newPTT = mUserPTTState; +					} +					else +					{ +						// If not configured to use PTT, it should always be true (otherwise the user will be unable to speak). +						newPTT = true; +					} +					 +					if(mMuteMic) +					{ +						// This always overrides any other PTT setting. +						newPTT = false; +					} +					 +					// Dirty if state changed. +					if(newPTT != mPTT) +					{ +						mPTT = newPTT; +						mPTTDirty = true; +					} +				} +				 +				if(!inSpatialChannel()) +				{ +					// When in a non-spatial channel, never send positional updates. +					mSpatialCoordsDirty = false; +				} +				else +				{ +					// Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position) +					enforceTether(); +				} +				 +				// Send an update if the ptt state has changed (which shouldn't be able to happen that often -- the user can only click so fast) +				// or every 10hz, whichever is sooner. +				if((mAudioSession && mAudioSession->mVolumeDirty) || mPTTDirty || mSpeakerVolumeDirty || mUpdateTimer.hasExpired()) +				{ +					mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS); +					sendPositionalUpdate(); +				} +			} +		break; +		 +		//MARK: stateLeavingSession +		case stateLeavingSession:		// waiting for terminate session response +			// The handler for the Session.Terminate response will transition from here to stateSessionTerminated. +		break; + +		//MARK: stateSessionTerminated +		case stateSessionTerminated: +			 +			// Must do this first, since it uses mAudioSession. +			notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL); +			 +			if(mAudioSession) +			{ +				sessionState *oldSession = mAudioSession; + +				mAudioSession = NULL; +				// We just notified status observers about this change.  Don't do it again. +				mAudioSessionChanged = false; + +				// The old session may now need to be deleted. +				reapSession(oldSession); +			} +			else +			{ +				LL_WARNS("Voice") << "stateSessionTerminated with NULL mAudioSession" << LL_ENDL; +			} +	 +			// Always reset the terminate request flag when we get here. +			mSessionTerminateRequested = false; + +			if(mVoiceEnabled && !mRelogRequested) +			{				 +				// Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state). +				setState(stateNoChannel); +			} +			else +			{ +				// Shutting down voice, continue with disconnecting. +				logout(); +				 +				// The state machine will take it from here +				mRelogRequested = false; +			} +			 +		break; +		 +		//MARK: stateLoggingOut +		case stateLoggingOut:			// waiting for logout response +			// The handler for the AccountLoginStateChangeEvent will transition from here to stateLoggedOut. +		break; +		 +		//MARK: stateLoggedOut +		case stateLoggedOut:			// logout response received +			 +			// Once we're logged out, all these things are invalid. +			mAccountHandle.clear(); +			deleteAllSessions(); +			deleteAllBuddies(); + +			if(mVoiceEnabled && !mRelogRequested) +			{ +				// User was logged out, but wants to be logged in.  Send a new login request. +				setState(stateNeedsLogin); +			} +			else +			{ +				// shut down the connector +				connectorShutdown(); +			} +		break; +		 +		//MARK: stateConnectorStopping +		case stateConnectorStopping:	// waiting for connector stop +			// The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped. +		break; + +		//MARK: stateConnectorStopped +		case stateConnectorStopped:		// connector stop received +			setState(stateDisableCleanup); +		break; + +		//MARK: stateConnectorFailed +		case stateConnectorFailed: +			setState(stateConnectorFailedWaiting); +		break; +		//MARK: stateConnectorFailedWaiting +		case stateConnectorFailedWaiting: +			if(!mVoiceEnabled) +			{ +				setState(stateDisableCleanup); +			} +		break; + +		//MARK: stateLoginFailed +		case stateLoginFailed: +			setState(stateLoginFailedWaiting); +		break; +		//MARK: stateLoginFailedWaiting +		case stateLoginFailedWaiting: +			if(!mVoiceEnabled) +			{ +				setState(stateDisableCleanup); +			} +		break; + +		//MARK: stateJoinSessionFailed +		case stateJoinSessionFailed: +			// Transition to error state.  Send out any notifications here. +			if(mAudioSession) +			{ +				LL_WARNS("Voice") << "stateJoinSessionFailed: (" << mAudioSession->mErrorStatusCode << "): " << mAudioSession->mErrorStatusString << LL_ENDL; +			} +			else +			{ +				LL_WARNS("Voice") << "stateJoinSessionFailed with no current session" << LL_ENDL; +			} +			 +			notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN); +			setState(stateJoinSessionFailedWaiting); +		break; +		 +		//MARK: stateJoinSessionFailedWaiting +		case stateJoinSessionFailedWaiting: +			// Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message. +			// Region crossings may leave this state and try the join again. +			if(mSessionTerminateRequested) +			{ +				setState(stateSessionTerminated); +			} +		break; +		 +		//MARK: stateJail +		case stateJail: +			// We have given up.  Do nothing. +		break; + +	} +	 +	if(mAudioSession && mAudioSession->mParticipantsChanged) +	{ +		mAudioSession->mParticipantsChanged = false; +		mAudioSessionChanged = true; +	} +	 +	if(mAudioSessionChanged) +	{ +		mAudioSessionChanged = false; +		notifyParticipantObservers(); +	} +} + +void LLVivoxVoiceClient::closeSocket(void) +{ +	mSocket.reset(); +	mConnected = false;	 +} + +void LLVivoxVoiceClient::loginSendMessage() +{ +	std::ostringstream stream; + +	bool autoPostCrashDumps = gSavedSettings.getBOOL("VivoxAutoPostCrashDumps"); + +	stream +	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.Login.1\">" +		<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" +		<< "<AccountName>" << mAccountName << "</AccountName>" +		<< "<AccountPassword>" << mAccountPassword << "</AccountPassword>" +		<< "<AudioSessionAnswerMode>VerifyAnswer</AudioSessionAnswerMode>" +		<< "<EnableBuddiesAndPresence>true</EnableBuddiesAndPresence>" +		<< "<BuddyManagementMode>Application</BuddyManagementMode>" +		<< "<ParticipantPropertyFrequency>5</ParticipantPropertyFrequency>" +		<< (autoPostCrashDumps?"<AutopostCrashDumps>true</AutopostCrashDumps>":"") +	<< "</Request>\n\n\n"; +	 +	writeString(stream.str()); +} + +void LLVivoxVoiceClient::logout() +{ +	// Ensure that we'll re-request provisioning before logging in again +	mAccountPassword.clear(); +	mVoiceAccountServerURI.clear(); +	 +	setState(stateLoggingOut); +	logoutSendMessage(); +} + +void LLVivoxVoiceClient::logoutSendMessage() +{ +	if(!mAccountHandle.empty()) +	{ +		std::ostringstream stream; +		stream +		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.Logout.1\">" +			<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" +		<< "</Request>" +		<< "\n\n\n"; + +		mAccountHandle.clear(); + +		writeString(stream.str()); +	} +} + +void LLVivoxVoiceClient::accountListBlockRulesSendMessage() +{ +	if(!mAccountHandle.empty()) +	{		 +		std::ostringstream stream; + +		LL_DEBUGS("Voice") << "requesting block rules" << LL_ENDL; + +		stream +		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ListBlockRules.1\">" +			<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" +		<< "</Request>" +		<< "\n\n\n"; + +		writeString(stream.str()); +	} +} + +void LLVivoxVoiceClient::accountListAutoAcceptRulesSendMessage() +{ +	if(!mAccountHandle.empty()) +	{		 +		std::ostringstream stream; + +		LL_DEBUGS("Voice") << "requesting auto-accept rules" << LL_ENDL; + +		stream +		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ListAutoAcceptRules.1\">" +			<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" +		<< "</Request>" +		<< "\n\n\n"; + +		writeString(stream.str()); +	} +} + +void LLVivoxVoiceClient::sessionGroupCreateSendMessage() +{ +	if(!mAccountHandle.empty()) +	{		 +		std::ostringstream stream; + +		LL_DEBUGS("Voice") << "creating session group" << LL_ENDL; + +		stream +		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.Create.1\">" +			<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" +			<< "<Type>Normal</Type>" +		<< "</Request>" +		<< "\n\n\n"; + +		writeString(stream.str()); +	} +} + +void LLVivoxVoiceClient::sessionCreateSendMessage(sessionState *session, bool startAudio, bool startText) +{ +	LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL; +	 +	session->mCreateInProgress = true; +	if(startAudio) +	{ +		session->mMediaConnectInProgress = true; +	} + +	std::ostringstream stream; +	stream +	<< "<Request requestId=\"" << session->mSIPURI << "\" action=\"Session.Create.1\">" +		<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" +		<< "<URI>" << session->mSIPURI << "</URI>"; + +	static const std::string allowed_chars = +				"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +				"0123456789" +				"-._~"; + +	if(!session->mHash.empty()) +	{ +		stream +			<< "<Password>" << LLURI::escape(session->mHash, allowed_chars) << "</Password>" +			<< "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>"; +	} +	 +	stream +		<< "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>" +		<< "<ConnectText>" << (startText?"true":"false") << "</ConnectText>" +		<< "<Name>" << mChannelName << "</Name>" +	<< "</Request>\n\n\n"; +	writeString(stream.str()); +} + +void LLVivoxVoiceClient::sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio, bool startText) +{ +	LL_DEBUGS("Voice") << "requesting create: " << session->mSIPURI << LL_ENDL; +	 +	session->mCreateInProgress = true; +	if(startAudio) +	{ +		session->mMediaConnectInProgress = true; +	} +	 +	std::string password; +	if(!session->mHash.empty()) +	{ +		static const std::string allowed_chars = +					"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +					"0123456789" +					"-._~" +					; +		password = LLURI::escape(session->mHash, allowed_chars); +	} + +	std::ostringstream stream; +	stream +	<< "<Request requestId=\"" << session->mSIPURI << "\" action=\"SessionGroup.AddSession.1\">" +		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>" +		<< "<URI>" << session->mSIPURI << "</URI>" +		<< "<Name>" << mChannelName << "</Name>" +		<< "<ConnectAudio>" << (startAudio?"true":"false") << "</ConnectAudio>" +		<< "<ConnectText>" << (startText?"true":"false") << "</ConnectText>" +		<< "<Password>" << password << "</Password>" +		<< "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>" +	<< "</Request>\n\n\n" +	; +	 +	writeString(stream.str()); +} + +void LLVivoxVoiceClient::sessionMediaConnectSendMessage(sessionState *session) +{ +	LL_DEBUGS("Voice") << "connecting audio to session handle: " << session->mHandle << LL_ENDL; + +	session->mMediaConnectInProgress = true; +	 +	std::ostringstream stream; + +	stream +	<< "<Request requestId=\"" << session->mHandle << "\" action=\"Session.MediaConnect.1\">" +		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>" +		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>" +		<< "<Media>Audio</Media>" +	<< "</Request>\n\n\n"; + +	writeString(stream.str()); +} + +void LLVivoxVoiceClient::sessionTextConnectSendMessage(sessionState *session) +{ +	LL_DEBUGS("Voice") << "connecting text to session handle: " << session->mHandle << LL_ENDL; +	 +	std::ostringstream stream; + +	stream +	<< "<Request requestId=\"" << session->mHandle << "\" action=\"Session.TextConnect.1\">" +		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>" +		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>" +	<< "</Request>\n\n\n"; + +	writeString(stream.str()); +} + +void LLVivoxVoiceClient::sessionTerminate() +{ +	mSessionTerminateRequested = true; +} + +void LLVivoxVoiceClient::requestRelog() +{ +	mSessionTerminateRequested = true; +	mRelogRequested = true; +} + + +void LLVivoxVoiceClient::leaveAudioSession() +{ +	if(mAudioSession) +	{ +		LL_DEBUGS("Voice") << "leaving session: " << mAudioSession->mSIPURI << LL_ENDL; + +		switch(getState()) +		{ +			case stateNoChannel: +				// In this case, we want to pretend the join failed so our state machine doesn't get stuck. +				// Skip the join failed transition state so we don't send out error notifications. +				setState(stateJoinSessionFailedWaiting); +			break; +			case stateJoiningSession: +			case stateSessionJoined: +			case stateRunning: +				if(!mAudioSession->mHandle.empty()) +				{ + +#if RECORD_EVERYTHING +					// HACK: for testing only +					// Save looped recording +					std::string savepath("/tmp/vivoxrecording"); +					{ +						time_t now = time(NULL); +						const size_t BUF_SIZE = 64; +						char time_str[BUF_SIZE];	/* Flawfinder: ignore */ +						 +						strftime(time_str, BUF_SIZE, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now)); +						savepath += time_str; +					} +					recordingLoopSave(savepath); +#endif + +					sessionMediaDisconnectSendMessage(mAudioSession); +					setState(stateLeavingSession); +				} +				else +				{ +					LL_WARNS("Voice") << "called with no session handle" << LL_ENDL;	 +					setState(stateSessionTerminated); +				} +			break; +			case stateJoinSessionFailed: +			case stateJoinSessionFailedWaiting: +				setState(stateSessionTerminated); +			break; +			 +			default: +				LL_WARNS("Voice") << "called from unknown state" << LL_ENDL; +			break; +		} +	} +	else +	{ +		LL_WARNS("Voice") << "called with no active session" << LL_ENDL; +		setState(stateSessionTerminated); +	} +} + +void LLVivoxVoiceClient::sessionTerminateSendMessage(sessionState *session) +{ +	std::ostringstream stream; +	 +	LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << session->mHandle << LL_ENDL;	 +	stream +	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Terminate.1\">" +		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>" +	<< "</Request>\n\n\n"; +	 +	writeString(stream.str()); +} + +void LLVivoxVoiceClient::sessionGroupTerminateSendMessage(sessionState *session) +{ +	std::ostringstream stream; +	 +	LL_DEBUGS("Voice") << "Sending SessionGroup.Terminate with handle " << session->mGroupHandle << LL_ENDL;	 +	stream +	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.Terminate.1\">" +		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>" +	<< "</Request>\n\n\n"; +	 +	writeString(stream.str()); +} + +void LLVivoxVoiceClient::sessionMediaDisconnectSendMessage(sessionState *session) +{ +	std::ostringstream stream; +	 +	LL_DEBUGS("Voice") << "Sending Session.MediaDisconnect with handle " << session->mHandle << LL_ENDL;	 +	stream +	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.MediaDisconnect.1\">" +		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>" +		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>" +		<< "<Media>Audio</Media>" +	<< "</Request>\n\n\n"; +	 +	writeString(stream.str()); +	 +} + +void LLVivoxVoiceClient::sessionTextDisconnectSendMessage(sessionState *session) +{ +	std::ostringstream stream; +	 +	LL_DEBUGS("Voice") << "Sending Session.TextDisconnect with handle " << session->mHandle << LL_ENDL;	 +	stream +	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.TextDisconnect.1\">" +		<< "<SessionGroupHandle>" << session->mGroupHandle << "</SessionGroupHandle>" +		<< "<SessionHandle>" << session->mHandle << "</SessionHandle>" +	<< "</Request>\n\n\n"; +	 +	writeString(stream.str()); +} + +void LLVivoxVoiceClient::getCaptureDevicesSendMessage() +{ +	std::ostringstream stream; +	stream +	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.GetCaptureDevices.1\">" +	<< "</Request>\n\n\n"; +	 +	writeString(stream.str()); +} + +void LLVivoxVoiceClient::getRenderDevicesSendMessage() +{ +	std::ostringstream stream; +	stream +	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.GetRenderDevices.1\">" +	<< "</Request>\n\n\n"; +	 +	writeString(stream.str()); +} + +void LLVivoxVoiceClient::clearCaptureDevices() +{ +	LL_DEBUGS("Voice") << "called" << LL_ENDL; +	mCaptureDevices.clear(); +} + +void LLVivoxVoiceClient::addCaptureDevice(const std::string& name) +{ +	LL_DEBUGS("Voice") << name << LL_ENDL; + +	mCaptureDevices.push_back(name); +} + +LLVoiceDeviceList& LLVivoxVoiceClient::getCaptureDevices() +{ +	return mCaptureDevices; +} + +void LLVivoxVoiceClient::setCaptureDevice(const std::string& name) +{ +	if(name == "Default") +	{ +		if(!mCaptureDevice.empty()) +		{ +			mCaptureDevice.clear(); +			mCaptureDeviceDirty = true;	 +		} +	} +	else +	{ +		if(mCaptureDevice != name) +		{ +			mCaptureDevice = name; +			mCaptureDeviceDirty = true;	 +		} +	} +} + +void LLVivoxVoiceClient::clearRenderDevices() +{	 +	LL_DEBUGS("Voice") << "called" << LL_ENDL; +	mRenderDevices.clear(); +} + +void LLVivoxVoiceClient::addRenderDevice(const std::string& name) +{ +	LL_DEBUGS("Voice") << name << LL_ENDL; +	mRenderDevices.push_back(name); +} + +LLVoiceDeviceList& LLVivoxVoiceClient::getRenderDevices() +{ +	return mRenderDevices; +} + +void LLVivoxVoiceClient::setRenderDevice(const std::string& name) +{ +	if(name == "Default") +	{ +		if(!mRenderDevice.empty()) +		{ +			mRenderDevice.clear(); +			mRenderDeviceDirty = true;	 +		} +	} +	else +	{ +		if(mRenderDevice != name) +		{ +			mRenderDevice = name; +			mRenderDeviceDirty = true;	 +		} +	} +	 +} + +void LLVivoxVoiceClient::tuningStart() +{ +	mTuningMode = true; +	LL_DEBUGS("Voice") << "Starting tuning" << LL_ENDL; +	if(getState() >= stateNoChannel) +	{ +		LL_DEBUGS("Voice") << "no channel" << LL_ENDL; +		sessionTerminate(); +	} +} + +void LLVivoxVoiceClient::tuningStop() +{ +	mTuningMode = false; +} + +bool LLVivoxVoiceClient::inTuningMode() +{ +	bool result = false; +	switch(getState()) +	{ +	case stateMicTuningRunning: +		result = true; +		break; +	default: +		break; +	} +	return result; +} + +void LLVivoxVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop) +{		 +	mTuningAudioFile = name; +	std::ostringstream stream; +	stream +	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.RenderAudioStart.1\">" +    << "<SoundFilePath>" << mTuningAudioFile << "</SoundFilePath>" +    << "<Loop>" << (loop?"1":"0") << "</Loop>" +	<< "</Request>\n\n\n"; +	 +	writeString(stream.str()); +} + +void LLVivoxVoiceClient::tuningRenderStopSendMessage() +{ +	std::ostringstream stream; +	stream +	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.RenderAudioStop.1\">" +    << "<SoundFilePath>" << mTuningAudioFile << "</SoundFilePath>" +	<< "</Request>\n\n\n"; +	 +	writeString(stream.str()); +} + +void LLVivoxVoiceClient::tuningCaptureStartSendMessage(int duration) +{ +	LL_DEBUGS("Voice") << "sending CaptureAudioStart" << LL_ENDL; +	 +	std::ostringstream stream; +	stream +	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStart.1\">" +    << "<Duration>" << duration << "</Duration>" +	<< "</Request>\n\n\n"; +	 +	writeString(stream.str()); +} + +void LLVivoxVoiceClient::tuningCaptureStopSendMessage() +{ +	LL_DEBUGS("Voice") << "sending CaptureAudioStop" << LL_ENDL; +	 +	std::ostringstream stream; +	stream +	<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStop.1\">" +	<< "</Request>\n\n\n"; +	 +	writeString(stream.str()); + +	mTuningEnergy = 0.0f; +} + +void LLVivoxVoiceClient::tuningSetMicVolume(float volume) +{ +	int scaled_volume = scale_mic_volume(volume); + +	if(scaled_volume != mTuningMicVolume) +	{ +		mTuningMicVolume = scaled_volume; +		mTuningMicVolumeDirty = true; +	} +} + +void LLVivoxVoiceClient::tuningSetSpeakerVolume(float volume) +{ +	int scaled_volume = scale_speaker_volume(volume);	 + +	if(scaled_volume != mTuningSpeakerVolume) +	{ +		mTuningSpeakerVolume = scaled_volume; +		mTuningSpeakerVolumeDirty = true; +	} +} +				 +float LLVivoxVoiceClient::tuningGetEnergy(void) +{ +	return mTuningEnergy; +} + +bool LLVivoxVoiceClient::deviceSettingsAvailable() +{ +	bool result = true; +	 +	if(!mConnected) +		result = false; +	 +	if(mRenderDevices.empty()) +		result = false; +	 +	return result; +} + +void LLVivoxVoiceClient::refreshDeviceLists(bool clearCurrentList) +{ +	if(clearCurrentList) +	{ +		clearCaptureDevices(); +		clearRenderDevices(); +	} +	getCaptureDevicesSendMessage(); +	getRenderDevicesSendMessage(); +} + +void LLVivoxVoiceClient::daemonDied() +{ +	// The daemon died, so the connection is gone.  Reset everything and start over. +	LL_WARNS("Voice") << "Connection to vivox daemon lost.  Resetting state."<< LL_ENDL; + +	// Try to relaunch the daemon +	setState(stateDisableCleanup); +} + +void LLVivoxVoiceClient::giveUp() +{ +	// All has failed.  Clean up and stop trying. +	closeSocket(); +	deleteAllSessions(); +	deleteAllBuddies(); +	 +	setState(stateJail); +} + +static void oldSDKTransform (LLVector3 &left, LLVector3 &up, LLVector3 &at, LLVector3d &pos, LLVector3 &vel) +{ +	F32 nat[3], nup[3], nl[3], nvel[3]; // the new at, up, left vectors and the  new position and velocity +	F64 npos[3]; +	 +	// The original XML command was sent like this: +	/* +			<< "<Position>" +				<< "<X>" << pos[VX] << "</X>" +				<< "<Y>" << pos[VZ] << "</Y>" +				<< "<Z>" << pos[VY] << "</Z>" +			<< "</Position>" +			<< "<Velocity>" +				<< "<X>" << mAvatarVelocity[VX] << "</X>" +				<< "<Y>" << mAvatarVelocity[VZ] << "</Y>" +				<< "<Z>" << mAvatarVelocity[VY] << "</Z>" +			<< "</Velocity>" +			<< "<AtOrientation>" +				<< "<X>" << l.mV[VX] << "</X>" +				<< "<Y>" << u.mV[VX] << "</Y>" +				<< "<Z>" << a.mV[VX] << "</Z>" +			<< "</AtOrientation>" +			<< "<UpOrientation>" +				<< "<X>" << l.mV[VZ] << "</X>" +				<< "<Y>" << u.mV[VY] << "</Y>" +				<< "<Z>" << a.mV[VZ] << "</Z>" +			<< "</UpOrientation>" +			<< "<LeftOrientation>" +				<< "<X>" << l.mV [VY] << "</X>" +				<< "<Y>" << u.mV [VZ] << "</Y>" +				<< "<Z>" << a.mV [VY] << "</Z>" +			<< "</LeftOrientation>"; +	*/ + +#if 1 +	// This was the original transform done when building the XML command +	nat[0] = left.mV[VX]; +	nat[1] = up.mV[VX]; +	nat[2] = at.mV[VX]; + +	nup[0] = left.mV[VZ]; +	nup[1] = up.mV[VY]; +	nup[2] = at.mV[VZ]; + +	nl[0] = left.mV[VY]; +	nl[1] = up.mV[VZ]; +	nl[2] = at.mV[VY]; + +	npos[0] = pos.mdV[VX]; +	npos[1] = pos.mdV[VZ]; +	npos[2] = pos.mdV[VY]; + +	nvel[0] = vel.mV[VX]; +	nvel[1] = vel.mV[VZ]; +	nvel[2] = vel.mV[VY]; + +	for(int i=0;i<3;++i) { +		at.mV[i] = nat[i]; +		up.mV[i] = nup[i]; +		left.mV[i] = nl[i]; +		pos.mdV[i] = npos[i]; +	} +	 +	// This was the original transform done in the SDK +	nat[0] = at.mV[2]; +	nat[1] = 0; // y component of at vector is always 0, this was up[2] +	nat[2] = -1 * left.mV[2]; + +	// We override whatever the application gives us +	nup[0] = 0; // x component of up vector is always 0 +	nup[1] = 1; // y component of up vector is always 1 +	nup[2] = 0; // z component of up vector is always 0 + +	nl[0] = at.mV[0]; +	nl[1] = 0;  // y component of left vector is always zero, this was up[0] +	nl[2] = -1 * left.mV[0]; + +	npos[2] = pos.mdV[2] * -1.0; +	npos[1] = pos.mdV[1]; +	npos[0] = pos.mdV[0]; + +	for(int i=0;i<3;++i) { +		at.mV[i] = nat[i]; +		up.mV[i] = nup[i]; +		left.mV[i] = nl[i]; +		pos.mdV[i] = npos[i]; +	} +#else +	// This is the compose of the two transforms (at least, that's what I'm trying for) +	nat[0] = at.mV[VX]; +	nat[1] = 0; // y component of at vector is always 0, this was up[2] +	nat[2] = -1 * up.mV[VZ]; + +	// We override whatever the application gives us +	nup[0] = 0; // x component of up vector is always 0 +	nup[1] = 1; // y component of up vector is always 1 +	nup[2] = 0; // z component of up vector is always 0 + +	nl[0] = left.mV[VX]; +	nl[1] = 0;  // y component of left vector is always zero, this was up[0] +	nl[2] = -1 * left.mV[VY]; + +	npos[0] = pos.mdV[VX]; +	npos[1] = pos.mdV[VZ]; +	npos[2] = pos.mdV[VY] * -1.0; + +	nvel[0] = vel.mV[VX]; +	nvel[1] = vel.mV[VZ]; +	nvel[2] = vel.mV[VY]; + +	for(int i=0;i<3;++i) { +		at.mV[i] = nat[i]; +		up.mV[i] = nup[i]; +		left.mV[i] = nl[i]; +		pos.mdV[i] = npos[i]; +	} +	 +#endif +} + +void LLVivoxVoiceClient::sendPositionalUpdate(void) +{	 +	std::ostringstream stream; +	 +	if(mSpatialCoordsDirty) +	{ +		LLVector3 l, u, a, vel; +		LLVector3d pos; + +		mSpatialCoordsDirty = false; +		 +		// Always send both speaker and listener positions together. +		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Set3DPosition.1\">"		 +			<< "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>"; +		 +		stream << "<SpeakerPosition>"; + +//		LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL; +		l = mAvatarRot.getLeftRow(); +		u = mAvatarRot.getUpRow(); +		a = mAvatarRot.getFwdRow(); +		pos = mAvatarPosition; +		vel = mAvatarVelocity; + +		// SLIM SDK: the old SDK was doing a transform on the passed coordinates that the new one doesn't do anymore. +		// The old transform is replicated by this function. +		oldSDKTransform(l, u, a, pos, vel); +		 +		stream  +			<< "<Position>" +				<< "<X>" << pos.mdV[VX] << "</X>" +				<< "<Y>" << pos.mdV[VY] << "</Y>" +				<< "<Z>" << pos.mdV[VZ] << "</Z>" +			<< "</Position>" +			<< "<Velocity>" +				<< "<X>" << vel.mV[VX] << "</X>" +				<< "<Y>" << vel.mV[VY] << "</Y>" +				<< "<Z>" << vel.mV[VZ] << "</Z>" +			<< "</Velocity>" +			<< "<AtOrientation>" +				<< "<X>" << a.mV[VX] << "</X>" +				<< "<Y>" << a.mV[VY] << "</Y>" +				<< "<Z>" << a.mV[VZ] << "</Z>" +			<< "</AtOrientation>" +			<< "<UpOrientation>" +				<< "<X>" << u.mV[VX] << "</X>" +				<< "<Y>" << u.mV[VY] << "</Y>" +				<< "<Z>" << u.mV[VZ] << "</Z>" +			<< "</UpOrientation>" +			<< "<LeftOrientation>" +				<< "<X>" << l.mV [VX] << "</X>" +				<< "<Y>" << l.mV [VY] << "</Y>" +				<< "<Z>" << l.mV [VZ] << "</Z>" +			<< "</LeftOrientation>"; + +		stream << "</SpeakerPosition>"; + +		stream << "<ListenerPosition>"; + +		LLVector3d	earPosition; +		LLVector3	earVelocity; +		LLMatrix3	earRot; +		 +		switch(mEarLocation) +		{ +			case earLocCamera: +			default: +				earPosition = mCameraPosition; +				earVelocity = mCameraVelocity; +				earRot = mCameraRot; +			break; +			 +			case earLocAvatar: +				earPosition = mAvatarPosition; +				earVelocity = mAvatarVelocity; +				earRot = mAvatarRot; +			break; +			 +			case earLocMixed: +				earPosition = mAvatarPosition; +				earVelocity = mAvatarVelocity; +				earRot = mCameraRot; +			break; +		} + +		l = earRot.getLeftRow(); +		u = earRot.getUpRow(); +		a = earRot.getFwdRow(); +		pos = earPosition; +		vel = earVelocity; + +//		LL_DEBUGS("Voice") << "Sending listener position " << earPosition << LL_ENDL; +		 +		oldSDKTransform(l, u, a, pos, vel); +		 +		stream  +			<< "<Position>" +				<< "<X>" << pos.mdV[VX] << "</X>" +				<< "<Y>" << pos.mdV[VY] << "</Y>" +				<< "<Z>" << pos.mdV[VZ] << "</Z>" +			<< "</Position>" +			<< "<Velocity>" +				<< "<X>" << vel.mV[VX] << "</X>" +				<< "<Y>" << vel.mV[VY] << "</Y>" +				<< "<Z>" << vel.mV[VZ] << "</Z>" +			<< "</Velocity>" +			<< "<AtOrientation>" +				<< "<X>" << a.mV[VX] << "</X>" +				<< "<Y>" << a.mV[VY] << "</Y>" +				<< "<Z>" << a.mV[VZ] << "</Z>" +			<< "</AtOrientation>" +			<< "<UpOrientation>" +				<< "<X>" << u.mV[VX] << "</X>" +				<< "<Y>" << u.mV[VY] << "</Y>" +				<< "<Z>" << u.mV[VZ] << "</Z>" +			<< "</UpOrientation>" +			<< "<LeftOrientation>" +				<< "<X>" << l.mV [VX] << "</X>" +				<< "<Y>" << l.mV [VY] << "</Y>" +				<< "<Z>" << l.mV [VZ] << "</Z>" +			<< "</LeftOrientation>"; + + +		stream << "</ListenerPosition>"; + +		stream << "</Request>\n\n\n"; +	}	 +	 +	if(mAudioSession && mAudioSession->mVolumeDirty) +	{ +		participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin(); + +		mAudioSession->mVolumeDirty = false; +		 +		for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) +		{ +			participantState *p = iter->second; +			 +			if(p->mVolumeDirty) +			{ +				// Can't set volume/mute for yourself +				if(!p->mIsSelf) +				{ +					int volume = 56; // nominal default value +					bool mute = p->mOnMuteList; +					 +					if(p->mUserVolume != -1) +					{ +						// scale from user volume in the range 0-400 (with 100 as "normal") to vivox volume in the range 0-100 (with 56 as "normal") +						if(p->mUserVolume < 100) +							volume = (p->mUserVolume * 56) / 100; +						else +							volume = (((p->mUserVolume - 100) * (100 - 56)) / 300) + 56; +					} +					else if(p->mVolume != -1) +					{ +						// Use the previously reported internal volume (comes in with a ParticipantUpdatedEvent) +						volume = p->mVolume; +					} +										 + +					if(mute) +					{ +						// SetParticipantMuteForMe doesn't work in p2p sessions. +						// If we want the user to be muted, set their volume to 0 as well. +						// This isn't perfect, but it will at least reduce their volume to a minimum. +						volume = 0; +					} +					 +					if(volume == 0) +						mute = true; + +					LL_DEBUGS("Voice") << "Setting volume/mute for avatar " << p->mAvatarID << " to " << volume << (mute?"/true":"/false") << LL_ENDL; +					 +					// SLIM SDK: Send both volume and mute commands. +					 +					// Send a "volume for me" command for the user. +					stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantVolumeForMe.1\">" +						<< "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>" +						<< "<ParticipantURI>" << p->mURI << "</ParticipantURI>" +						<< "<Volume>" << volume << "</Volume>" +						<< "</Request>\n\n\n"; + +					if(!mAudioSession->mIsP2P) +					  { +					    // Send a "mute for me" command for the user +					    // Doesn't work in P2P sessions +					    stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantMuteForMe.1\">" +					      << "<SessionHandle>" << getAudioSessionHandle() << "</SessionHandle>" +					      << "<ParticipantURI>" << p->mURI << "</ParticipantURI>" +					      << "<Mute>" << (mute?"1":"0") << "</Mute>" +					      << "<Scope>Audio</Scope>" +					      << "</Request>\n\n\n"; +					    } +				} +				 +				p->mVolumeDirty = false; +			} +		} +	} +			 +	buildLocalAudioUpdates(stream); +	 +	if(!stream.str().empty()) +	{ +		writeString(stream.str()); +	} +	 +	// Friends list updates can be huge, especially on the first voice login of an account with lots of friends. +	// Batching them all together can choke SLVoice, so send them in separate writes. +	sendFriendsListUpdates(); +} + +void LLVivoxVoiceClient::buildSetCaptureDevice(std::ostringstream &stream) +{ +	if(mCaptureDeviceDirty) +	{ +		LL_DEBUGS("Voice") << "Setting input device = \"" << mCaptureDevice << "\"" << LL_ENDL; +	 +		stream  +		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetCaptureDevice.1\">" +			<< "<CaptureDeviceSpecifier>" << mCaptureDevice << "</CaptureDeviceSpecifier>" +		<< "</Request>" +		<< "\n\n\n"; +		 +		mCaptureDeviceDirty = false; +	} +} + +void LLVivoxVoiceClient::buildSetRenderDevice(std::ostringstream &stream) +{ +	if(mRenderDeviceDirty) +	{ +		LL_DEBUGS("Voice") << "Setting output device = \"" << mRenderDevice << "\"" << LL_ENDL; + +		stream +		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetRenderDevice.1\">" +			<< "<RenderDeviceSpecifier>" << mRenderDevice << "</RenderDeviceSpecifier>" +		<< "</Request>" +		<< "\n\n\n"; +		mRenderDeviceDirty = false; +	} +} + +void LLVivoxVoiceClient::buildLocalAudioUpdates(std::ostringstream &stream) +{ +	buildSetCaptureDevice(stream); + +	buildSetRenderDevice(stream); + +	if(mPTTDirty) +	{ +		mPTTDirty = false; + +		// Send a local mute command. +		// NOTE that the state of "PTT" is the inverse of "local mute". +		//   (i.e. when PTT is true, we send a mute command with "false", and vice versa) +		 +		LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter " << (mPTT?"false":"true") << LL_ENDL; + +		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">" +			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" +			<< "<Value>" << (mPTT?"false":"true") << "</Value>" +			<< "</Request>\n\n\n"; +		 +	} + +	if(mSpeakerMuteDirty) +	{ +	  const char *muteval = ((mSpeakerVolume <= scale_speaker_volume(0))?"true":"false"); + +		mSpeakerMuteDirty = false; + +		LL_INFOS("Voice") << "Setting speaker mute to " << muteval  << LL_ENDL; +		 +		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalSpeaker.1\">" +			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" +			<< "<Value>" << muteval << "</Value>" +			<< "</Request>\n\n\n";	 +		 +	} +	 +	if(mSpeakerVolumeDirty) +	{ +		mSpeakerVolumeDirty = false; + +		LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume  << LL_ENDL; + +		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalSpeakerVolume.1\">" +			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" +			<< "<Value>" << mSpeakerVolume << "</Value>" +			<< "</Request>\n\n\n"; +			 +	} +	 +	if(mMicVolumeDirty) +	{ +		mMicVolumeDirty = false; + +		LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume  << LL_ENDL; + +		stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalMicVolume.1\">" +			<< "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>" +			<< "<Value>" << mMicVolume << "</Value>" +			<< "</Request>\n\n\n";				 +	} + +	 +} + +void LLVivoxVoiceClient::checkFriend(const LLUUID& id) +{ +	std::string name; +	buddyListEntry *buddy = findBuddy(id); + +	// Make sure we don't add a name before it's been looked up. +	if(gCacheName->getFullName(id, name)) +	{ + +		const LLRelationship* relationInfo = LLAvatarTracker::instance().getBuddyInfo(id); +		bool canSeeMeOnline = false; +		if(relationInfo && relationInfo->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS)) +			canSeeMeOnline = true; +		 +		// When we get here, mNeedsSend is true and mInSLFriends is false.  Change them as necessary. +		 +		if(buddy) +		{ +			// This buddy is already in both lists. + +			if(name != buddy->mDisplayName) +			{ +				// The buddy is in the list with the wrong name.  Update it with the correct name. +				LL_WARNS("Voice") << "Buddy " << id << " has wrong name (\"" << buddy->mDisplayName << "\" should be \"" << name << "\"), updating."<< LL_ENDL; +				buddy->mDisplayName = name; +				buddy->mNeedsNameUpdate = true;		// This will cause the buddy to be resent. +			} +		} +		else +		{ +			// This buddy was not in the vivox list, needs to be added. +			buddy = addBuddy(sipURIFromID(id), name); +			buddy->mUUID = id; +		} +		 +		// In all the above cases, the buddy is in the SL friends list (which is how we got here). +		buddy->mInSLFriends = true; +		buddy->mCanSeeMeOnline = canSeeMeOnline; +		buddy->mNameResolved = true; +		 +	} +	else +	{ +		// This name hasn't been looked up yet.  Don't do anything with this buddy list entry until it has. +		if(buddy) +		{ +			buddy->mNameResolved = false; +		} +		 +		// Initiate a lookup. +		// The "lookup completed" callback will ensure that the friends list is rechecked after it completes. +		lookupName(id); +	} +} + +void LLVivoxVoiceClient::clearAllLists() +{ +	// FOR TESTING ONLY +	 +	// This will send the necessary commands to delete ALL buddies, autoaccept rules, and block rules SLVoice tells us about. +	buddyListMap::iterator buddy_it; +	for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();) +	{ +		buddyListEntry *buddy = buddy_it->second; +		buddy_it++; +		 +		std::ostringstream stream; + +		if(buddy->mInVivoxBuddies) +		{ +			// delete this entry from the vivox buddy list +			buddy->mInVivoxBuddies = false; +			LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL; +			stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.BuddyDelete.1\">" +				<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" +				<< "<BuddyURI>" << buddy->mURI << "</BuddyURI>" +				<< "</Request>\n\n\n";		 +		} + +		if(buddy->mHasBlockListEntry) +		{ +			// Delete the associated block list entry (so the block list doesn't fill up with junk) +			buddy->mHasBlockListEntry = false; +			stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteBlockRule.1\">" +				<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" +				<< "<BlockMask>" << buddy->mURI << "</BlockMask>" +				<< "</Request>\n\n\n";								 +		} +		if(buddy->mHasAutoAcceptListEntry) +		{ +			// Delete the associated auto-accept list entry (so the auto-accept list doesn't fill up with junk) +			buddy->mHasAutoAcceptListEntry = false; +			stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteAutoAcceptRule.1\">" +				<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" +				<< "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>" +				<< "</Request>\n\n\n"; +		} + +		writeString(stream.str()); + +	} +} + +void LLVivoxVoiceClient::sendFriendsListUpdates() +{ +	if(mBuddyListMapPopulated && mBlockRulesListReceived && mAutoAcceptRulesListReceived && mFriendsListDirty) +	{ +		mFriendsListDirty = false; +		 +		if(0) +		{ +			// FOR TESTING ONLY -- clear all buddy list, block list, and auto-accept list entries. +			clearAllLists(); +			return; +		} +		 +		LL_INFOS("Voice") << "Checking vivox buddy list against friends list..." << LL_ENDL; +		 +		buddyListMap::iterator buddy_it; +		for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++) +		{ +			// reset the temp flags in the local buddy list +			buddy_it->second->mInSLFriends = false; +		} +		 +		// correlate with the friends list +		{ +			LLCollectAllBuddies collect; +			LLAvatarTracker::instance().applyFunctor(collect); +			LLCollectAllBuddies::buddy_map_t::const_iterator it = collect.mOnline.begin(); +			LLCollectAllBuddies::buddy_map_t::const_iterator end = collect.mOnline.end(); +			 +			for ( ; it != end; ++it) +			{ +				checkFriend(it->second); +			} +			it = collect.mOffline.begin(); +			end = collect.mOffline.end(); +			for ( ; it != end; ++it) +			{ +				checkFriend(it->second); +			} +		} +				 +		LL_INFOS("Voice") << "Sending friend list updates..." << LL_ENDL; + +		for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end();) +		{ +			buddyListEntry *buddy = buddy_it->second; +			buddy_it++; +			 +			// Ignore entries that aren't resolved yet. +			if(buddy->mNameResolved) +			{ +				std::ostringstream stream; + +				if(buddy->mInSLFriends && (!buddy->mInVivoxBuddies || buddy->mNeedsNameUpdate)) +				{					 +					if(mNumberOfAliases > 0) +					{ +						// Add (or update) this entry in the vivox buddy list +						buddy->mInVivoxBuddies = true; +						buddy->mNeedsNameUpdate = false; +						LL_DEBUGS("Voice") << "add/update " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL; +						stream  +							<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.BuddySet.1\">" +								<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" +								<< "<BuddyURI>" << buddy->mURI << "</BuddyURI>" +								<< "<DisplayName>" << buddy->mDisplayName << "</DisplayName>" +								<< "<BuddyData></BuddyData>"	// Without this, SLVoice doesn't seem to parse the command. +								<< "<GroupID>0</GroupID>" +							<< "</Request>\n\n\n";	 +					} +				} +				else if(!buddy->mInSLFriends) +				{ +					// This entry no longer exists in your SL friends list.  Remove all traces of it from the Vivox buddy list. + 					if(buddy->mInVivoxBuddies) +					{ +						// delete this entry from the vivox buddy list +						buddy->mInVivoxBuddies = false; +						LL_DEBUGS("Voice") << "delete " << buddy->mURI << " (" << buddy->mDisplayName << ")" << LL_ENDL; +						stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.BuddyDelete.1\">" +							<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" +							<< "<BuddyURI>" << buddy->mURI << "</BuddyURI>" +							<< "</Request>\n\n\n";		 +					} + +					if(buddy->mHasBlockListEntry) +					{ +						// Delete the associated block list entry, if any +						buddy->mHasBlockListEntry = false; +						stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteBlockRule.1\">" +							<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" +							<< "<BlockMask>" << buddy->mURI << "</BlockMask>" +							<< "</Request>\n\n\n";								 +					} +					if(buddy->mHasAutoAcceptListEntry) +					{ +						// Delete the associated auto-accept list entry, if any +						buddy->mHasAutoAcceptListEntry = false; +						stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteAutoAcceptRule.1\">" +							<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" +							<< "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>" +							<< "</Request>\n\n\n"; +					} +				} +				 +				if(buddy->mInSLFriends) +				{ + +					if(buddy->mCanSeeMeOnline) +					{ +						// Buddy should not be blocked. + +						// If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent. +						 +						// If the buddy has a block list entry, delete it. +						if(buddy->mHasBlockListEntry) +						{ +							buddy->mHasBlockListEntry = false; +							stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteBlockRule.1\">" +								<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" +								<< "<BlockMask>" << buddy->mURI << "</BlockMask>" +								<< "</Request>\n\n\n";		 +							 +							 +							// If we just deleted a block list entry, add an auto-accept entry. +							if(!buddy->mHasAutoAcceptListEntry) +							{ +								buddy->mHasAutoAcceptListEntry = true;								 +								stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.CreateAutoAcceptRule.1\">" +									<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" +									<< "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>" +									<< "<AutoAddAsBuddy>0</AutoAddAsBuddy>" +									<< "</Request>\n\n\n"; +							} +						} +					} +					else +					{ +						// Buddy should be blocked. +						 +						// If this buddy doesn't already have either a block or autoaccept list entry, we'll update their status when we receive a SubscriptionEvent. + +						// If this buddy has an autoaccept entry, delete it +						if(buddy->mHasAutoAcceptListEntry) +						{ +							buddy->mHasAutoAcceptListEntry = false; +							stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.DeleteAutoAcceptRule.1\">" +								<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" +								<< "<AutoAcceptMask>" << buddy->mURI << "</AutoAcceptMask>" +								<< "</Request>\n\n\n"; +						 +							// If we just deleted an auto-accept entry, add a block list entry. +							if(!buddy->mHasBlockListEntry) +							{ +								buddy->mHasBlockListEntry = true; +								stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.CreateBlockRule.1\">" +									<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" +									<< "<BlockMask>" << buddy->mURI << "</BlockMask>" +									<< "<PresenceOnly>1</PresenceOnly>" +									<< "</Request>\n\n\n";								 +							} +						} +					} + +					if(!buddy->mInSLFriends && !buddy->mInVivoxBuddies) +					{ +						// Delete this entry from the local buddy list.  This should NOT invalidate the iterator, +						// since it has already been incremented to the next entry. +						deleteBuddy(buddy->mURI); +					} + +				} +				writeString(stream.str()); +			} +		} +	} +} + +///////////////////////////// +// Response/Event handlers + +void LLVivoxVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID) +{	 +	if(statusCode != 0) +	{ +		LL_WARNS("Voice") << "Connector.Create response failure: " << statusString << LL_ENDL; +		setState(stateConnectorFailed); +		LLSD args; +		std::stringstream errs; +		errs << mVoiceAccountServerURI << "\n:UDP: 3478, 3479, 5060, 5062, 12000-17000"; +		args["HOSTID"] = errs.str(); +		if (LLGridManager::getInstance()->isSystemGrid()) +		{ +			LLNotificationsUtil::add("NoVoiceConnect", args);	 +		} +		else +		{ +			LLNotificationsUtil::add("NoVoiceConnect-GIAB", args);	 +		} +	} +	else +	{ +		// Connector created, move forward. +		LL_INFOS("Voice") << "Connector.Create succeeded, Vivox SDK version is " << versionID << LL_ENDL; +		mVoiceVersion.serverVersion = versionID; +		mConnectorHandle = connectorHandle; +		if(getState() == stateConnectorStarting) +		{ +			setState(stateConnectorStarted); +		} +	} +} + +void LLVivoxVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases) +{  +	LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL; +	 +	// Status code of 20200 means "bad password".  We may want to special-case that at some point. +	 +	if ( statusCode == 401 ) +	{ +		// Login failure which is probably caused by the delay after a user's password being updated. +		LL_INFOS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; +		setState(stateLoginRetry); +	} +	else if(statusCode != 0) +	{ +		LL_WARNS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL; +		setState(stateLoginFailed); +	} +	else +	{ +		// Login succeeded, move forward. +		mAccountHandle = accountHandle; +		mNumberOfAliases = numberOfAliases; +		// This needs to wait until the AccountLoginStateChangeEvent is received. +//		if(getState() == stateLoggingIn) +//		{ +//			setState(stateLoggedIn); +//		} +	} +} + +void LLVivoxVoiceClient::sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle) +{	 +	sessionState *session = findSessionBeingCreatedByURI(requestId); +	 +	if(session) +	{ +		session->mCreateInProgress = false; +	} +	 +	if(statusCode != 0) +	{ +		LL_WARNS("Voice") << "Session.Create response failure (" << statusCode << "): " << statusString << LL_ENDL; +		if(session) +		{ +			session->mErrorStatusCode = statusCode;		 +			session->mErrorStatusString = statusString; +			if(session == mAudioSession) +			{ +				setState(stateJoinSessionFailed); +			} +			else +			{ +				reapSession(session); +			} +		} +	} +	else +	{ +		LL_INFOS("Voice") << "Session.Create response received (success), session handle is " << sessionHandle << LL_ENDL; +		if(session) +		{ +			setSessionHandle(session, sessionHandle); +		} +	} +} + +void LLVivoxVoiceClient::sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle) +{	 +	sessionState *session = findSessionBeingCreatedByURI(requestId); +	 +	if(session) +	{ +		session->mCreateInProgress = false; +	} +	 +	if(statusCode != 0) +	{ +		LL_WARNS("Voice") << "SessionGroup.AddSession response failure (" << statusCode << "): " << statusString << LL_ENDL; +		if(session) +		{ +			session->mErrorStatusCode = statusCode;		 +			session->mErrorStatusString = statusString; +			if(session == mAudioSession) +			{ +				setState(stateJoinSessionFailed); +			} +			else +			{ +				reapSession(session); +			} +		} +	} +	else +	{ +		LL_DEBUGS("Voice") << "SessionGroup.AddSession response received (success), session handle is " << sessionHandle << LL_ENDL; +		if(session) +		{ +			setSessionHandle(session, sessionHandle); +		} +	} +} + +void LLVivoxVoiceClient::sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString) +{ +	sessionState *session = findSession(requestId); +	if(statusCode != 0) +	{ +		LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL; +		if(session) +		{ +			session->mMediaConnectInProgress = false; +			session->mErrorStatusCode = statusCode;		 +			session->mErrorStatusString = statusString; +			if(session == mAudioSession) +				setState(stateJoinSessionFailed); +		} +	} +	else +	{ +		LL_DEBUGS("Voice") << "Session.Connect response received (success)" << LL_ENDL; +	} +} + +void LLVivoxVoiceClient::logoutResponse(int statusCode, std::string &statusString) +{	 +	if(statusCode != 0) +	{ +		LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL; +		// Should this ever fail?  do we care if it does? +	} +} + +void LLVivoxVoiceClient::connectorShutdownResponse(int statusCode, std::string &statusString) +{ +	if(statusCode != 0) +	{ +		LL_WARNS("Voice") << "Connector.InitiateShutdown response failure: " << statusString << LL_ENDL; +		// Should this ever fail?  do we care if it does? +	} +	 +	mConnected = false; +	 +	if(getState() == stateConnectorStopping) +	{ +		setState(stateConnectorStopped); +	} +} + +void LLVivoxVoiceClient::sessionAddedEvent( +		std::string &uriString,  +		std::string &alias,  +		std::string &sessionHandle,  +		std::string &sessionGroupHandle,  +		bool isChannel,  +		bool incoming, +		std::string &nameString, +		std::string &applicationString) +{ +	sessionState *session = NULL; + +	LL_INFOS("Voice") << "session " << uriString << ", alias " << alias << ", name " << nameString << " handle " << sessionHandle << LL_ENDL; +	 +	session = addSession(uriString, sessionHandle); +	if(session) +	{ +		session->mGroupHandle = sessionGroupHandle; +		session->mIsChannel = isChannel; +		session->mIncoming = incoming; +		session->mAlias = alias; +			 +		// Generate a caller UUID -- don't need to do this for channels +		if(!session->mIsChannel) +		{ +			if(IDFromName(session->mSIPURI, session->mCallerID)) +			{ +				// Normal URI(base64-encoded UUID)  +			} +			else if(!session->mAlias.empty() && IDFromName(session->mAlias, session->mCallerID)) +			{ +				// Wrong URI, but an alias is available.  Stash the incoming URI as an alternate +				session->mAlternateSIPURI = session->mSIPURI; +				 +				// and generate a proper URI from the ID. +				setSessionURI(session, sipURIFromID(session->mCallerID)); +			} +			else +			{ +				LL_INFOS("Voice") << "Could not generate caller id from uri, using hash of uri " << session->mSIPURI << LL_ENDL; +				setUUIDFromStringHash(session->mCallerID, session->mSIPURI); +				session->mSynthesizedCallerID = true; +				 +				// Can't look up the name in this case -- we have to extract it from the URI. +				std::string namePortion = nameFromsipURI(session->mSIPURI); +				if(namePortion.empty()) +				{ +					// Didn't seem to be a SIP URI, just use the whole provided name. +					namePortion = nameString; +				} +				 +				// Some incoming names may be separated with an underscore instead of a space.  Fix this. +				LLStringUtil::replaceChar(namePortion, '_', ' '); +				 +				// Act like we just finished resolving the name (this stores it in all the right places) +				avatarNameResolved(session->mCallerID, namePortion); +			} +		 +			LL_INFOS("Voice") << "caller ID: " << session->mCallerID << LL_ENDL; + +			if(!session->mSynthesizedCallerID) +			{ +				// If we got here, we don't have a proper name.  Initiate a lookup. +				lookupName(session->mCallerID); +			} +		} +	} +} + +void LLVivoxVoiceClient::sessionGroupAddedEvent(std::string &sessionGroupHandle) +{ +	LL_DEBUGS("Voice") << "handle " << sessionGroupHandle << LL_ENDL; +	 +#if USE_SESSION_GROUPS +	if(mMainSessionGroupHandle.empty()) +	{ +		// This is the first (i.e. "main") session group.  Save its handle. +		mMainSessionGroupHandle = sessionGroupHandle; +	} +	else +	{ +		LL_DEBUGS("Voice") << "Already had a session group handle " << mMainSessionGroupHandle << LL_ENDL; +	} +#endif +} + +void LLVivoxVoiceClient::joinedAudioSession(sessionState *session) +{ +	LL_DEBUGS("Voice") << "Joined Audio Session" << LL_ENDL; +	if(mAudioSession != session) +	{ +		sessionState *oldSession = mAudioSession; + +		mAudioSession = session; +		mAudioSessionChanged = true; + +		// The old session may now need to be deleted. +		reapSession(oldSession); +	} +	 +	// This is the session we're joining. +	if(getState() == stateJoiningSession) +	{ +		setState(stateSessionJoined); +		 +		// SLIM SDK: we don't always receive a participant state change for ourselves when joining a channel now. +		// Add the current user as a participant here. +		participantState *participant = session->addParticipant(sipURIFromName(mAccountName)); +		if(participant) +		{ +			participant->mIsSelf = true; +			lookupName(participant->mAvatarID); + +			LL_INFOS("Voice") << "added self as participant \"" << participant->mAccountName  +					<< "\" (" << participant->mAvatarID << ")"<< LL_ENDL; +		} +		 +		if(!session->mIsChannel) +		{ +			// this is a p2p session.  Make sure the other end is added as a participant. +			participantState *participant = session->addParticipant(session->mSIPURI); +			if(participant) +			{ +				if(participant->mAvatarIDValid) +				{ +					lookupName(participant->mAvatarID); +				} +				else if(!session->mName.empty()) +				{ +					participant->mDisplayName = session->mName; +					avatarNameResolved(participant->mAvatarID, session->mName); +				} +				 +				// TODO: Question: Do we need to set up mAvatarID/mAvatarIDValid here? +				LL_INFOS("Voice") << "added caller as participant \"" << participant->mAccountName  +						<< "\" (" << participant->mAvatarID << ")"<< LL_ENDL; +			} +		} +	} +} + +void LLVivoxVoiceClient::sessionRemovedEvent( +	std::string &sessionHandle,  +	std::string &sessionGroupHandle) +{ +	LL_INFOS("Voice") << "handle " << sessionHandle << LL_ENDL; +	 +	sessionState *session = findSession(sessionHandle); +	if(session) +	{ +		leftAudioSession(session); + +		// This message invalidates the session's handle.  Set it to empty. +		setSessionHandle(session); +		 +		// This also means that the session's session group is now empty. +		// Terminate the session group so it doesn't leak. +		sessionGroupTerminateSendMessage(session); +		 +		// Reset the media state (we now have no info) +		session->mMediaStreamState = streamStateUnknown; +		session->mTextStreamState = streamStateUnknown; +		 +		// Conditionally delete the session +		reapSession(session); +	} +	else +	{ +		LL_WARNS("Voice") << "unknown session " << sessionHandle << " removed" << LL_ENDL; +	} +} + +void LLVivoxVoiceClient::reapSession(sessionState *session) +{ +	if(session) +	{ +		if(!session->mHandle.empty()) +		{ +			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (non-null session handle)" << LL_ENDL; +		} +		else if(session->mCreateInProgress) +		{ +			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (create in progress)" << LL_ENDL; +		} +		else if(session->mMediaConnectInProgress) +		{ +			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (connect in progress)" << LL_ENDL; +		} +		else if(session == mAudioSession) +		{ +			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the current session)" << LL_ENDL; +		} +		else if(session == mNextAudioSession) +		{ +			LL_DEBUGS("Voice") << "NOT deleting session " << session->mSIPURI << " (it's the next session)" << LL_ENDL; +		} +		else +		{ +			// TODO: Question: Should we check for queued text messages here? +			// We don't have a reason to keep tracking this session, so just delete it. +			LL_DEBUGS("Voice") << "deleting session " << session->mSIPURI << LL_ENDL; +			deleteSession(session); +			session = NULL; +		}	 +	} +	else +	{ +//		LL_DEBUGS("Voice") << "session is NULL" << LL_ENDL; +	} +} + +// Returns true if the session seems to indicate we've moved to a region on a different voice server +bool LLVivoxVoiceClient::sessionNeedsRelog(sessionState *session) +{ +	bool result = false; +	 +	if(session != NULL) +	{ +		// Only make this check for spatial channels (so it won't happen for group or p2p calls) +		if(session->mIsSpatial) +		{	 +			std::string::size_type atsign; +			 +			atsign = session->mSIPURI.find("@"); +			 +			if(atsign != std::string::npos) +			{ +				std::string urihost = session->mSIPURI.substr(atsign + 1); +				if(stricmp(urihost.c_str(), mVoiceSIPURIHostName.c_str())) +				{ +					// The hostname in this URI is different from what we expect.  This probably means we need to relog. +					 +					// We could make a ProvisionVoiceAccountRequest and compare the result with the current values of +					// mVoiceSIPURIHostName and mVoiceAccountServerURI to be really sure, but this is a pretty good indicator. +					 +					result = true; +				} +			} +		} +	} +	 +	return result; +} + +void LLVivoxVoiceClient::leftAudioSession( +	sessionState *session) +{ +	if(mAudioSession == session) +	{ +		switch(getState()) +		{ +			case stateJoiningSession: +			case stateSessionJoined: +			case stateRunning: +			case stateLeavingSession: +			case stateJoinSessionFailed: +			case stateJoinSessionFailedWaiting: +				// normal transition +				LL_DEBUGS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL; +				setState(stateSessionTerminated); +			break; +			 +			case stateSessionTerminated: +				// this will happen sometimes -- there are cases where we send the terminate and then go straight to this state. +				LL_WARNS("Voice") << "left session " << session->mHandle << " in state " << state2string(getState()) << LL_ENDL; +			break; +			 +			default: +				LL_WARNS("Voice") << "unexpected SessionStateChangeEvent (left session) in state " << state2string(getState()) << LL_ENDL; +				setState(stateSessionTerminated); +			break; +		} +	} +} + +void LLVivoxVoiceClient::accountLoginStateChangeEvent( +		std::string &accountHandle,  +		int statusCode,  +		std::string &statusString,  +		int state) +{ +	/* +		According to Mike S., status codes for this event are: +		login_state_logged_out=0, +        login_state_logged_in = 1, +        login_state_logging_in = 2, +        login_state_logging_out = 3, +        login_state_resetting = 4, +        login_state_error=100	 +	*/ +	 +	LL_DEBUGS("Voice") << "state change event: " << state << LL_ENDL; +	switch(state) +	{ +		case 1: +		if(getState() == stateLoggingIn) +		{ +			setState(stateLoggedIn); +		} +		break; + +		case 3: +			// The user is in the process of logging out. +			setState(stateLoggingOut); +		break; + +		case 0: +			// The user has been logged out.   +			setState(stateLoggedOut); +		break; +		 +		default: +			//Used to be a commented out warning +			LL_DEBUGS("Voice") << "unknown state: " << state << LL_ENDL; +		break; +	} +} + +void LLVivoxVoiceClient::mediaStreamUpdatedEvent( +	std::string &sessionHandle,  +	std::string &sessionGroupHandle,  +	int statusCode,  +	std::string &statusString,  +	int state,  +	bool incoming) +{ +	sessionState *session = findSession(sessionHandle); +	 +	LL_DEBUGS("Voice") << "session " << sessionHandle << ", status code " << statusCode << ", string \"" << statusString << "\"" << LL_ENDL; +	 +	if(session) +	{ +		// We know about this session +		 +		// Save the state for later use +		session->mMediaStreamState = state; +		 +		switch(statusCode) +		{ +			case 0: +			case 200: +				// generic success +				// Don't change the saved error code (it may have been set elsewhere) +			break; +			default: +				// save the status code for later +				session->mErrorStatusCode = statusCode; +			break; +		} +		 +		switch(state) +		{ +			case streamStateIdle: +				// Standard "left audio session" +				session->mVoiceEnabled = false; +				session->mMediaConnectInProgress = false; +				leftAudioSession(session); +			break; + +			case streamStateConnected: +				session->mVoiceEnabled = true; +				session->mMediaConnectInProgress = false; +				joinedAudioSession(session); +			break; +			 +			case streamStateRinging: +				if(incoming) +				{ +					// Send the voice chat invite to the GUI layer +					// TODO: Question: Should we correlate with the mute list here? +					session->mIMSessionID = LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID); +					session->mVoiceInvitePending = true; +					if(session->mName.empty()) +					{ +						lookupName(session->mCallerID); +					} +					else +					{ +						// Act like we just finished resolving the name +						avatarNameResolved(session->mCallerID, session->mName); +					} +				} +			break; +			 +			default: +				LL_WARNS("Voice") << "unknown state " << state << LL_ENDL; +			break; +			 +		} +		 +	} +	else +	{ +		LL_WARNS("Voice") << "session " << sessionHandle << "not found"<< LL_ENDL; +	} +} + +void LLVivoxVoiceClient::textStreamUpdatedEvent( +	std::string &sessionHandle,  +	std::string &sessionGroupHandle,  +	bool enabled, +	int state,  +	bool incoming) +{ +	sessionState *session = findSession(sessionHandle); +	 +	if(session) +	{ +		// Save the state for later use +		session->mTextStreamState = state; +		 +		// We know about this session +		switch(state) +		{ +			case 0:	// We see this when the text stream closes +				LL_DEBUGS("Voice") << "stream closed" << LL_ENDL; +			break; +			 +			case 1:	// We see this on an incoming call from the Connector +				// Try to send any text messages queued for this session. +				sendQueuedTextMessages(session); + +				// Send the text chat invite to the GUI layer +				// TODO: Question: Should we correlate with the mute list here? +				session->mTextInvitePending = true; +				if(session->mName.empty()) +				{ +					lookupName(session->mCallerID); +				} +				else +				{ +					// Act like we just finished resolving the name +					avatarNameResolved(session->mCallerID, session->mName); +				} +			break; + +			default: +				LL_WARNS("Voice") << "unknown state " << state << LL_ENDL; +			break; +			 +		} +	} +} + +void LLVivoxVoiceClient::participantAddedEvent( +		std::string &sessionHandle,  +		std::string &sessionGroupHandle,  +		std::string &uriString,  +		std::string &alias,  +		std::string &nameString,  +		std::string &displayNameString,  +		int participantType) +{ +	sessionState *session = findSession(sessionHandle); +	if(session) +	{ +		participantState *participant = session->addParticipant(uriString); +		if(participant) +		{ +			participant->mAccountName = nameString; + +			LL_DEBUGS("Voice") << "added participant \"" << participant->mAccountName  +					<< "\" (" << participant->mAvatarID << ")"<< LL_ENDL; + +			if(participant->mAvatarIDValid) +			{ +				// Initiate a lookup +				lookupName(participant->mAvatarID); +			} +			else +			{ +				// If we don't have a valid avatar UUID, we need to fill in the display name to make the active speakers floater work. +				std::string namePortion = nameFromsipURI(uriString); +				if(namePortion.empty()) +				{ +					// Problem with the SIP URI, fall back to the display name +					namePortion = displayNameString; +				} +				if(namePortion.empty()) +				{ +					// Problems with both of the above, fall back to the account name +					namePortion = nameString; +				} +				 +				// Set the display name (which is a hint to the active speakers window not to do its own lookup) +				participant->mDisplayName = namePortion; +				avatarNameResolved(participant->mAvatarID, namePortion); +			} +		} +	} +} + +void LLVivoxVoiceClient::participantRemovedEvent( +		std::string &sessionHandle,  +		std::string &sessionGroupHandle,  +		std::string &uriString,  +		std::string &alias,  +		std::string &nameString) +{ +	sessionState *session = findSession(sessionHandle); +	if(session) +	{ +		participantState *participant = session->findParticipant(uriString); +		if(participant) +		{ +			session->removeParticipant(participant); +		} +		else +		{ +			LL_DEBUGS("Voice") << "unknown participant " << uriString << LL_ENDL; +		} +	} +	else +	{ +		LL_DEBUGS("Voice") << "unknown session " << sessionHandle << LL_ENDL; +	} +} + + +void LLVivoxVoiceClient::participantUpdatedEvent( +		std::string &sessionHandle,  +		std::string &sessionGroupHandle,  +		std::string &uriString,  +		std::string &alias,  +		bool isModeratorMuted,  +		bool isSpeaking,  +		int volume,  +		F32 energy) +{ +	sessionState *session = findSession(sessionHandle); +	if(session) +	{ +		participantState *participant = session->findParticipant(uriString); +		 +		if(participant) +		{ +			participant->mIsSpeaking = isSpeaking; +			participant->mIsModeratorMuted = isModeratorMuted; + +			// SLIM SDK: convert range: ensure that energy is set to zero if is_speaking is false +			if (isSpeaking) +			{ +				participant->mSpeakingTimeout.reset(); +				participant->mPower = energy; +			} +			else +			{ +				participant->mPower = 0.0f; +			} + +			// *HACK: Minimal hack to fix EXT-6508, ignore the incoming volume if it is zero. +			// This happens because we send volume zero to Vivox when someone is muted, +			// Vivox then send it back to us, overwriting the previous volume. +			// Remove this hack once volume refactoring from EXT-6031 is applied. +			if (volume != 0) +			  { +			    participant->mVolume = volume; +			  } +  +			 +			// *HACK: mantipov: added while working on EXT-3544                                                                                    +			/*                                                                                                                                     +			 Sometimes LLVoiceClient::participantUpdatedEvent callback is called BEFORE                                                             +			 LLViewerChatterBoxSessionAgentListUpdates::post() sometimes AFTER.                                                                     +			  +			 participantUpdatedEvent updates voice participant state in particular participantState::mIsModeratorMuted                              +			 Originally we wanted to update session Speaker Manager to fire LLSpeakerVoiceModerationEvent to fix the EXT-3544 bug.                  +			 Calling of the LLSpeakerMgr::update() method was added into LLIMMgr::processAgentListUpdates.                                          +			  +			 But in case participantUpdatedEvent() is called after LLViewerChatterBoxSessionAgentListUpdates::post()                                +			 voice participant mIsModeratorMuted is changed after speakers are updated in Speaker Manager                                           +			 and event is not fired.                                                                                                                +			  +			 So, we have to call LLSpeakerMgr::update() here. In any case it is better than call it                                                 +			 in LLCallFloater::draw()                                                                                                               +			 */ +			LLVoiceChannel* voice_cnl = LLVoiceChannel::getCurrentVoiceChannel(); +			 +			// ignore session ID of local chat                                                                                                     +			if (voice_cnl && voice_cnl->getSessionID().notNull()) +			{ +				LLSpeakerMgr* speaker_manager = LLIMModel::getInstance()->getSpeakerManager(voice_cnl->getSessionID()); +				if (speaker_manager) +				{ +					speaker_manager->update(true); +				} +			} +			 +		} +		else +		{ +			LL_WARNS("Voice") << "unknown participant: " << uriString << LL_ENDL; +		} +	} +	else +	{ +		LL_INFOS("Voice") << "unknown session " << sessionHandle << LL_ENDL; +	} +} + +void LLVivoxVoiceClient::buddyPresenceEvent( +		std::string &uriString,  +		std::string &alias,  +		std::string &statusString, +		std::string &applicationString) +{ +	buddyListEntry *buddy = findBuddy(uriString); +	 +	if(buddy) +	{ +		LL_DEBUGS("Voice") << "Presence event for " << buddy->mDisplayName << " status \"" << statusString << "\", application \"" << applicationString << "\""<< LL_ENDL; +		LL_DEBUGS("Voice") << "before: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL; + +		if(applicationString.empty()) +		{ +			// This presence event is from a client that doesn't set up the Application string.  Do things the old-skool way. +			// NOTE: this will be needed to support people who aren't on the 3010-class SDK yet. + +			if ( stricmp("Unknown", statusString.c_str())== 0)  +			{ +				// User went offline with a non-SLim-enabled viewer. +				buddy->mOnlineSL = false; +			} +			else if ( stricmp("Online", statusString.c_str())== 0)  +			{ +				// User came online with a non-SLim-enabled viewer. +				buddy->mOnlineSL = true; +			} +			else +			{ +				// If the user is online through SLim, their status will be "Online-slc", "Away", or something else. +				// NOTE: we should never see this unless someone is running an OLD version of SLim -- the versions that should be in use now all set the application string. +				buddy->mOnlineSLim = true; +			}  +		} +		else if(applicationString.find("SecondLifeViewer") != std::string::npos) +		{ +			// This presence event is from a viewer that sets the application string +			if ( stricmp("Unknown", statusString.c_str())== 0)  +			{ +				// Viewer says they're offline +				buddy->mOnlineSL = false; +			} +			else +			{ +				// Viewer says they're online +				buddy->mOnlineSL = true; +			} +		} +		else +		{ +			// This presence event is from something which is NOT the SL viewer (assume it's SLim). +			if ( stricmp("Unknown", statusString.c_str())== 0)  +			{ +				// SLim says they're offline +				buddy->mOnlineSLim = false; +			} +			else +			{ +				// SLim says they're online +				buddy->mOnlineSLim = true; +			} +		}  + +		LL_DEBUGS("Voice") << "after: mOnlineSL = " << (buddy->mOnlineSL?"true":"false") << ", mOnlineSLim = " << (buddy->mOnlineSLim?"true":"false") << LL_ENDL; +		 +		// HACK -- increment the internal change serial number in the LLRelationship (without changing the actual status), so the UI notices the change. +		LLAvatarTracker::instance().setBuddyOnline(buddy->mUUID,LLAvatarTracker::instance().isBuddyOnline(buddy->mUUID)); + +		notifyFriendObservers(); +	} +	else +	{ +		LL_DEBUGS("Voice") << "Presence for unknown buddy " << uriString << LL_ENDL; +	}	 +} + +void LLVivoxVoiceClient::messageEvent( +		std::string &sessionHandle,  +		std::string &uriString,  +		std::string &alias,  +		std::string &messageHeader,  +		std::string &messageBody, +		std::string &applicationString) +{ +	LL_DEBUGS("Voice") << "Message event, session " << sessionHandle << " from " << uriString << LL_ENDL; +//	LL_DEBUGS("Voice") << "    header " << messageHeader << ", body: \n" << messageBody << LL_ENDL; +	 +	if(messageHeader.find("text/html") != std::string::npos) +	{ +		std::string message; + +		{ +			const std::string startMarker = "<body"; +			const std::string startMarker2 = ">"; +			const std::string endMarker = "</body>"; +			const std::string startSpan = "<span"; +			const std::string endSpan = "</span>"; +			std::string::size_type start; +			std::string::size_type end; +			 +			// Default to displaying the raw string, so the message gets through. +			message = messageBody; + +			// Find the actual message text within the XML fragment +			start = messageBody.find(startMarker); +			start = messageBody.find(startMarker2, start); +			end = messageBody.find(endMarker); + +			if(start != std::string::npos) +			{ +				start += startMarker2.size(); +				 +				if(end != std::string::npos) +					end -= start; +					 +				message.assign(messageBody, start, end); +			} +			else  +			{ +				// Didn't find a <body>, try looking for a <span> instead. +				start = messageBody.find(startSpan); +				start = messageBody.find(startMarker2, start); +				end = messageBody.find(endSpan); +				 +				if(start != std::string::npos) +				{ +					start += startMarker2.size(); +					 +					if(end != std::string::npos) +						end -= start; +					 +					message.assign(messageBody, start, end); +				}			 +			} +		}	 +		 +//		LL_DEBUGS("Voice") << "    raw message = \n" << message << LL_ENDL; + +		// strip formatting tags +		{ +			std::string::size_type start; +			std::string::size_type end; +			 +			while((start = message.find('<')) != std::string::npos) +			{ +				if((end = message.find('>', start + 1)) != std::string::npos) +				{ +					// Strip out the tag +					message.erase(start, (end + 1) - start); +				} +				else +				{ +					// Avoid an infinite loop +					break; +				} +			} +		} +		 +		// Decode ampersand-escaped chars +		{ +			std::string::size_type mark = 0; + +			// The text may contain text encoded with <, >, and & +			mark = 0; +			while((mark = message.find("<", mark)) != std::string::npos) +			{ +				message.replace(mark, 4, "<"); +				mark += 1; +			} +			 +			mark = 0; +			while((mark = message.find(">", mark)) != std::string::npos) +			{ +				message.replace(mark, 4, ">"); +				mark += 1; +			} +			 +			mark = 0; +			while((mark = message.find("&", mark)) != std::string::npos) +			{ +				message.replace(mark, 5, "&"); +				mark += 1; +			} +		} +		 +		// strip leading/trailing whitespace (since we always seem to get a couple newlines) +		LLStringUtil::trim(message); +		 +//		LL_DEBUGS("Voice") << "    stripped message = \n" << message << LL_ENDL; +		 +		sessionState *session = findSession(sessionHandle); +		if(session) +		{ +			bool is_busy = gAgent.getBusy(); +			bool is_muted = LLMuteList::getInstance()->isMuted(session->mCallerID, session->mName, LLMute::flagTextChat); +			bool is_linden = LLMuteList::getInstance()->isLinden(session->mName); +			bool quiet_chat = false; +			LLChat chat; + +			chat.mMuted = is_muted && !is_linden; +			 +			if(!chat.mMuted) +			{ +				chat.mFromID = session->mCallerID; +				chat.mFromName = session->mName; +				chat.mSourceType = CHAT_SOURCE_AGENT; + +				if(is_busy && !is_linden) +				{ +					quiet_chat = true; +					// TODO: Question: Return busy mode response here?  Or maybe when session is started instead? +				} +				 +				LL_DEBUGS("Voice") << "adding message, name " << session->mName << " session " << session->mIMSessionID << ", target " << session->mCallerID << LL_ENDL; +				gIMMgr->addMessage(session->mIMSessionID, +						session->mCallerID, +						session->mName.c_str(), +						message.c_str(), +						LLStringUtil::null,		// default arg +						IM_NOTHING_SPECIAL,		// default arg +						0,						// default arg +						LLUUID::null,			// default arg +						LLVector3::zero,		// default arg +						true);					// prepend name and make it a link to the user's profile + +			} +		}		 +	} +} + +void LLVivoxVoiceClient::sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType) +{ +	sessionState *session = findSession(sessionHandle); +	 +	if(session) +	{ +		participantState *participant = session->findParticipant(uriString); +		if(participant) +		{ +			if (!stricmp(notificationType.c_str(), "Typing")) +			{ +				// Other end started typing +				// TODO: The proper way to add a typing notification seems to be LLIMMgr::processIMTypingStart(). +				// It requires an LLIMInfo for the message, which we don't have here. +			} +			else if (!stricmp(notificationType.c_str(), "NotTyping")) +			{ +				// Other end stopped typing +				// TODO: The proper way to remove a typing notification seems to be LLIMMgr::processIMTypingStop(). +				// It requires an LLIMInfo for the message, which we don't have here. +			} +			else +			{ +				LL_DEBUGS("Voice") << "Unknown notification type " << notificationType << "for participant " << uriString << " in session " << session->mSIPURI << LL_ENDL; +			} +		} +		else +		{ +			LL_DEBUGS("Voice") << "Unknown participant " << uriString << " in session " << session->mSIPURI << LL_ENDL; +		} +	} +	else +	{ +		LL_DEBUGS("Voice") << "Unknown session handle " << sessionHandle << LL_ENDL; +	} +} + +void LLVivoxVoiceClient::subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType) +{ +	buddyListEntry *buddy = findBuddy(buddyURI); +	 +	if(!buddy) +	{ +		// Couldn't find buddy by URI, try converting the alias... +		if(!alias.empty()) +		{ +			LLUUID id; +			if(IDFromName(alias, id)) +			{ +				buddy = findBuddy(id); +			} +		} +	} +	 +	if(buddy) +	{ +		std::ostringstream stream; +		 +		if(buddy->mCanSeeMeOnline) +		{ +			// Sending the response will create an auto-accept rule +			buddy->mHasAutoAcceptListEntry = true; +		} +		else +		{ +			// Sending the response will create a block rule +			buddy->mHasBlockListEntry = true; +		} +		 +		if(buddy->mInSLFriends) +		{ +			buddy->mInVivoxBuddies = true; +		} +		 +		stream +			<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.SendSubscriptionReply.1\">" +				<< "<AccountHandle>" << mAccountHandle << "</AccountHandle>" +				<< "<BuddyURI>" << buddy->mURI << "</BuddyURI>" +				<< "<RuleType>" << (buddy->mCanSeeMeOnline?"Allow":"Hide") << "</RuleType>" +				<< "<AutoAccept>"<< (buddy->mInSLFriends?"1":"0")<< "</AutoAccept>" +				<< "<SubscriptionHandle>" << subscriptionHandle << "</SubscriptionHandle>" +			<< "</Request>" +			<< "\n\n\n"; +			 +		writeString(stream.str()); +	} +} + +void LLVivoxVoiceClient::auxAudioPropertiesEvent(F32 energy) +{ +	LL_DEBUGS("Voice") << "got energy " << energy << LL_ENDL; +	mTuningEnergy = energy; +} + +void LLVivoxVoiceClient::buddyListChanged() +{ +	// This is called after we receive a BuddyAndGroupListChangedEvent. +	mBuddyListMapPopulated = true; +	mFriendsListDirty = true; +} + +void LLVivoxVoiceClient::muteListChanged() +{ +	// The user's mute list has been updated.  Go through the current participant list and sync it with the mute list. +	if(mAudioSession) +	{ +		participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin(); +		 +		for(; iter != mAudioSession->mParticipantsByURI.end(); iter++) +		{ +			participantState *p = iter->second; +			 +			// Check to see if this participant is on the mute list already +			if(p->updateMuteState()) +				mAudioSession->mVolumeDirty = true; +		} +	} +} + +void LLVivoxVoiceClient::updateFriends(U32 mask) +{ +	if(mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::POWERS)) +	{ +		// Just resend the whole friend list to the daemon +		mFriendsListDirty = true; +	} +} + +///////////////////////////// +// Managing list of participants +LLVivoxVoiceClient::participantState::participantState(const std::string &uri) :  +	 mURI(uri),  +	 mPTT(false),  +	 mIsSpeaking(false),  +	 mIsModeratorMuted(false),  +	 mLastSpokeTimestamp(0.f),  +	 mPower(0.f),  +	 mVolume(-1),  +	 mOnMuteList(false),  +	 mUserVolume(-1),  +	 mVolumeDirty(false),  +	 mAvatarIDValid(false), +	 mIsSelf(false) +{ +} + +LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::addParticipant(const std::string &uri) +{ +	participantState *result = NULL; +	bool useAlternateURI = false; +	 +	// Note: this is mostly the body of LLVivoxVoiceClient::sessionState::findParticipant(), but since we need to know if it +	// matched the alternate SIP URI (so we can add it properly), we need to reproduce it here. +	{ +		participantMap::iterator iter = mParticipantsByURI.find(uri); + +		if(iter == mParticipantsByURI.end()) +		{ +			if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI)) +			{ +				// This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant. +				// Use mSIPURI instead, since it will be properly encoded. +				iter = mParticipantsByURI.find(mSIPURI); +				useAlternateURI = true; +			} +		} + +		if(iter != mParticipantsByURI.end()) +		{ +			result = iter->second; +		} +	} +		 +	if(!result) +	{ +		// participant isn't already in one list or the other. +		result = new participantState(useAlternateURI?mSIPURI:uri); +		mParticipantsByURI.insert(participantMap::value_type(result->mURI, result)); +		mParticipantsChanged = true; +		 +		// Try to do a reverse transform on the URI to get the GUID back. +		{ +			LLUUID id; +			if(LLVivoxVoiceClient::getInstance()->IDFromName(result->mURI, id)) +			{ +				result->mAvatarIDValid = true; +				result->mAvatarID = id; + +				if(result->updateMuteState()) +					mVolumeDirty = true; +			} +			else +			{ +				// Create a UUID by hashing the URI, but do NOT set mAvatarIDValid. +				// This tells both code in LLVivoxVoiceClient and code in llfloateractivespeakers.cpp that the ID will not be in the name cache. +				setUUIDFromStringHash(result->mAvatarID, uri); +			} +		} +		 +		mParticipantsByUUID.insert(participantUUIDMap::value_type(result->mAvatarID, result)); + +		result->mUserVolume = LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID); +		 +		LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL; +	} +	 +	return result; +} + +bool LLVivoxVoiceClient::participantState::updateMuteState() +{ +	bool result = false; +	 +	if(mAvatarIDValid) +	{ +		bool isMuted = LLMuteList::getInstance()->isMuted(mAvatarID, LLMute::flagVoiceChat); +		if(mOnMuteList != isMuted) +		{ +			mOnMuteList = isMuted; +			mVolumeDirty = true; +			result = true; +		} +	} +	return result; +} + +bool LLVivoxVoiceClient::participantState::isAvatar() +{ +	return mAvatarIDValid; +} + +void LLVivoxVoiceClient::sessionState::removeParticipant(LLVivoxVoiceClient::participantState *participant) +{ +	if(participant) +	{ +		participantMap::iterator iter = mParticipantsByURI.find(participant->mURI); +		participantUUIDMap::iterator iter2 = mParticipantsByUUID.find(participant->mAvatarID); +		 +		LL_DEBUGS("Voice") << "participant \"" << participant->mURI <<  "\" (" << participant->mAvatarID << ") removed." << LL_ENDL; +		 +		if(iter == mParticipantsByURI.end()) +		{ +			LL_ERRS("Voice") << "Internal error: participant " << participant->mURI << " not in URI map" << LL_ENDL; +		} +		else if(iter2 == mParticipantsByUUID.end()) +		{ +			LL_ERRS("Voice") << "Internal error: participant ID " << participant->mAvatarID << " not in UUID map" << LL_ENDL; +		} +		else if(iter->second != iter2->second) +		{ +			LL_ERRS("Voice") << "Internal error: participant mismatch!" << LL_ENDL; +		} +		else +		{ +			mParticipantsByURI.erase(iter); +			mParticipantsByUUID.erase(iter2); +			 +			delete participant; +			mParticipantsChanged = true; +		} +	} +} + +void LLVivoxVoiceClient::sessionState::removeAllParticipants() +{ +	LL_DEBUGS("Voice") << "called" << LL_ENDL; + +	while(!mParticipantsByURI.empty()) +	{ +		removeParticipant(mParticipantsByURI.begin()->second); +	} +	 +	if(!mParticipantsByUUID.empty()) +	{ +		LL_ERRS("Voice") << "Internal error: empty URI map, non-empty UUID map" << LL_ENDL; +	} +} + +void LLVivoxVoiceClient::getParticipantList(std::set<LLUUID> &participants) +{ +	if(mAudioSession) +	{ +		for(participantUUIDMap::iterator iter = mAudioSession->mParticipantsByUUID.begin(); +			iter != mAudioSession->mParticipantsByUUID.end();  +			iter++) +		{ +			participants.insert(iter->first); +		} +	} +} + +bool LLVivoxVoiceClient::isParticipant(const LLUUID &speaker_id) +{ +  if(mAudioSession) +    { +      return (mAudioSession->mParticipantsByUUID.find(speaker_id) != mAudioSession->mParticipantsByUUID.end()); +    } +  return false; +} + + +LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::findParticipant(const std::string &uri) +{ +	participantState *result = NULL; +	 +	participantMap::iterator iter = mParticipantsByURI.find(uri); + +	if(iter == mParticipantsByURI.end()) +	{ +		if(!mAlternateSIPURI.empty() && (uri == mAlternateSIPURI)) +		{ +			// This is a p2p session (probably with the SLIM client) with an alternate URI for the other participant. +			// Look up the other URI +			iter = mParticipantsByURI.find(mSIPURI); +		} +	} + +	if(iter != mParticipantsByURI.end()) +	{ +		result = iter->second; +	} +		 +	return result; +} + +LLVivoxVoiceClient::participantState* LLVivoxVoiceClient::sessionState::findParticipantByID(const LLUUID& id) +{ +	participantState * result = NULL; +	participantUUIDMap::iterator iter = mParticipantsByUUID.find(id); + +	if(iter != mParticipantsByUUID.end()) +	{ +		result = iter->second; +	} + +	return result; +} + +LLVivoxVoiceClient::participantState* LLVivoxVoiceClient::findParticipantByID(const LLUUID& id) +{ +	participantState * result = NULL; +	 +	if(mAudioSession) +	{ +		result = mAudioSession->findParticipantByID(id); +	} +	 +	return result; +} + + +void LLVivoxVoiceClient::parcelChanged() +{ +	if(getState() >= stateNoChannel) +	{ +		// If the user is logged in, start a channel lookup. +		LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL; + +		std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest"); +		LLSD data; +		LLHTTPClient::post( +			url, +			data, +			new LLVivoxVoiceClientCapResponder); +	} +	else +	{ +		// The transition to stateNoChannel needs to kick this off again. +		LL_INFOS("Voice") << "not logged in yet, deferring" << LL_ENDL; +	} +} + +void LLVivoxVoiceClient::switchChannel( +	std::string uri, +	bool spatial, +	bool no_reconnect, +	bool is_p2p, +	std::string hash) +{ +	bool needsSwitch = false; +	 +	LL_DEBUGS("Voice")  +		<< "called in state " << state2string(getState())  +		<< " with uri \"" << uri << "\""  +		<< (spatial?", spatial is true":", spatial is false") +		<< LL_ENDL; +	 +	switch(getState()) +	{ +		case stateJoinSessionFailed: +		case stateJoinSessionFailedWaiting: +		case stateNoChannel: +			// Always switch to the new URI from these states. +			needsSwitch = true; +		break; + +		default: +			if(mSessionTerminateRequested) +			{ +				// If a terminate has been requested, we need to compare against where the URI we're already headed to. +				if(mNextAudioSession) +				{ +					if(mNextAudioSession->mSIPURI != uri) +						needsSwitch = true; +				} +				else +				{ +					// mNextAudioSession is null -- this probably means we're on our way back to spatial. +					if(!uri.empty()) +					{ +						// We do want to process a switch in this case. +						needsSwitch = true; +					} +				} +			} +			else +			{ +				// Otherwise, compare against the URI we're in now. +				if(mAudioSession) +				{ +					if(mAudioSession->mSIPURI != uri) +					{ +						needsSwitch = true; +					} +				} +				else +				{ +					if(!uri.empty()) +					{ +						// mAudioSession is null -- it's not clear what case would cause this. +						// For now, log it as a warning and see if it ever crops up. +						LL_WARNS("Voice") << "No current audio session." << LL_ENDL; +					} +				} +			} +		break; +	} +	 +	if(needsSwitch) +	{ +		if(uri.empty()) +		{ +			// Leave any channel we may be in +			LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL; + +			sessionState *oldSession = mNextAudioSession; +			mNextAudioSession = NULL; + +			// The old session may now need to be deleted. +			reapSession(oldSession); + +			notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED); +		} +		else +		{ +			LL_DEBUGS("Voice") << "switching to channel " << uri << LL_ENDL; + +			mNextAudioSession = addSession(uri); +			mNextAudioSession->mHash = hash; +			mNextAudioSession->mIsSpatial = spatial; +			mNextAudioSession->mReconnect = !no_reconnect; +			mNextAudioSession->mIsP2P = is_p2p; +		} +		 +		if(getState() <= stateNoChannel) +		{ +			// We're already set up to join a channel, just needed to fill in the session URI +		} +		else +		{ +			// State machine will come around and rejoin if uri/handle is not empty. +			sessionTerminate(); +		} +	} +} + +void LLVivoxVoiceClient::joinSession(sessionState *session) +{ +	mNextAudioSession = session; +	 +	if(getState() <= stateNoChannel) +	{ +		// We're already set up to join a channel, just needed to fill in the session handle +	} +	else +	{ +		// State machine will come around and rejoin if uri/handle is not empty. +		sessionTerminate(); +	} +} + +void LLVivoxVoiceClient::setNonSpatialChannel( +	const std::string &uri, +	const std::string &credentials) +{ +	switchChannel(uri, false, false, false, credentials); +} + +void LLVivoxVoiceClient::setSpatialChannel( +	const std::string &uri, +	const std::string &credentials) +{ +	mSpatialSessionURI = uri; +	mSpatialSessionCredentials = credentials; +	mAreaVoiceDisabled = mSpatialSessionURI.empty(); + +	LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL; +	 +	if((mAudioSession && !(mAudioSession->mIsSpatial)) || (mNextAudioSession && !(mNextAudioSession->mIsSpatial))) +	{ +		// User is in a non-spatial chat or joining a non-spatial chat.  Don't switch channels. +		LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL; +	} +	else +	{ +		switchChannel(mSpatialSessionURI, true, false, false, mSpatialSessionCredentials); +	} +} + +void LLVivoxVoiceClient::callUser(const LLUUID &uuid) +{ +	std::string userURI = sipURIFromID(uuid); + +	switchChannel(userURI, false, true, true); +} + +LLVivoxVoiceClient::sessionState* LLVivoxVoiceClient::startUserIMSession(const LLUUID &uuid) +{ +	// Figure out if a session with the user already exists +	sessionState *session = findSession(uuid); +	if(!session) +	{ +		// No session with user, need to start one. +		std::string uri = sipURIFromID(uuid); +		session = addSession(uri); + +		llassert(session); +		if (!session) return NULL; + +		session->mIsSpatial = false; +		session->mReconnect = false;	 +		session->mIsP2P = true; +		session->mCallerID = uuid; +	} +	 +	if(session->mHandle.empty()) +	  { +	    // Session isn't active -- start it up. +	    sessionCreateSendMessage(session, false, true); +	  } +	else +	  {	 +	    // Session is already active -- start up text. +	    sessionTextConnectSendMessage(session); +	  } +	 +	return session; +} + +BOOL LLVivoxVoiceClient::sendTextMessage(const LLUUID& participant_id, const std::string& message) +{ +	bool result = false; + +	// Attempt to locate the indicated session +	sessionState *session = startUserIMSession(participant_id); +	if(session) +	{ +		// found the session, attempt to send the message +		session->mTextMsgQueue.push(message); +		 +		// Try to send queued messages (will do nothing if the session is not open yet) +		sendQueuedTextMessages(session); + +		// The message is queued, so we succeed. +		result = true; +	}	 +	else +	{ +		LL_DEBUGS("Voice") << "Session not found for participant ID " << participant_id << LL_ENDL; +	} +	 +	return result; +} + +void LLVivoxVoiceClient::sendQueuedTextMessages(sessionState *session) +{ +	if(session->mTextStreamState == 1) +	{ +		if(!session->mTextMsgQueue.empty()) +		{ +			std::ostringstream stream; +			 +			while(!session->mTextMsgQueue.empty()) +			{ +				std::string message = session->mTextMsgQueue.front(); +				session->mTextMsgQueue.pop(); +				stream +				<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SendMessage.1\">" +					<< "<SessionHandle>" << session->mHandle << "</SessionHandle>" +					<< "<MessageHeader>text/HTML</MessageHeader>" +					<< "<MessageBody>" << message << "</MessageBody>" +				<< "</Request>" +				<< "\n\n\n"; +			}		 +			writeString(stream.str()); +		} +	} +	else +	{ +		// Session isn't connected yet, defer until later. +	} +} + +void LLVivoxVoiceClient::endUserIMSession(const LLUUID &uuid) +{ +	// Figure out if a session with the user exists +	sessionState *session = findSession(uuid); +	if(session) +	{ +		// found the session +		if(!session->mHandle.empty()) +		{ +			sessionTextDisconnectSendMessage(session); +		} +	}	 +	else +	{ +		LL_DEBUGS("Voice") << "Session not found for participant ID " << uuid << LL_ENDL; +	} +} + +bool LLVivoxVoiceClient::answerInvite(std::string &sessionHandle) +{ +	// this is only ever used to answer incoming p2p call invites. +	 +	sessionState *session = findSession(sessionHandle); +	if(session) +	{ +		session->mIsSpatial = false; +		session->mReconnect = false;	 +		session->mIsP2P = true; + +		joinSession(session); +		return true; +	} +	 +	return false; +} + +BOOL LLVivoxVoiceClient::isOnlineSIP(const LLUUID &id) +{ +	bool result = false; +	buddyListEntry *buddy = findBuddy(id); +	if(buddy) +	{ +		result = buddy->mOnlineSLim; +		LL_DEBUGS("Voice") << "Buddy " << buddy->mDisplayName << " is SIP " << (result?"online":"offline") << LL_ENDL; +	} + +	if(!result) +	{ +		// This user isn't on the buddy list or doesn't show online status through the buddy list, but could be a participant in an existing session if they initiated a text IM. +		sessionState *session = findSession(id); +		if(session && !session->mHandle.empty()) +		{ +			if((session->mTextStreamState != streamStateUnknown) || (session->mMediaStreamState > streamStateIdle)) +			{ +				LL_DEBUGS("Voice") << "Open session with " << id << " found, returning SIP online state" << LL_ENDL; +				// we have a p2p text session open with this user, so by definition they're online. +				result = true; +			} +		} +	} +	 +	return result; +} + +bool LLVivoxVoiceClient::isVoiceWorking() +{ +  //Added stateSessionTerminated state to avoid problems with call in parcels with disabled voice (EXT-4758) +  // Condition with joining spatial num was added to take into account possible problems with connection to voice +  // server(EXT-4313). See bug descriptions and comments for MAX_NORMAL_JOINING_SPATIAL_NUM for more info. +  return (mSpatialJoiningNum < MAX_NORMAL_JOINING_SPATIAL_NUM) && (stateLoggedIn <= mState) && (mState <= stateSessionTerminated); +} + +// Returns true if the indicated participant in the current audio session is really an SL avatar. +// Currently this will be false only for PSTN callers into group chats, and PSTN p2p calls. +BOOL LLVivoxVoiceClient::isParticipantAvatar(const LLUUID &id) +{ +	BOOL result = TRUE;  +	sessionState *session = findSession(id); +	 +	if(session != NULL) +	{ +		// this is a p2p session with the indicated caller, or the session with the specified UUID. +		if(session->mSynthesizedCallerID) +			result = FALSE; +	} +	else +	{ +		// Didn't find a matching session -- check the current audio session for a matching participant +		if(mAudioSession != NULL) +		{ +			participantState *participant = findParticipantByID(id); +			if(participant != NULL) +			{ +				result = participant->isAvatar(); +			} +		} +	} +	 +	return result; +} + +// Returns true if calling back the session URI after the session has closed is possible. +// Currently this will be false only for PSTN P2P calls.		 +BOOL LLVivoxVoiceClient::isSessionCallBackPossible(const LLUUID &session_id) +{ +	BOOL result = TRUE;  +	sessionState *session = findSession(session_id); +	 +	if(session != NULL) +	{ +		result = session->isCallBackPossible(); +	} +	 +	return result; +} + +// Returns true if the session can accepte text IM's. +// Currently this will be false only for PSTN P2P calls. +BOOL LLVivoxVoiceClient::isSessionTextIMPossible(const LLUUID &session_id) +{ +	bool result = TRUE;  +	sessionState *session = findSession(session_id); +	 +	if(session != NULL) +	{ +		result = session->isTextIMPossible(); +	} +	 +	return result; +} +		 + +void LLVivoxVoiceClient::declineInvite(std::string &sessionHandle) +{ +	sessionState *session = findSession(sessionHandle); +	if(session) +	{ +		sessionMediaDisconnectSendMessage(session); +	} +} + +void LLVivoxVoiceClient::leaveNonSpatialChannel() +{ +	LL_DEBUGS("Voice")  +		<< "called in state " << state2string(getState())  +		<< LL_ENDL; +	 +	// Make sure we don't rejoin the current session.	 +	sessionState *oldNextSession = mNextAudioSession; +	mNextAudioSession = NULL; +	 +	// Most likely this will still be the current session at this point, but check it anyway. +	reapSession(oldNextSession); +	 +	verifySessionState(); +	 +	sessionTerminate(); +} + +std::string LLVivoxVoiceClient::getCurrentChannel() +{ +	std::string result; +	 +	if((getState() == stateRunning) && !mSessionTerminateRequested) +	{ +		result = getAudioSessionURI(); +	} +	 +	return result; +} + +bool LLVivoxVoiceClient::inProximalChannel() +{ +	bool result = false; +	 +	if((getState() == stateRunning) && !mSessionTerminateRequested) +	{ +		result = inSpatialChannel(); +	} +	 +	return result; +} + +std::string LLVivoxVoiceClient::sipURIFromID(const LLUUID &id) +{ +	std::string result; +	result = "sip:"; +	result += nameFromID(id); +	result += "@"; +	result += mVoiceSIPURIHostName; +	 +	return result; +} + +std::string LLVivoxVoiceClient::sipURIFromAvatar(LLVOAvatar *avatar) +{ +	std::string result; +	if(avatar) +	{ +		result = "sip:"; +		result += nameFromID(avatar->getID()); +		result += "@"; +		result += mVoiceSIPURIHostName; +	} +	 +	return result; +} + +std::string LLVivoxVoiceClient::nameFromAvatar(LLVOAvatar *avatar) +{ +	std::string result; +	if(avatar) +	{ +		result = nameFromID(avatar->getID()); +	}	 +	return result; +} + +std::string LLVivoxVoiceClient::nameFromID(const LLUUID &uuid) +{ +	std::string result; +	 +	if (uuid.isNull()) { +		//VIVOX, the uuid emtpy look for the mURIString and return that instead. +		//result.assign(uuid.mURIStringName); +		LLStringUtil::replaceChar(result, '_', ' '); +		return result; +	} +	// Prepending this apparently prevents conflicts with reserved names inside the vivox and diamondware code. +	result = "x"; +	 +	// Base64 encode and replace the pieces of base64 that are less compatible  +	// with e-mail local-parts. +	// See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet" +	result += LLBase64::encode(uuid.mData, UUID_BYTES); +	LLStringUtil::replaceChar(result, '+', '-'); +	LLStringUtil::replaceChar(result, '/', '_'); +	 +	// If you need to transform a GUID to this form on the Mac OS X command line, this will do so: +	// echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-') +	 +	// The reverse transform can be done with: +	// echo 'x5mkTKmxDTuGnjWyC__WfMg==' |cut -b 2- -|tr '_-' '/+' |openssl base64 -d|xxd -p +	 +	return result; +} + +bool LLVivoxVoiceClient::IDFromName(const std::string inName, LLUUID &uuid) +{ +	bool result = false; +	 +	// SLIM SDK: The "name" may actually be a SIP URI such as: "sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com" +	// If it is, convert to a bare name before doing the transform. +	std::string name = nameFromsipURI(inName); +	 +	// Doesn't look like a SIP URI, assume it's an actual name. +	if(name.empty()) +		name = inName; + +	// This will only work if the name is of the proper form. +	// As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is: +	// "xFnPP04IpREWNkuw1cOXlhw==" +	 +	if((name.size() == 25) && (name[0] == 'x') && (name[23] == '=') && (name[24] == '=')) +	{ +		// The name appears to have the right form. + +		// Reverse the transforms done by nameFromID +		std::string temp = name; +		LLStringUtil::replaceChar(temp, '-', '+'); +		LLStringUtil::replaceChar(temp, '_', '/'); + +		U8 rawuuid[UUID_BYTES + 1];  +		int len = apr_base64_decode_binary(rawuuid, temp.c_str() + 1); +		if(len == UUID_BYTES) +		{ +			// The decode succeeded.  Stuff the bits into the result's UUID +			memcpy(uuid.mData, rawuuid, UUID_BYTES); +			result = true; +		} +	}  +	 +	if(!result) +	{ +		// VIVOX:  not a standard account name, just copy the URI name mURIString field +		// and hope for the best.  bpj +		uuid.setNull();  // VIVOX, set the uuid field to nulls +	} +	 +	return result; +} + +std::string LLVivoxVoiceClient::displayNameFromAvatar(LLVOAvatar *avatar) +{ +	return avatar->getFullname(); +} + +std::string LLVivoxVoiceClient::sipURIFromName(std::string &name) +{ +	std::string result; +	result = "sip:"; +	result += name; +	result += "@"; +	result += mVoiceSIPURIHostName; + +//	LLStringUtil::toLower(result); + +	return result; +} + +std::string LLVivoxVoiceClient::nameFromsipURI(const std::string &uri) +{ +	std::string result; + +	std::string::size_type sipOffset, atOffset; +	sipOffset = uri.find("sip:"); +	atOffset = uri.find("@"); +	if((sipOffset != std::string::npos) && (atOffset != std::string::npos)) +	{ +		result = uri.substr(sipOffset + 4, atOffset - (sipOffset + 4)); +	} +	 +	return result; +} + +bool LLVivoxVoiceClient::inSpatialChannel(void) +{ +	bool result = false; +	 +	if(mAudioSession) +		result = mAudioSession->mIsSpatial; +		 +	return result; +} + +std::string LLVivoxVoiceClient::getAudioSessionURI() +{ +	std::string result; +	 +	if(mAudioSession) +		result = mAudioSession->mSIPURI; +		 +	return result; +} + +std::string LLVivoxVoiceClient::getAudioSessionHandle() +{ +	std::string result; +	 +	if(mAudioSession) +		result = mAudioSession->mHandle; +		 +	return result; +} + + +///////////////////////////// +// Sending updates of current state + +void LLVivoxVoiceClient::enforceTether(void) +{ +	LLVector3d tethered	= mCameraRequestedPosition; + +	// constrain 'tethered' to within 50m of mAvatarPosition. +	{ +		F32 max_dist = 50.0f; +		LLVector3d camera_offset = mCameraRequestedPosition - mAvatarPosition; +		F32 camera_distance = (F32)camera_offset.magVec(); +		if(camera_distance > max_dist) +		{ +			tethered = mAvatarPosition +  +				(max_dist / camera_distance) * camera_offset; +		} +	} +	 +	if(dist_vec(mCameraPosition, tethered) > 0.1) +	{ +		mCameraPosition = tethered; +		mSpatialCoordsDirty = true; +	} +} + +void LLVivoxVoiceClient::updatePosition(void) +{ +	 +	LLViewerRegion *region = gAgent.getRegion(); +	if(region && isAgentAvatarValid()) +	{ +		LLMatrix3 rot; +		LLVector3d pos; +		 +		// TODO: If camera and avatar velocity are actually used by the voice system, we could compute them here... +		// They're currently always set to zero. +		 +		// Send the current camera position to the voice code +		rot.setRows(LLViewerCamera::getInstance()->getAtAxis(), LLViewerCamera::getInstance()->getLeftAxis (),  LLViewerCamera::getInstance()->getUpAxis());		 +		pos = gAgent.getRegion()->getPosGlobalFromRegion(LLViewerCamera::getInstance()->getOrigin()); +		 +		LLVivoxVoiceClient::getInstance()->setCameraPosition( +															 pos,				// position +															 LLVector3::zero, 	// velocity +															 rot);				// rotation matrix +		 +		// Send the current avatar position to the voice code +		rot = gAgentAvatarp->getRootJoint()->getWorldRotation().getMatrix3(); +		pos = gAgentAvatarp->getPositionGlobal(); + +		// TODO: Can we get the head offset from outside the LLVOAvatar? +		//			pos += LLVector3d(mHeadOffset); +		pos += LLVector3d(0.f, 0.f, 1.f); +		 +		LLVivoxVoiceClient::getInstance()->setAvatarPosition( +															 pos,				// position +															 LLVector3::zero, 	// velocity +															 rot);				// rotation matrix +	} +} + +void LLVivoxVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot) +{ +	mCameraRequestedPosition = position; +	 +	if(mCameraVelocity != velocity) +	{ +		mCameraVelocity = velocity; +		mSpatialCoordsDirty = true; +	} +	 +	if(mCameraRot != rot) +	{ +		mCameraRot = rot; +		mSpatialCoordsDirty = true; +	} +} + +void LLVivoxVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot) +{ +	if(dist_vec(mAvatarPosition, position) > 0.1) +	{ +		mAvatarPosition = position; +		mSpatialCoordsDirty = true; +	} +	 +	if(mAvatarVelocity != velocity) +	{ +		mAvatarVelocity = velocity; +		mSpatialCoordsDirty = true; +	} +	 +	if(mAvatarRot != rot) +	{ +		mAvatarRot = rot; +		mSpatialCoordsDirty = true; +	} +} + +bool LLVivoxVoiceClient::channelFromRegion(LLViewerRegion *region, std::string &name) +{ +	bool result = false; +	 +	if(region) +	{ +		name = region->getName(); +	} +	 +	if(!name.empty()) +		result = true; +	 +	return result; +} + +void LLVivoxVoiceClient::leaveChannel(void) +{ +	if(getState() == stateRunning) +	{ +		LL_DEBUGS("Voice") << "leaving channel for teleport/logout" << LL_ENDL; +		mChannelName.clear(); +		sessionTerminate(); +	} +} + +void LLVivoxVoiceClient::setMuteMic(bool muted) +{ +	mMuteMic = muted; +} + +void LLVivoxVoiceClient::setUserPTTState(bool ptt) +{ +	mUserPTTState = ptt; +} + +bool LLVivoxVoiceClient::getUserPTTState() +{ +	return mUserPTTState; +} + +void LLVivoxVoiceClient::inputUserControlState(bool down) +{ +	if(mPTTIsToggle) +	{ +		if(down) // toggle open-mic state on 'down'                                                         +		{ +			toggleUserPTTState(); +		} +	} +	else // set open-mic state as an absolute                                                                   +	{ +		setUserPTTState(down); +	} +} + + +void LLVivoxVoiceClient::toggleUserPTTState(void) +{ +	mUserPTTState = !mUserPTTState; +} + +void LLVivoxVoiceClient::setVoiceEnabled(bool enabled) +{ +	if (enabled != mVoiceEnabled) +	{ +		// TODO: Refactor this so we don't call into LLVoiceChannel, but simply +		// use the status observer +		mVoiceEnabled = enabled; +		LLVoiceClientStatusObserver::EStatusType status; +		 +		 +		if (enabled) +		{ +			LLVoiceChannel::getCurrentVoiceChannel()->activate(); +			status = LLVoiceClientStatusObserver::STATUS_VOICE_ENABLED; +		} +		else +		{ +			// Turning voice off looses your current channel -- this makes sure the UI isn't out of sync when you re-enable it. +			LLVoiceChannel::getCurrentVoiceChannel()->deactivate(); +			status = LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED; +		} +	} +} + +bool LLVivoxVoiceClient::voiceEnabled() +{ +	return gSavedSettings.getBOOL("EnableVoiceChat") && !gSavedSettings.getBOOL("CmdLineDisableVoice"); +} + +void LLVivoxVoiceClient::setLipSyncEnabled(BOOL enabled) +{ +	mLipSyncEnabled = enabled; +} + +BOOL LLVivoxVoiceClient::lipSyncEnabled() +{ +	    +	if ( mVoiceEnabled && stateDisabled != getState() ) +	{ +		return mLipSyncEnabled; +	} +	else +	{ +		return FALSE; +	} +} + +void LLVivoxVoiceClient::setUsePTT(bool usePTT) +{ +	if(usePTT && !mUsePTT) +	{ +		// When the user turns on PTT, reset the current state. +		mUserPTTState = false; +	} +	mUsePTT = usePTT; +} + +void LLVivoxVoiceClient::setPTTIsToggle(bool PTTIsToggle) +{ +	if(!PTTIsToggle && mPTTIsToggle) +	{ +		// When the user turns off toggle, reset the current state. +		mUserPTTState = false; +	} +	 +	mPTTIsToggle = PTTIsToggle; +} + +bool LLVivoxVoiceClient::getPTTIsToggle() +{ +	return mPTTIsToggle; +} + +void LLVivoxVoiceClient::setPTTKey(std::string &key) +{ +	if(key == "MiddleMouse") +	{ +		mPTTIsMiddleMouse = true; +	} +	else +	{ +		mPTTIsMiddleMouse = false; +		if(!LLKeyboard::keyFromString(key, &mPTTKey)) +		{ +			// If the call failed, don't match any key. +			key = KEY_NONE; +		} +	} +} + +void LLVivoxVoiceClient::setEarLocation(S32 loc) +{ +	if(mEarLocation != loc) +	{ +		LL_DEBUGS("Voice") << "Setting mEarLocation to " << loc << LL_ENDL; +		 +		mEarLocation = loc; +		mSpatialCoordsDirty = true; +	} +} + +void LLVivoxVoiceClient::setVoiceVolume(F32 volume) +{ +	int scaled_volume = scale_speaker_volume(volume);	 + +	if(scaled_volume != mSpeakerVolume) +	{ +	  int min_volume = scale_speaker_volume(0); +		if((scaled_volume == min_volume) || (mSpeakerVolume == min_volume)) +		{ +			mSpeakerMuteDirty = true; +		} + +		mSpeakerVolume = scaled_volume; +		mSpeakerVolumeDirty = true; +	} +} + +void LLVivoxVoiceClient::setMicGain(F32 volume) +{ +	int scaled_volume = scale_mic_volume(volume); +	 +	if(scaled_volume != mMicVolume) +	{ +		mMicVolume = scaled_volume; +		mMicVolumeDirty = true; +	} +} + +void LLVivoxVoiceClient::keyDown(KEY key, MASK mask) +{	 +	if (gKeyboard->getKeyRepeated(key)) +	{ +		// ignore auto-repeat keys                                                                          +		return; +	} +	 +	if(!mPTTIsMiddleMouse) +	{ +		bool down = (mPTTKey != KEY_NONE) +		&& gKeyboard->getKeyDown(mPTTKey); +		inputUserControlState(down); +	} +	 +	 +} +void LLVivoxVoiceClient::keyUp(KEY key, MASK mask) +{ +	if(!mPTTIsMiddleMouse) +	{ +		bool down = (mPTTKey != KEY_NONE) +		&& gKeyboard->getKeyDown(mPTTKey); +		inputUserControlState(down); +	} +	 +} +void LLVivoxVoiceClient::middleMouseState(bool down) +{ +	if(mPTTIsMiddleMouse) +	{ +        if(mPTTIsMiddleMouse) +        { +			inputUserControlState(down); +        }		 +	} +} + +///////////////////////////// +// Accessors for data related to nearby speakers +BOOL LLVivoxVoiceClient::getVoiceEnabled(const LLUUID& id) +{ +	BOOL result = FALSE; +	participantState *participant = findParticipantByID(id); +	if(participant) +	{ +		// I'm not sure what the semantics of this should be. +		// For now, if we have any data about the user that came through the chat channel, assume they're voice-enabled. +		result = TRUE; +	} +	 +	return result; +} + +std::string LLVivoxVoiceClient::getDisplayName(const LLUUID& id) +{ +	std::string result; +	participantState *participant = findParticipantByID(id); +	if(participant) +	{ +		result = participant->mDisplayName; +	} +	 +	return result; +} + + + +BOOL LLVivoxVoiceClient::getIsSpeaking(const LLUUID& id) +{ +	BOOL result = FALSE; + +	participantState *participant = findParticipantByID(id); +	if(participant) +	{ +		if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT) +		{ +			participant->mIsSpeaking = FALSE; +		} +		result = participant->mIsSpeaking; +	} +	 +	return result; +} + +BOOL LLVivoxVoiceClient::getIsModeratorMuted(const LLUUID& id) +{ +	BOOL result = FALSE; + +	participantState *participant = findParticipantByID(id); +	if(participant) +	{ +		result = participant->mIsModeratorMuted; +	} +	 +	return result; +} + +F32 LLVivoxVoiceClient::getCurrentPower(const LLUUID& id) +{		 +	F32 result = 0; +	participantState *participant = findParticipantByID(id); +	if(participant) +	{ +		result = participant->mPower; +	} +	 +	return result; +} + + + +BOOL LLVivoxVoiceClient::getUsingPTT(const LLUUID& id) +{ +	BOOL result = FALSE; + +	participantState *participant = findParticipantByID(id); +	if(participant) +	{ +		// I'm not sure what the semantics of this should be. +		// Does "using PTT" mean they're configured with a push-to-talk button? +		// For now, we know there's no PTT mechanism in place, so nobody is using it. +	} +	 +	return result; +} + +BOOL LLVivoxVoiceClient::getOnMuteList(const LLUUID& id) +{ +	BOOL result = FALSE; +	 +	participantState *participant = findParticipantByID(id); +	if(participant) +	{ +		result = participant->mOnMuteList; +	} + +	return result; +} + +// External accessiors. Maps 0.0 to 1.0 to internal values 0-400 with .5 == 100 +// internal = 400 * external^2 +F32 LLVivoxVoiceClient::getUserVolume(const LLUUID& id) +{ +	F32 result = 0.0f; +	 +	participantState *participant = findParticipantByID(id); +	if(participant) +	{ +		S32 ires = 100; // nominal default volume +		 +		if(participant->mIsSelf) +		{ +			// Always make it look like the user's own volume is set at the default. +		} +		else if(participant->mUserVolume != -1) +		{ +			// Use the internal volume +			ires = participant->mUserVolume; +			 +			// Enable this when debugging voice slider issues.  It's way to spammy even for debug-level logging. +//			LL_DEBUGS("Voice") << "mapping from mUserVolume " << ires << LL_ENDL; +		} +		else if(participant->mVolume != -1) +		{ +			// Map backwards from vivox volume  + +			// Enable this when debugging voice slider issues.  It's way to spammy even for debug-level logging. +//			LL_DEBUGS("Voice") << "mapping from mVolume " << participant->mVolume << LL_ENDL; + +			if(participant->mVolume < 56) +			{ +				ires = (participant->mVolume * 100) / 56; +			} +			else +			{ +				ires = (((participant->mVolume - 56) * 300) / (100 - 56)) + 100; +			} +		} +		result = sqrtf(((F32)ires) / 400.f); +	} + +	// Enable this when debugging voice slider issues.  It's way to spammy even for debug-level logging. +//	LL_DEBUGS("Voice") << "returning " << result << LL_ENDL; + +	return result; +} + +void LLVivoxVoiceClient::setUserVolume(const LLUUID& id, F32 volume) +{ +	if(mAudioSession) +	{ +		participantState *participant = findParticipantByID(id); +		if (participant) +		{ +			// store this volume setting for future sessions +			LLSpeakerVolumeStorage::getInstance()->storeSpeakerVolume(id, volume); +			// volume can amplify by as much as 4x! +			S32 ivol = (S32)(400.f * volume * volume); +			participant->mUserVolume = llclamp(ivol, 0, 400); +			participant->mVolumeDirty = TRUE; +			mAudioSession->mVolumeDirty = TRUE; + +		} +	} +} + +std::string LLVivoxVoiceClient::getGroupID(const LLUUID& id) +{ +	std::string result; + +	participantState *participant = findParticipantByID(id); +	if(participant) +	{ +		result = participant->mGroupID; +	} +	 +	return result; +} + +BOOL LLVivoxVoiceClient::getAreaVoiceDisabled() +{ +	return mAreaVoiceDisabled; +} + +void LLVivoxVoiceClient::recordingLoopStart(int seconds, int deltaFramesPerControlFrame) +{ +//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Start)" << LL_ENDL; +	 +	if(!mMainSessionGroupHandle.empty()) +	{ +		std::ostringstream stream; +		stream +		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">" +		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>" +		<< "<RecordingControlType>Start</RecordingControlType>"  +		<< "<DeltaFramesPerControlFrame>" << deltaFramesPerControlFrame << "</DeltaFramesPerControlFrame>" +		<< "<Filename>" << "" << "</Filename>" +		<< "<EnableAudioRecordingEvents>false</EnableAudioRecordingEvents>" +		<< "<LoopModeDurationSeconds>" << seconds << "</LoopModeDurationSeconds>" +		<< "</Request>\n\n\n"; + + +		writeString(stream.str()); +	} +} + +void LLVivoxVoiceClient::recordingLoopSave(const std::string& filename) +{ +//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Flush)" << LL_ENDL; + +	if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) +	{ +		std::ostringstream stream; +		stream +		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">" +		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>" +		<< "<RecordingControlType>Flush</RecordingControlType>"  +		<< "<Filename>" << filename << "</Filename>" +		<< "</Request>\n\n\n"; + +		writeString(stream.str()); +	} +} + +void LLVivoxVoiceClient::recordingStop() +{ +//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlRecording (Stop)" << LL_ENDL; + +	if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) +	{ +		std::ostringstream stream; +		stream +		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlRecording.1\">" +		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>" +		<< "<RecordingControlType>Stop</RecordingControlType>"  +		<< "</Request>\n\n\n"; + +		writeString(stream.str()); +	} +} + +void LLVivoxVoiceClient::filePlaybackStart(const std::string& filename) +{ +//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Start)" << LL_ENDL; + +	if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) +	{ +		std::ostringstream stream; +		stream +		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlPlayback.1\">" +		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>" +		<< "<RecordingControlType>Start</RecordingControlType>"  +		<< "<Filename>" << filename << "</Filename>" +		<< "</Request>\n\n\n"; + +		writeString(stream.str()); +	} +} + +void LLVivoxVoiceClient::filePlaybackStop() +{ +//	LL_DEBUGS("Voice") << "sending SessionGroup.ControlPlayback (Stop)" << LL_ENDL; + +	if(mAudioSession != NULL && !mAudioSession->mGroupHandle.empty()) +	{ +		std::ostringstream stream; +		stream +		<< "<Request requestId=\"" << mCommandCookie++ << "\" action=\"SessionGroup.ControlPlayback.1\">" +		<< "<SessionGroupHandle>" << mMainSessionGroupHandle << "</SessionGroupHandle>" +		<< "<RecordingControlType>Stop</RecordingControlType>"  +		<< "</Request>\n\n\n"; + +		writeString(stream.str()); +	} +} + +void LLVivoxVoiceClient::filePlaybackSetPaused(bool paused) +{ +	// TODO: Implement once Vivox gives me a sample +} + +void LLVivoxVoiceClient::filePlaybackSetMode(bool vox, float speed) +{ +	// TODO: Implement once Vivox gives me a sample +} + +LLVivoxVoiceClient::sessionState::sessionState() : +        mErrorStatusCode(0), +	mMediaStreamState(streamStateUnknown), +	mTextStreamState(streamStateUnknown), +	mCreateInProgress(false), +	mMediaConnectInProgress(false), +	mVoiceInvitePending(false), +	mTextInvitePending(false), +	mSynthesizedCallerID(false), +	mIsChannel(false), +	mIsSpatial(false), +	mIsP2P(false), +	mIncoming(false), +	mVoiceEnabled(false), +	mReconnect(false), +	mVolumeDirty(false), +	mParticipantsChanged(false) +{ +} + +LLVivoxVoiceClient::sessionState::~sessionState() +{ +	removeAllParticipants(); +} + +bool LLVivoxVoiceClient::sessionState::isCallBackPossible() +{ +	// This may change to be explicitly specified by vivox in the future... +	// Currently, only PSTN P2P calls cannot be returned. +	// Conveniently, this is also the only case where we synthesize a caller UUID. +	return !mSynthesizedCallerID; +} + +bool LLVivoxVoiceClient::sessionState::isTextIMPossible() +{ +	// This may change to be explicitly specified by vivox in the future... +	return !mSynthesizedCallerID; +} + + +LLVivoxVoiceClient::sessionIterator LLVivoxVoiceClient::sessionsBegin(void) +{ +	return mSessions.begin(); +} + +LLVivoxVoiceClient::sessionIterator LLVivoxVoiceClient::sessionsEnd(void) +{ +	return mSessions.end(); +} + + +LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSession(const std::string &handle) +{ +	sessionState *result = NULL; +	sessionMap::iterator iter = mSessionsByHandle.find(handle); +	if(iter != mSessionsByHandle.end()) +	{ +		result = iter->second; +	} +	 +	return result; +} + +LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSessionBeingCreatedByURI(const std::string &uri) +{	 +	sessionState *result = NULL; +	for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) +	{ +		sessionState *session = *iter; +		if(session->mCreateInProgress && (session->mSIPURI == uri)) +		{ +			result = session; +			break; +		} +	} +	 +	return result; +} + +LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::findSession(const LLUUID &participant_id) +{ +	sessionState *result = NULL; +	 +	for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) +	{ +		sessionState *session = *iter; +		if((session->mCallerID == participant_id) || (session->mIMSessionID == participant_id)) +		{ +			result = session; +			break; +		} +	} +	 +	return result; +} + +LLVivoxVoiceClient::sessionState *LLVivoxVoiceClient::addSession(const std::string &uri, const std::string &handle) +{ +	sessionState *result = NULL; +	 +	if(handle.empty()) +	{ +		// No handle supplied. +		// Check whether there's already a session with this URI +		for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) +		{ +			sessionState *s = *iter; +			if((s->mSIPURI == uri) || (s->mAlternateSIPURI == uri)) +			{ +				// TODO: I need to think about this logic... it's possible that this case should raise an internal error. +				result = s; +				break; +			} +		} +	} +	else // (!handle.empty()) +	{ +		// Check for an existing session with this handle +		sessionMap::iterator iter = mSessionsByHandle.find(handle); +		 +		if(iter != mSessionsByHandle.end()) +		{ +			result = iter->second; +		} +	} + +	if(!result) +	{ +		// No existing session found. +		 +		LL_DEBUGS("Voice") << "adding new session: handle " << handle << " URI " << uri << LL_ENDL; +		result = new sessionState(); +		result->mSIPURI = uri; +		result->mHandle = handle; +		 +		mSessions.insert(result); + +		if(!result->mHandle.empty()) +		{ +			mSessionsByHandle.insert(sessionMap::value_type(result->mHandle, result)); +		} +	} +	else +	{ +		// Found an existing session +		 +		if(uri != result->mSIPURI) +		{ +			// TODO: Should this be an internal error? +			LL_DEBUGS("Voice") << "changing uri from " << result->mSIPURI << " to " << uri << LL_ENDL; +			setSessionURI(result, uri); +		} + +		if(handle != result->mHandle) +		{ +			if(handle.empty()) +			{ +				// There's at least one race condition where where addSession was clearing an existing session handle, which caused things to break. +				LL_DEBUGS("Voice") << "NOT clearing handle " << result->mHandle << LL_ENDL; +			} +			else +			{ +				// TODO: Should this be an internal error? +				LL_DEBUGS("Voice") << "changing handle from " << result->mHandle << " to " << handle << LL_ENDL; +				setSessionHandle(result, handle); +			} +		} +		 +		LL_DEBUGS("Voice") << "returning existing session: handle " << handle << " URI " << uri << LL_ENDL; +	} + +	verifySessionState(); +		 +	return result; +} + +void LLVivoxVoiceClient::setSessionHandle(sessionState *session, const std::string &handle) +{ +	// Have to remove the session from the handle-indexed map before changing the handle, or things will break badly. +	 +	if(!session->mHandle.empty()) +	{ +		// Remove session from the map if it should have been there. +		sessionMap::iterator iter = mSessionsByHandle.find(session->mHandle); +		if(iter != mSessionsByHandle.end()) +		{ +			if(iter->second != session) +			{ +				LL_ERRS("Voice") << "Internal error: session mismatch!" << LL_ENDL; +			} + +			mSessionsByHandle.erase(iter); +		} +		else +		{ +			LL_ERRS("Voice") << "Internal error: session handle not found in map!" << LL_ENDL; +		} +	} +			 +	session->mHandle = handle; + +	if(!handle.empty()) +	{ +		mSessionsByHandle.insert(sessionMap::value_type(session->mHandle, session)); +	} + +	verifySessionState(); +} + +void LLVivoxVoiceClient::setSessionURI(sessionState *session, const std::string &uri) +{ +	// There used to be a map of session URIs to sessions, which made this complex.... +	session->mSIPURI = uri; + +	verifySessionState(); +} + +void LLVivoxVoiceClient::deleteSession(sessionState *session) +{ +	// Remove the session from the handle map +	if(!session->mHandle.empty()) +	{ +		sessionMap::iterator iter = mSessionsByHandle.find(session->mHandle); +		if(iter != mSessionsByHandle.end()) +		{ +			if(iter->second != session) +			{ +				LL_ERRS("Voice") << "Internal error: session mismatch" << LL_ENDL; +			} +			mSessionsByHandle.erase(iter); +		} +	} + +	// Remove the session from the URI map +	mSessions.erase(session); +	 +	// At this point, the session should be unhooked from all lists and all state should be consistent. +	verifySessionState(); + +	// If this is the current audio session, clean up the pointer which will soon be dangling. +	if(mAudioSession == session) +	{ +		mAudioSession = NULL; +		mAudioSessionChanged = true; +	} + +	// ditto for the next audio session +	if(mNextAudioSession == session) +	{ +		mNextAudioSession = NULL; +	} + +	// delete the session +	delete session; +} + +void LLVivoxVoiceClient::deleteAllSessions() +{ +	LL_DEBUGS("Voice") << "called" << LL_ENDL; + +	while(!mSessions.empty()) +	{ +		deleteSession(*(sessionsBegin())); +	} +	 +	if(!mSessionsByHandle.empty()) +	{ +		LL_ERRS("Voice") << "Internal error: empty session map, non-empty handle map" << LL_ENDL; +	} +} + +void LLVivoxVoiceClient::verifySessionState(void) +{ +	// This is mostly intended for debugging problems with session state management. +	LL_DEBUGS("Voice") << "Total session count: " << mSessions.size() << " , session handle map size: " << mSessionsByHandle.size() << LL_ENDL; + +	for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) +	{ +		sessionState *session = *iter; + +		LL_DEBUGS("Voice") << "session " << session << ": handle " << session->mHandle << ", URI " << session->mSIPURI << LL_ENDL; +		 +		if(!session->mHandle.empty()) +		{ +			// every session with a non-empty handle needs to be in the handle map +			sessionMap::iterator i2 = mSessionsByHandle.find(session->mHandle); +			if(i2 == mSessionsByHandle.end()) +			{ +				LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " not found in session map)" << LL_ENDL; +			} +			else +			{ +				if(i2->second != session) +				{ +					LL_ERRS("Voice") << "internal error (handle " << session->mHandle << " in session map points to another session)" << LL_ENDL; +				} +			} +		} +	} +		 +	// check that every entry in the handle map points to a valid session in the session set +	for(sessionMap::iterator iter = mSessionsByHandle.begin(); iter != mSessionsByHandle.end(); iter++) +	{ +		sessionState *session = iter->second; +		sessionIterator i2 = mSessions.find(session); +		if(i2 == mSessions.end()) +		{ +			LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " not found in session map)" << LL_ENDL; +		} +		else +		{ +			if(session->mHandle != (*i2)->mHandle) +			{ +				LL_ERRS("Voice") << "internal error (session for handle " << session->mHandle << " points to session with different handle " << (*i2)->mHandle << ")" << LL_ENDL; +			} +		} +	} +} + +LLVivoxVoiceClient::buddyListEntry::buddyListEntry(const std::string &uri) : +	mURI(uri) +{ +	mOnlineSL = false; +	mOnlineSLim = false; +	mCanSeeMeOnline = true; +	mHasBlockListEntry = false; +	mHasAutoAcceptListEntry = false; +	mNameResolved = false; +	mInVivoxBuddies = false; +	mInSLFriends = false; +	mNeedsNameUpdate = false; +} + +void LLVivoxVoiceClient::processBuddyListEntry(const std::string &uri, const std::string &displayName) +{ +	buddyListEntry *buddy = addBuddy(uri, displayName); +	buddy->mInVivoxBuddies = true;	 +} + +LLVivoxVoiceClient::buddyListEntry *LLVivoxVoiceClient::addBuddy(const std::string &uri) +{ +	std::string empty; +	buddyListEntry *buddy = addBuddy(uri, empty); +	if(buddy->mDisplayName.empty()) +	{ +		buddy->mNameResolved = false; +	} +	return buddy; +} + +LLVivoxVoiceClient::buddyListEntry *LLVivoxVoiceClient::addBuddy(const std::string &uri, const std::string &displayName) +{ +	buddyListEntry *result = NULL; +	buddyListMap::iterator iter = mBuddyListMap.find(uri); +	 +	if(iter != mBuddyListMap.end()) +	{ +		// Found a matching buddy already in the map. +		LL_DEBUGS("Voice") << "adding existing buddy " << uri << LL_ENDL; +		result = iter->second; +	} + +	if(!result) +	{ +		// participant isn't already in one list or the other. +		LL_DEBUGS("Voice") << "adding new buddy " << uri << LL_ENDL; +		result = new buddyListEntry(uri); +		result->mDisplayName = displayName; + +		if(IDFromName(uri, result->mUUID))  +		{ +			// Extracted UUID from name successfully. +		} +		else +		{ +			LL_DEBUGS("Voice") << "Couldn't find ID for buddy " << uri << " (\"" << displayName << "\")" << LL_ENDL; +		} + +		mBuddyListMap.insert(buddyListMap::value_type(result->mURI, result)); +	} +	 +	return result; +} + +LLVivoxVoiceClient::buddyListEntry *LLVivoxVoiceClient::findBuddy(const std::string &uri) +{ +	buddyListEntry *result = NULL; +	buddyListMap::iterator iter = mBuddyListMap.find(uri); +	if(iter != mBuddyListMap.end()) +	{ +		result = iter->second; +	} +	 +	return result; +} + +LLVivoxVoiceClient::buddyListEntry *LLVivoxVoiceClient::findBuddy(const LLUUID &id) +{ +	buddyListEntry *result = NULL; +	buddyListMap::iterator iter; + +	for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++) +	{ +		if(iter->second->mUUID == id) +		{ +			result = iter->second; +			break; +		} +	} +	 +	return result; +} + +LLVivoxVoiceClient::buddyListEntry *LLVivoxVoiceClient::findBuddyByDisplayName(const std::string &name) +{ +	buddyListEntry *result = NULL; +	buddyListMap::iterator iter; + +	for(iter = mBuddyListMap.begin(); iter != mBuddyListMap.end(); iter++) +	{ +		if(iter->second->mDisplayName == name) +		{ +			result = iter->second; +			break; +		} +	} +	 +	return result; +} + +void LLVivoxVoiceClient::deleteBuddy(const std::string &uri) +{ +	buddyListMap::iterator iter = mBuddyListMap.find(uri); +	if(iter != mBuddyListMap.end()) +	{ +		LL_DEBUGS("Voice") << "deleting buddy " << uri << LL_ENDL; +		buddyListEntry *buddy = iter->second; +		mBuddyListMap.erase(iter); +		delete buddy; +	} +	else +	{ +		LL_DEBUGS("Voice") << "attempt to delete nonexistent buddy " << uri << LL_ENDL; +	} +	 +} + +void LLVivoxVoiceClient::deleteAllBuddies(void) +{ +	while(!mBuddyListMap.empty()) +	{ +		deleteBuddy(mBuddyListMap.begin()->first); +	} +	 +	// Don't want to correlate with friends list when we've emptied the buddy list. +	mBuddyListMapPopulated = false; +	 +	// Don't want to correlate with friends list when we've reset the block rules. +	mBlockRulesListReceived = false; +	mAutoAcceptRulesListReceived = false; +} + +void LLVivoxVoiceClient::deleteAllBlockRules(void) +{ +	// Clear the block list entry flags from all local buddy list entries +	buddyListMap::iterator buddy_it; +	for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++) +	{ +		buddy_it->second->mHasBlockListEntry = false; +	} +} + +void LLVivoxVoiceClient::deleteAllAutoAcceptRules(void) +{ +	// Clear the auto-accept list entry flags from all local buddy list entries +	buddyListMap::iterator buddy_it; +	for(buddy_it = mBuddyListMap.begin(); buddy_it != mBuddyListMap.end(); buddy_it++) +	{ +		buddy_it->second->mHasAutoAcceptListEntry = false; +	} +} + +void LLVivoxVoiceClient::addBlockRule(const std::string &blockMask, const std::string &presenceOnly) +{ +	buddyListEntry *buddy = NULL; + +	// blockMask is the SIP URI of a friends list entry +	buddyListMap::iterator iter = mBuddyListMap.find(blockMask); +	if(iter != mBuddyListMap.end()) +	{ +		LL_DEBUGS("Voice") << "block list entry for " << blockMask << LL_ENDL; +		buddy = iter->second; +	} + +	if(buddy == NULL) +	{ +		LL_DEBUGS("Voice") << "block list entry for unknown buddy " << blockMask << LL_ENDL; +		buddy = addBuddy(blockMask); +	} +	 +	if(buddy != NULL) +	{ +		buddy->mHasBlockListEntry = true; +	} +} + +void LLVivoxVoiceClient::addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy) +{ +	buddyListEntry *buddy = NULL; + +	// blockMask is the SIP URI of a friends list entry +	buddyListMap::iterator iter = mBuddyListMap.find(autoAcceptMask); +	if(iter != mBuddyListMap.end()) +	{ +		LL_DEBUGS("Voice") << "auto-accept list entry for " << autoAcceptMask << LL_ENDL; +		buddy = iter->second; +	} + +	if(buddy == NULL) +	{ +		LL_DEBUGS("Voice") << "auto-accept list entry for unknown buddy " << autoAcceptMask << LL_ENDL; +		buddy = addBuddy(autoAcceptMask); +	} + +	if(buddy != NULL) +	{ +		buddy->mHasAutoAcceptListEntry = true; +	} +} + +void LLVivoxVoiceClient::accountListBlockRulesResponse(int statusCode, const std::string &statusString) +{ +	// Block list entries were updated via addBlockRule() during parsing.  Just flag that we're done. +	mBlockRulesListReceived = true; +} + +void LLVivoxVoiceClient::accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString) +{ +	// Block list entries were updated via addBlockRule() during parsing.  Just flag that we're done. +	mAutoAcceptRulesListReceived = true; +} + +void LLVivoxVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer) +{ +	mParticipantObservers.insert(observer); +} + +void LLVivoxVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer) +{ +	mParticipantObservers.erase(observer); +} + +void LLVivoxVoiceClient::notifyParticipantObservers() +{ +	for (observer_set_t::iterator it = mParticipantObservers.begin(); +		it != mParticipantObservers.end(); +		) +	{ +		LLVoiceClientParticipantObserver* observer = *it; +		observer->onChange(); +		// In case onChange() deleted an entry. +		it = mParticipantObservers.upper_bound(observer); +	} +} + +void LLVivoxVoiceClient::addObserver(LLVoiceClientStatusObserver* observer) +{ +	mStatusObservers.insert(observer); +} + +void LLVivoxVoiceClient::removeObserver(LLVoiceClientStatusObserver* observer) +{ +	mStatusObservers.erase(observer); +} + +void LLVivoxVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status) +{ +	if(mAudioSession) +	{ +		if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN) +		{ +			switch(mAudioSession->mErrorStatusCode) +			{ +				case 20713:		status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL; 		break; +				case 20714:		status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED; 	break; +				case 20715: +					//invalid channel, we may be using a set of poorly cached +					//info +					status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; +					break; +				case 1009: +					//invalid username and password +					status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; +					break; +			} + +			// Reset the error code to make sure it won't be reused later by accident. +			mAudioSession->mErrorStatusCode = 0; +		} +		else if(status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL) +		{ +			switch(mAudioSession->mErrorStatusCode) +			{ +				case 404:	// NOT_FOUND +				case 480:	// TEMPORARILY_UNAVAILABLE +				case 408:	// REQUEST_TIMEOUT +					// call failed because other user was not available +					// treat this as an error case +					status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE; + +					// Reset the error code to make sure it won't be reused later by accident. +					mAudioSession->mErrorStatusCode = 0; +				break; +			} +		} +	} +		 +	LL_DEBUGS("Voice")  +		<< " " << LLVoiceClientStatusObserver::status2string(status)   +		<< ", session URI " << getAudioSessionURI()  +		<< (inSpatialChannel()?", proximal is true":", proximal is false") +	<< LL_ENDL; + +	for (status_observer_set_t::iterator it = mStatusObservers.begin(); +		it != mStatusObservers.end(); +		) +	{ +		LLVoiceClientStatusObserver* observer = *it; +		observer->onChange(status, getAudioSessionURI(), inSpatialChannel()); +		// In case onError() deleted an entry. +		it = mStatusObservers.upper_bound(observer); +	} + +} + +void LLVivoxVoiceClient::addObserver(LLFriendObserver* observer) +{ +	mFriendObservers.insert(observer); +} + +void LLVivoxVoiceClient::removeObserver(LLFriendObserver* observer) +{ +	mFriendObservers.erase(observer); +} + +void LLVivoxVoiceClient::notifyFriendObservers() +{ +	for (friend_observer_set_t::iterator it = mFriendObservers.begin(); +		it != mFriendObservers.end(); +		) +	{ +		LLFriendObserver* observer = *it; +		it++; +		// The only friend-related thing we notify on is online/offline transitions. +		observer->changed(LLFriendObserver::ONLINE); +	} +} + +void LLVivoxVoiceClient::lookupName(const LLUUID &id) +{ +	BOOL is_group = FALSE; +	gCacheName->get(id, is_group, &LLVivoxVoiceClient::onAvatarNameLookup); +} + +//static +void LLVivoxVoiceClient::onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group) +{ +		std::string name = llformat("%s %s", first.c_str(), last.c_str()); +		LLVivoxVoiceClient::getInstance()->avatarNameResolved(id, name); +	 +} + +void LLVivoxVoiceClient::avatarNameResolved(const LLUUID &id, const std::string &name) +{ +	// If the avatar whose name just resolved is on our friends list, resync the friends list. +	if(LLAvatarTracker::instance().getBuddyInfo(id) != NULL) +	{ +		mFriendsListDirty = true; +	} +	 +	// Iterate over all sessions. +	for(sessionIterator iter = sessionsBegin(); iter != sessionsEnd(); iter++) +	{ +		sessionState *session = *iter; + +		// Check for this user as a participant in this session +		participantState *participant = session->findParticipantByID(id); +		if(participant) +		{ +			// Found -- fill in the name +			participant->mAccountName = name; +			// and post a "participants updated" message to listeners later. +			session->mParticipantsChanged = true; +		} +		 +		// Check whether this is a p2p session whose caller name just resolved +		if(session->mCallerID == id) +		{ +			// this session's "caller ID" just resolved.  Fill in the name. +			session->mName = name; +			if(session->mTextInvitePending) +			{ +				session->mTextInvitePending = false; + +				// We don't need to call gIMMgr->addP2PSession() here.  The first incoming message will create the panel.				 +			} +			if(session->mVoiceInvitePending) +			{ +				session->mVoiceInvitePending = false; + +				gIMMgr->inviteToSession( +										LLIMMgr::computeSessionID(IM_SESSION_P2P_INVITE, session->mCallerID), +										session->mName, +										session->mCallerID,  +										session->mName,  +										IM_SESSION_P2P_INVITE,  +										LLIMMgr::INVITATION_TYPE_VOICE, +										session->mHandle); +			} +			 +		} +	} +} + + +LLVivoxProtocolParser::LLVivoxProtocolParser() +{ +	parser = NULL; +	parser = XML_ParserCreate(NULL); +	 +	reset(); +} + +void LLVivoxProtocolParser::reset() +{ +	responseDepth = 0; +	ignoringTags = false; +	accumulateText = false; +	energy = 0.f; +	hasText = false; +	hasAudio = false; +	hasVideo = false; +	terminated = false; +	ignoreDepth = 0; +	isChannel = false; +	incoming = false; +	enabled = false; +	isEvent = false; +	isLocallyMuted = false; +	isModeratorMuted = false; +	isSpeaking = false; +	participantType = 0; +	squelchDebugOutput = false; +	returnCode = -1; +	state = 0; +	statusCode = 0; +	volume = 0; +	textBuffer.clear(); +	alias.clear(); +	numberOfAliases = 0; +	applicationString.clear(); +} + +//virtual  +LLVivoxProtocolParser::~LLVivoxProtocolParser() +{ +	if (parser) +		XML_ParserFree(parser); +} + +// virtual +LLIOPipe::EStatus LLVivoxProtocolParser::process_impl( +													  const LLChannelDescriptors& channels, +													  buffer_ptr_t& buffer, +													  bool& eos, +													  LLSD& context, +													  LLPumpIO* pump) +{ +	LLBufferStream istr(channels, buffer.get()); +	std::ostringstream ostr; +	while (istr.good()) +	{ +		char buf[1024]; +		istr.read(buf, sizeof(buf)); +		mInput.append(buf, istr.gcount()); +	} +	 +	// Look for input delimiter(s) in the input buffer.  If one is found, send the message to the xml parser. +	int start = 0; +	int delim; +	while((delim = mInput.find("\n\n\n", start)) != std::string::npos) +	{	 +		 +		// Reset internal state of the LLVivoxProtocolParser (no effect on the expat parser) +		reset(); +		 +		XML_ParserReset(parser, NULL); +		XML_SetElementHandler(parser, ExpatStartTag, ExpatEndTag); +		XML_SetCharacterDataHandler(parser, ExpatCharHandler); +		XML_SetUserData(parser, this);	 +		XML_Parse(parser, mInput.data() + start, delim - start, false); +		 +		// If this message isn't set to be squelched, output the raw XML received. +		if(!squelchDebugOutput) +		{ +			LL_DEBUGS("Voice") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL; +		} +		 +		start = delim + 3; +	} +	 +	if(start != 0) +		mInput = mInput.substr(start); +	 +	LL_DEBUGS("VivoxProtocolParser") << "at end, mInput is: " << mInput << LL_ENDL; +	 +	if(!LLVivoxVoiceClient::getInstance()->mConnected) +	{ +		// If voice has been disabled, we just want to close the socket.  This does so. +		LL_INFOS("Voice") << "returning STATUS_STOP" << LL_ENDL; +		return STATUS_STOP; +	} +	 +	return STATUS_OK; +} + +void XMLCALL LLVivoxProtocolParser::ExpatStartTag(void *data, const char *el, const char **attr) +{ +	if (data) +	{ +		LLVivoxProtocolParser	*object = (LLVivoxProtocolParser*)data; +		object->StartTag(el, attr); +	} +} + +// -------------------------------------------------------------------------------- + +void XMLCALL LLVivoxProtocolParser::ExpatEndTag(void *data, const char *el) +{ +	if (data) +	{ +		LLVivoxProtocolParser	*object = (LLVivoxProtocolParser*)data; +		object->EndTag(el); +	} +} + +// -------------------------------------------------------------------------------- + +void XMLCALL LLVivoxProtocolParser::ExpatCharHandler(void *data, const XML_Char *s, int len) +{ +	if (data) +	{ +		LLVivoxProtocolParser	*object = (LLVivoxProtocolParser*)data; +		object->CharData(s, len); +	} +} + +// -------------------------------------------------------------------------------- + + +void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr) +{ +	// Reset the text accumulator. We shouldn't have strings that are inturrupted by new tags +	textBuffer.clear(); +	// only accumulate text if we're not ignoring tags. +	accumulateText = !ignoringTags; +	 +	if (responseDepth == 0) +	{	 +		isEvent = !stricmp("Event", tag); +		 +		if (!stricmp("Response", tag) || isEvent) +		{ +			// Grab the attributes +			while (*attr) +			{ +				const char	*key = *attr++; +				const char	*value = *attr++; +				 +				if (!stricmp("requestId", key)) +				{ +					requestId = value; +				} +				else if (!stricmp("action", key)) +				{ +					actionString = value; +				} +				else if (!stricmp("type", key)) +				{ +					eventTypeString = value; +				} +			} +		} +		LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")"  << LL_ENDL; +	} +	else +	{ +		if (ignoringTags) +		{ +			LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; +		} +		else +		{ +			LL_DEBUGS("VivoxProtocolParser") << tag << " (" << responseDepth << ")"  << LL_ENDL; +			 +			// Ignore the InputXml stuff so we don't get confused +			if (!stricmp("InputXml", tag)) +			{ +				ignoringTags = true; +				ignoreDepth = responseDepth; +				accumulateText = false; +				 +				LL_DEBUGS("VivoxProtocolParser") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL; +			} +			else if (!stricmp("CaptureDevices", tag)) +			{ +				LLVivoxVoiceClient::getInstance()->clearCaptureDevices(); +			}			 +			else if (!stricmp("RenderDevices", tag)) +			{ +				LLVivoxVoiceClient::getInstance()->clearRenderDevices(); +			} +			else if (!stricmp("CaptureDevice", tag)) +			{ +				deviceString.clear(); +			} +			else if (!stricmp("RenderDevice", tag)) +			{ +				deviceString.clear(); +			}			 +			else if (!stricmp("Buddies", tag)) +			{ +				LLVivoxVoiceClient::getInstance()->deleteAllBuddies(); +			} +			else if (!stricmp("BlockRules", tag)) +			{ +				LLVivoxVoiceClient::getInstance()->deleteAllBlockRules(); +			} +			else if (!stricmp("AutoAcceptRules", tag)) +			{ +				LLVivoxVoiceClient::getInstance()->deleteAllAutoAcceptRules(); +			} +			 +		} +	} +	responseDepth++; +} + +// -------------------------------------------------------------------------------- + +void LLVivoxProtocolParser::EndTag(const char *tag) +{ +	const std::string& string = textBuffer; +	 +	responseDepth--; +	 +	if (ignoringTags) +	{ +		if (ignoreDepth == responseDepth) +		{ +			LL_DEBUGS("VivoxProtocolParser") << "end of ignore" << LL_ENDL; +			ignoringTags = false; +		} +		else +		{ +			LL_DEBUGS("VivoxProtocolParser") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; +		} +	} +	 +	if (!ignoringTags) +	{ +		LL_DEBUGS("VivoxProtocolParser") << "processing tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL; +		 +		// Closing a tag. Finalize the text we've accumulated and reset +		if (!stricmp("ReturnCode", tag)) +			returnCode = strtol(string.c_str(), NULL, 10); +		else if (!stricmp("SessionHandle", tag)) +			sessionHandle = string; +		else if (!stricmp("SessionGroupHandle", tag)) +			sessionGroupHandle = string; +		else if (!stricmp("StatusCode", tag)) +			statusCode = strtol(string.c_str(), NULL, 10); +		else if (!stricmp("StatusString", tag)) +			statusString = string; +		else if (!stricmp("ParticipantURI", tag)) +			uriString = string; +		else if (!stricmp("Volume", tag)) +			volume = strtol(string.c_str(), NULL, 10); +		else if (!stricmp("Energy", tag)) +			energy = (F32)strtod(string.c_str(), NULL); +		else if (!stricmp("IsModeratorMuted", tag)) +			isModeratorMuted = !stricmp(string.c_str(), "true"); +		else if (!stricmp("IsSpeaking", tag)) +			isSpeaking = !stricmp(string.c_str(), "true"); +		else if (!stricmp("Alias", tag)) +			alias = string; +		else if (!stricmp("NumberOfAliases", tag)) +			numberOfAliases = strtol(string.c_str(), NULL, 10); +		else if (!stricmp("Application", tag)) +			applicationString = string; +		else if (!stricmp("ConnectorHandle", tag)) +			connectorHandle = string; +		else if (!stricmp("VersionID", tag)) +			versionID = string; +		else if (!stricmp("AccountHandle", tag)) +			accountHandle = string; +		else if (!stricmp("State", tag)) +			state = strtol(string.c_str(), NULL, 10); +		else if (!stricmp("URI", tag)) +			uriString = string; +		else if (!stricmp("IsChannel", tag)) +			isChannel = !stricmp(string.c_str(), "true"); +		else if (!stricmp("Incoming", tag)) +			incoming = !stricmp(string.c_str(), "true"); +		else if (!stricmp("Enabled", tag)) +			enabled = !stricmp(string.c_str(), "true"); +		else if (!stricmp("Name", tag)) +			nameString = string; +		else if (!stricmp("AudioMedia", tag)) +			audioMediaString = string; +		else if (!stricmp("ChannelName", tag)) +			nameString = string; +		else if (!stricmp("DisplayName", tag)) +			displayNameString = string; +		else if (!stricmp("Device", tag)) +			deviceString = string;		 +		else if (!stricmp("AccountName", tag)) +			nameString = string; +		else if (!stricmp("ParticipantType", tag)) +			participantType = strtol(string.c_str(), NULL, 10); +		else if (!stricmp("IsLocallyMuted", tag)) +			isLocallyMuted = !stricmp(string.c_str(), "true"); +		else if (!stricmp("MicEnergy", tag)) +			energy = (F32)strtod(string.c_str(), NULL); +		else if (!stricmp("ChannelName", tag)) +			nameString = string; +		else if (!stricmp("ChannelURI", tag)) +			uriString = string; +		else if (!stricmp("BuddyURI", tag)) +			uriString = string; +		else if (!stricmp("Presence", tag)) +			statusString = string; +		else if (!stricmp("CaptureDevice", tag)) +		{ +			LLVivoxVoiceClient::getInstance()->addCaptureDevice(deviceString); +		} +		else if (!stricmp("RenderDevice", tag)) +		{ +			LLVivoxVoiceClient::getInstance()->addRenderDevice(deviceString); +		} +		else if (!stricmp("Buddy", tag)) +		{ +			LLVivoxVoiceClient::getInstance()->processBuddyListEntry(uriString, displayNameString); +		} +		else if (!stricmp("BlockRule", tag)) +		{ +			LLVivoxVoiceClient::getInstance()->addBlockRule(blockMask, presenceOnly); +		} +		else if (!stricmp("BlockMask", tag)) +			blockMask = string; +		else if (!stricmp("PresenceOnly", tag)) +			presenceOnly = string; +		else if (!stricmp("AutoAcceptRule", tag)) +		{ +			LLVivoxVoiceClient::getInstance()->addAutoAcceptRule(autoAcceptMask, autoAddAsBuddy); +		} +		else if (!stricmp("AutoAcceptMask", tag)) +			autoAcceptMask = string; +		else if (!stricmp("AutoAddAsBuddy", tag)) +			autoAddAsBuddy = string; +		else if (!stricmp("MessageHeader", tag)) +			messageHeader = string; +		else if (!stricmp("MessageBody", tag)) +			messageBody = string; +		else if (!stricmp("NotificationType", tag)) +			notificationType = string; +		else if (!stricmp("HasText", tag)) +			hasText = !stricmp(string.c_str(), "true"); +		else if (!stricmp("HasAudio", tag)) +			hasAudio = !stricmp(string.c_str(), "true"); +		else if (!stricmp("HasVideo", tag)) +			hasVideo = !stricmp(string.c_str(), "true"); +		else if (!stricmp("Terminated", tag)) +			terminated = !stricmp(string.c_str(), "true"); +		else if (!stricmp("SubscriptionHandle", tag)) +			subscriptionHandle = string; +		else if (!stricmp("SubscriptionType", tag)) +			subscriptionType = string; +		 +	 +		textBuffer.clear(); +		accumulateText= false; +		 +		if (responseDepth == 0) +		{ +			// We finished all of the XML, process the data +			processResponse(tag); +		} +	} +} + +// -------------------------------------------------------------------------------- + +void LLVivoxProtocolParser::CharData(const char *buffer, int length) +{ +	/* +	 This method is called for anything that isn't a tag, which can be text you +	 want that lies between tags, and a lot of stuff you don't want like file formatting +	 (tabs, spaces, CR/LF, etc). +	  +	 Only copy text if we are in accumulate mode... +	 */ +	if (accumulateText) +		textBuffer.append(buffer, length); +} + +// -------------------------------------------------------------------------------- + +void LLVivoxProtocolParser::processResponse(std::string tag) +{ +	LL_DEBUGS("VivoxProtocolParser") << tag << LL_ENDL; +	 +	// SLIM SDK: the SDK now returns a statusCode of "200" (OK) for success.  This is a change vs. previous SDKs. +	// According to Mike S., "The actual API convention is that responses with return codes of 0 are successful, regardless of the status code returned", +	// so I believe this will give correct behavior. +	 +	if(returnCode == 0) +		statusCode = 0; +	 +	if (isEvent) +	{ +		const char *eventTypeCstr = eventTypeString.c_str(); +		if (!stricmp(eventTypeCstr, "AccountLoginStateChangeEvent")) +		{ +			LLVivoxVoiceClient::getInstance()->accountLoginStateChangeEvent(accountHandle, statusCode, statusString, state); +		} +		else if (!stricmp(eventTypeCstr, "SessionAddedEvent")) +		{ +			/* +			 <Event type="SessionAddedEvent"> +			 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle> +			 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle> +			 <Uri>sip:confctl-1408789@bhr.vivox.com</Uri> +			 <IsChannel>true</IsChannel> +			 <Incoming>false</Incoming> +			 <ChannelName /> +			 </Event> +			 */ +			LLVivoxVoiceClient::getInstance()->sessionAddedEvent(uriString, alias, sessionHandle, sessionGroupHandle, isChannel, incoming, nameString, applicationString); +		} +		else if (!stricmp(eventTypeCstr, "SessionRemovedEvent")) +		{ +			LLVivoxVoiceClient::getInstance()->sessionRemovedEvent(sessionHandle, sessionGroupHandle); +		} +		else if (!stricmp(eventTypeCstr, "SessionGroupAddedEvent")) +		{ +			LLVivoxVoiceClient::getInstance()->sessionGroupAddedEvent(sessionGroupHandle); +		} +		else if (!stricmp(eventTypeCstr, "MediaStreamUpdatedEvent")) +		{ +			/* +			 <Event type="MediaStreamUpdatedEvent"> +			 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle> +			 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle> +			 <StatusCode>200</StatusCode> +			 <StatusString>OK</StatusString> +			 <State>2</State> +			 <Incoming>false</Incoming> +			 </Event> +			 */ +			LLVivoxVoiceClient::getInstance()->mediaStreamUpdatedEvent(sessionHandle, sessionGroupHandle, statusCode, statusString, state, incoming); +		}		 +		else if (!stricmp(eventTypeCstr, "TextStreamUpdatedEvent")) +		{ +			/* +			 <Event type="TextStreamUpdatedEvent"> +			 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg1</SessionGroupHandle> +			 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==1</SessionHandle> +			 <Enabled>true</Enabled> +			 <State>1</State> +			 <Incoming>true</Incoming> +			 </Event> +			 */ +			LLVivoxVoiceClient::getInstance()->textStreamUpdatedEvent(sessionHandle, sessionGroupHandle, enabled, state, incoming); +		} +		else if (!stricmp(eventTypeCstr, "ParticipantAddedEvent")) +		{ +			/*  +			 <Event type="ParticipantAddedEvent"> +			 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4</SessionGroupHandle> +			 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==4</SessionHandle> +			 <ParticipantUri>sip:xI5auBZ60SJWIk606-1JGRQ==@bhr.vivox.com</ParticipantUri> +			 <AccountName>xI5auBZ60SJWIk606-1JGRQ==</AccountName> +			 <DisplayName /> +			 <ParticipantType>0</ParticipantType> +			 </Event> +			 */ +			LLVivoxVoiceClient::getInstance()->participantAddedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString, displayNameString, participantType); +		} +		else if (!stricmp(eventTypeCstr, "ParticipantRemovedEvent")) +		{ +			/* +			 <Event type="ParticipantRemovedEvent"> +			 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg4</SessionGroupHandle> +			 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==4</SessionHandle> +			 <ParticipantUri>sip:xtx7YNV-3SGiG7rA1fo5Ndw==@bhr.vivox.com</ParticipantUri> +			 <AccountName>xtx7YNV-3SGiG7rA1fo5Ndw==</AccountName> +			 </Event> +			 */ +			LLVivoxVoiceClient::getInstance()->participantRemovedEvent(sessionHandle, sessionGroupHandle, uriString, alias, nameString); +		} +		else if (!stricmp(eventTypeCstr, "ParticipantUpdatedEvent")) +		{ +			/* +			 <Event type="ParticipantUpdatedEvent"> +			 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle> +			 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle> +			 <ParticipantUri>sip:xFnPP04IpREWNkuw1cOXlhw==@bhr.vivox.com</ParticipantUri> +			 <IsModeratorMuted>false</IsModeratorMuted> +			 <IsSpeaking>true</IsSpeaking> +			 <Volume>44</Volume> +			 <Energy>0.0879437</Energy> +			 </Event> +			 */ +			 +			// These happen so often that logging them is pretty useless. +			squelchDebugOutput = true; +			 +			LLVivoxVoiceClient::getInstance()->participantUpdatedEvent(sessionHandle, sessionGroupHandle, uriString, alias, isModeratorMuted, isSpeaking, volume, energy); +		} +		else if (!stricmp(eventTypeCstr, "AuxAudioPropertiesEvent")) +		{ +			LLVivoxVoiceClient::getInstance()->auxAudioPropertiesEvent(energy); +		} +		else if (!stricmp(eventTypeCstr, "BuddyPresenceEvent")) +		{ +			LLVivoxVoiceClient::getInstance()->buddyPresenceEvent(uriString, alias, statusString, applicationString); +		} +		else if (!stricmp(eventTypeCstr, "BuddyAndGroupListChangedEvent")) +		{ +			// The buddy list was updated during parsing. +			// Need to recheck against the friends list. +			LLVivoxVoiceClient::getInstance()->buddyListChanged(); +		} +		else if (!stricmp(eventTypeCstr, "BuddyChangedEvent")) +		{ +			/* +			 <Event type="BuddyChangedEvent"> +			 <AccountHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==</AccountHandle> +			 <BuddyURI>sip:x9fFHFZjOTN6OESF1DUPrZQ==@bhr.vivox.com</BuddyURI> +			 <DisplayName>Monroe Tester</DisplayName> +			 <BuddyData /> +			 <GroupID>0</GroupID> +			 <ChangeType>Set</ChangeType> +			 </Event> +			 */		 +			// TODO: Question: Do we need to process this at all? +		} +		else if (!stricmp(eventTypeCstr, "MessageEvent"))   +		{ +			LLVivoxVoiceClient::getInstance()->messageEvent(sessionHandle, uriString, alias, messageHeader, messageBody, applicationString); +		} +		else if (!stricmp(eventTypeCstr, "SessionNotificationEvent"))   +		{ +			LLVivoxVoiceClient::getInstance()->sessionNotificationEvent(sessionHandle, uriString, notificationType); +		} +		else if (!stricmp(eventTypeCstr, "SubscriptionEvent"))   +		{ +			LLVivoxVoiceClient::getInstance()->subscriptionEvent(uriString, subscriptionHandle, alias, displayNameString, applicationString, subscriptionType); +		} +		else if (!stricmp(eventTypeCstr, "SessionUpdatedEvent"))   +		{ +			/* +			 <Event type="SessionUpdatedEvent"> +			 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle> +			 <SessionHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==0</SessionHandle> +			 <Uri>sip:confctl-9@bhd.vivox.com</Uri> +			 <IsMuted>0</IsMuted> +			 <Volume>50</Volume> +			 <TransmitEnabled>1</TransmitEnabled> +			 <IsFocused>0</IsFocused> +			 <SpeakerPosition><Position><X>0</X><Y>0</Y><Z>0</Z></Position></SpeakerPosition> +			 <SessionFontID>0</SessionFontID> +			 </Event> +			 */ +			// We don't need to process this, but we also shouldn't warn on it, since that confuses people. +		} +		 +		else if (!stricmp(eventTypeCstr, "SessionGroupRemovedEvent"))   +		{ +			/* +			 <Event type="SessionGroupRemovedEvent"> +			 <SessionGroupHandle>c1_m1000xFnPP04IpREWNkuw1cOXlhw==_sg0</SessionGroupHandle> +			 </Event> +			 */ +			// We don't need to process this, but we also shouldn't warn on it, since that confuses people. +		} +		else +		{ +			LL_WARNS("VivoxProtocolParser") << "Unknown event type " << eventTypeString << LL_ENDL; +		} +	} +	else +	{ +		const char *actionCstr = actionString.c_str(); +		if (!stricmp(actionCstr, "Connector.Create.1")) +		{ +			LLVivoxVoiceClient::getInstance()->connectorCreateResponse(statusCode, statusString, connectorHandle, versionID); +		} +		else if (!stricmp(actionCstr, "Account.Login.1")) +		{ +			LLVivoxVoiceClient::getInstance()->loginResponse(statusCode, statusString, accountHandle, numberOfAliases); +		} +		else if (!stricmp(actionCstr, "Session.Create.1")) +		{ +			LLVivoxVoiceClient::getInstance()->sessionCreateResponse(requestId, statusCode, statusString, sessionHandle);			 +		} +		else if (!stricmp(actionCstr, "SessionGroup.AddSession.1")) +		{ +			LLVivoxVoiceClient::getInstance()->sessionGroupAddSessionResponse(requestId, statusCode, statusString, sessionHandle);			 +		} +		else if (!stricmp(actionCstr, "Session.Connect.1")) +		{ +			LLVivoxVoiceClient::getInstance()->sessionConnectResponse(requestId, statusCode, statusString);			 +		} +		else if (!stricmp(actionCstr, "Account.Logout.1")) +		{ +			LLVivoxVoiceClient::getInstance()->logoutResponse(statusCode, statusString);			 +		} +		else if (!stricmp(actionCstr, "Connector.InitiateShutdown.1")) +		{ +			LLVivoxVoiceClient::getInstance()->connectorShutdownResponse(statusCode, statusString);			 +		} +		else if (!stricmp(actionCstr, "Account.ListBlockRules.1")) +		{ +			LLVivoxVoiceClient::getInstance()->accountListBlockRulesResponse(statusCode, statusString);						 +		} +		else if (!stricmp(actionCstr, "Account.ListAutoAcceptRules.1")) +		{ +			LLVivoxVoiceClient::getInstance()->accountListAutoAcceptRulesResponse(statusCode, statusString);						 +		} +		else if (!stricmp(actionCstr, "Session.Set3DPosition.1")) +		{ +			// We don't need to process these, but they're so spammy we don't want to log them. +			squelchDebugOutput = true; +		} +		/* +		 else if (!stricmp(actionCstr, "Account.ChannelGetList.1")) +		 { +		 LLVoiceClient::getInstance()->channelGetListResponse(statusCode, statusString); +		 } +		 else if (!stricmp(actionCstr, "Connector.AccountCreate.1")) +		 { +		  +		 } +		 else if (!stricmp(actionCstr, "Connector.MuteLocalMic.1")) +		 { +		  +		 } +		 else if (!stricmp(actionCstr, "Connector.MuteLocalSpeaker.1")) +		 { +		  +		 } +		 else if (!stricmp(actionCstr, "Connector.SetLocalMicVolume.1")) +		 { +		  +		 } +		 else if (!stricmp(actionCstr, "Connector.SetLocalSpeakerVolume.1")) +		 { +		  +		 } +		 else if (!stricmp(actionCstr, "Session.ListenerSetPosition.1")) +		 { +		  +		 } +		 else if (!stricmp(actionCstr, "Session.SpeakerSetPosition.1")) +		 { +		  +		 } +		 else if (!stricmp(actionCstr, "Session.AudioSourceSetPosition.1")) +		 { +		  +		 } +		 else if (!stricmp(actionCstr, "Session.GetChannelParticipants.1")) +		 { +		  +		 } +		 else if (!stricmp(actionCstr, "Account.ChannelCreate.1")) +		 { +		  +		 } +		 else if (!stricmp(actionCstr, "Account.ChannelUpdate.1")) +		 { +		  +		 } +		 else if (!stricmp(actionCstr, "Account.ChannelDelete.1")) +		 { +		  +		 } +		 else if (!stricmp(actionCstr, "Account.ChannelCreateAndInvite.1")) +		 { +		  +		 } +		 else if (!stricmp(actionCstr, "Account.ChannelFolderCreate.1")) +		 { +		  +		 } +		 else if (!stricmp(actionCstr, "Account.ChannelFolderUpdate.1")) +		 { +		  +		 } +		 else if (!stricmp(actionCstr, "Account.ChannelFolderDelete.1")) +		 { +		  +		 } +		 else if (!stricmp(actionCstr, "Account.ChannelAddModerator.1")) +		 { +		  +		 } +		 else if (!stricmp(actionCstr, "Account.ChannelDeleteModerator.1")) +		 { +		  +		 } +		 */ +	} +} + diff --git a/indra/newview/llvoicevivox.h b/indra/newview/llvoicevivox.h new file mode 100644 index 0000000000..10577254e8 --- /dev/null +++ b/indra/newview/llvoicevivox.h @@ -0,0 +1,914 @@ +/**  + * @file llvoicevivox.h + * @brief Declaration of LLDiamondwareVoiceClient class which is the interface to the voice client process. + * + * $LicenseInfo:firstyear=2001&license=viewergpl$ + *  + * Copyright (c) 2001-2010, Linden Research, Inc. + *  + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab.  Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + *  + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + *  + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + *  + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ +#ifndef LL_VOICE_VIVOX_H +#define LL_VOICE_VIVOX_H + +class LLVOAvatar; +class LLVivoxProtocolParser; + +#include "lliopipe.h" +#include "llpumpio.h" +#include "llchainio.h" +#include "lliosocket.h" +#include "v3math.h" +#include "llframetimer.h" +#include "llviewerregion.h" +#include "llcallingcard.h"   // for LLFriendObserver + +#ifdef LL_STANDALONE +# include "expat.h" +#else +# include "expat/expat.h" +#endif +#include "llvoiceclient.h" + + +class LLVivoxVoiceAccountProvisionResponder; +class LLVivoxVoiceClientMuteListObserver; +class LLVivoxVoiceClientFriendsObserver;	 + + +class LLVivoxVoiceClientParticipantObserver +{ +public: +	virtual ~LLVivoxVoiceClientParticipantObserver() { } +	virtual void onChange() = 0; +}; + + +class LLVivoxVoiceClient: public LLSingleton<LLVivoxVoiceClient>, virtual public LLVoiceModuleInterface +{ +	LOG_CLASS(LLVivoxVoiceClient); +public: +	LLVivoxVoiceClient();	 +	virtual ~LLVivoxVoiceClient(); +	 +	 +	/// @name LLVoiceModuleInterface virtual implementations +	///  @see LLVoiceModuleInterface +	//@{ +	virtual void init(LLPumpIO *pump);	// Call this once at application startup (creates connector) +	virtual void terminate();	// Call this to clean up during shutdown +	 +	virtual const LLVoiceVersionInfo& getVersion(); +	 +	virtual void updateSettings(); // call after loading settings and whenever they change + +	// Returns true if vivox has successfully logged in and is not in error state	 +	virtual bool isVoiceWorking(); + +	///////////////////// +	/// @name Tuning +	//@{ +	virtual void tuningStart(); +	virtual void tuningStop(); +	virtual bool inTuningMode(); +	 +	virtual void tuningSetMicVolume(float volume); +	virtual void tuningSetSpeakerVolume(float volume); +	virtual float tuningGetEnergy(void); +	//@} +	 +	///////////////////// +	/// @name Devices +	//@{ +	// This returns true when it's safe to bring up the "device settings" dialog in the prefs. +	// i.e. when the daemon is running and connected, and the device lists are populated. +	virtual bool deviceSettingsAvailable(); +	 +	// Requery the vivox daemon for the current list of input/output devices. +	// If you pass true for clearCurrentList, deviceSettingsAvailable() will be false until the query has completed +	// (use this if you want to know when it's done). +	// If you pass false, you'll have no way to know when the query finishes, but the device lists will not appear empty in the interim. +	virtual void refreshDeviceLists(bool clearCurrentList = true); +	 +	virtual void setCaptureDevice(const std::string& name); +	virtual void setRenderDevice(const std::string& name); +	 +	virtual LLVoiceDeviceList& getCaptureDevices(); +	virtual LLVoiceDeviceList& getRenderDevices(); +	//@}	 +	 +	virtual void getParticipantList(std::set<LLUUID> &participants); +	virtual bool isParticipant(const LLUUID& speaker_id); + +	// Send a text message to the specified user, initiating the session if necessary. +	virtual BOOL sendTextMessage(const LLUUID& participant_id, const std::string& message); +	 +	// close any existing text IM session with the specified user +	virtual void endUserIMSession(const LLUUID &uuid); +	 +	// Returns true if calling back the session URI after the session has closed is possible. +	// Currently this will be false only for PSTN P2P calls.		 +	// NOTE: this will return true if the session can't be found.  +	virtual BOOL isSessionCallBackPossible(const LLUUID &session_id); +	 +	// Returns true if the session can accepte text IM's. +	// Currently this will be false only for PSTN P2P calls. +	// NOTE: this will return true if the session can't be found.  +	virtual BOOL isSessionTextIMPossible(const LLUUID &session_id); +	 +	 +	//////////////////////////// +	/// @name Channel stuff +	//@{ +	// returns true iff the user is currently in a proximal (local spatial) channel. +	// Note that gestures should only fire if this returns true. +	virtual bool inProximalChannel(); +	 +	virtual void setNonSpatialChannel(const std::string &uri, +									  const std::string &credentials); +	 +	virtual void setSpatialChannel(const std::string &uri, +								   const std::string &credentials); +	 +	virtual void leaveNonSpatialChannel(); +	 +	virtual void leaveChannel(void);	 +	 +	// Returns the URI of the current channel, or an empty string if not currently in a channel. +	// NOTE that it will return an empty string if it's in the process of joining a channel. +	virtual std::string getCurrentChannel(); +	//@} +	 +	 +	////////////////////////// +	/// @name invitations +	//@{ +	// start a voice channel with the specified user +	virtual void callUser(const LLUUID &uuid);	 +	virtual bool answerInvite(std::string &channelHandle); +	virtual void declineInvite(std::string &channelHandle); +	//@} +	 +	///////////////////////// +	/// @name Volume/gain +	//@{ +	virtual void setVoiceVolume(F32 volume); +	virtual void setMicGain(F32 volume); +	//@} +	 +	///////////////////////// +	/// @name enable disable voice and features +	//@{ +	virtual bool voiceEnabled(); +	virtual void setVoiceEnabled(bool enabled); +	virtual BOOL lipSyncEnabled();	 +	virtual void setLipSyncEnabled(BOOL enabled); +	virtual void setMuteMic(bool muted);		// Use this to mute the local mic (for when the client is minimized, etc), ignoring user PTT state. +	//@} +	 +	//////////////////////// +	/// @name PTT +	//@{ +	virtual void setUserPTTState(bool ptt); +	virtual bool getUserPTTState(); +	virtual void setUsePTT(bool usePTT); +	virtual void setPTTIsToggle(bool PTTIsToggle); +	virtual bool getPTTIsToggle(); +	virtual void inputUserControlState(bool down);  // interpret any sort of up-down mic-open control input according to ptt-toggle prefs	 +	virtual void toggleUserPTTState(void); +	 +	virtual void keyDown(KEY key, MASK mask); +	virtual void keyUp(KEY key, MASK mask); +	virtual void middleMouseState(bool down); +	//@} +	 +	////////////////////////// +	/// @name nearby speaker accessors +	//@{ +	virtual BOOL getVoiceEnabled(const LLUUID& id);		// true if we've received data for this avatar +	virtual std::string getDisplayName(const LLUUID& id); +	virtual BOOL isOnlineSIP(const LLUUID &id); +	virtual BOOL isParticipantAvatar(const LLUUID &id); +	virtual BOOL getIsSpeaking(const LLUUID& id); +	virtual BOOL getIsModeratorMuted(const LLUUID& id); +	virtual F32 getCurrentPower(const LLUUID& id);		// "power" is related to "amplitude" in a defined way.  I'm just not sure what the formula is... +	virtual BOOL getOnMuteList(const LLUUID& id); +	virtual F32 getUserVolume(const LLUUID& id); +	virtual void setUserVolume(const LLUUID& id, F32 volume); // set's volume for specified agent, from 0-1 (where .5 is nominal)	 +	//@} +	 +	// authorize the user +	virtual void userAuthorized(const std::string& user_id, +								const LLUUID &agentID); +	 +	////////////////////////////// +	/// @name Status notification +	//@{ +	virtual void addObserver(LLVoiceClientStatusObserver* observer); +	virtual void removeObserver(LLVoiceClientStatusObserver* observer); +	virtual void addObserver(LLFriendObserver* observer); +	virtual void removeObserver(LLFriendObserver* observer);		 +	virtual void addObserver(LLVoiceClientParticipantObserver* observer); +	virtual void removeObserver(LLVoiceClientParticipantObserver* observer); +	 +	 +	 +	//@} +	 +	virtual std::string sipURIFromID(const LLUUID &id); +	//@} + +				 +protected: +	////////////////////// +	// Vivox Specific definitions	 +	 +	friend class LLVivoxVoiceAccountProvisionResponder; +	friend class LLVivoxVoiceClientMuteListObserver; +	friend class LLVivoxVoiceClientFriendsObserver;	 +	 +	enum streamState +	{ +		streamStateUnknown = 0, +		streamStateIdle = 1, +		streamStateConnected = 2, +		streamStateRinging = 3, +	};	 +	struct participantState +	{ +	public: +		participantState(const std::string &uri); +		 +		bool updateMuteState(); +		bool isAvatar(); +		 +		std::string mURI; +		LLUUID mAvatarID; +		std::string mAccountName; +		std::string mDisplayName; +		LLFrameTimer mSpeakingTimeout; +		F32	mLastSpokeTimestamp; +		F32 mPower; +		int mVolume; +		std::string mGroupID; +		int mUserVolume; +		bool mPTT; +		bool mIsSpeaking; +		bool mIsModeratorMuted; +		bool mOnMuteList;		// true if this avatar is on the user's mute list (and should be muted) +		bool mVolumeDirty;		// true if this participant needs a volume command sent (either mOnMuteList or mUserVolume has changed) +		bool mAvatarIDValid; +		bool mIsSelf; +	}; +	 +	typedef std::map<const std::string, participantState*> participantMap; +	 +	typedef std::map<const LLUUID, participantState*> participantUUIDMap; +	 +	struct sessionState +	{ +	public: +		sessionState(); +		~sessionState(); +		 +		participantState *addParticipant(const std::string &uri); +		// Note: after removeParticipant returns, the participant* that was passed to it will have been deleted. +		// Take care not to use the pointer again after that. +		void removeParticipant(participantState *participant); +		void removeAllParticipants(); +		 +		participantState *findParticipant(const std::string &uri); +		participantState *findParticipantByID(const LLUUID& id); +		 +		bool isCallBackPossible(); +		bool isTextIMPossible(); +		 +		std::string mHandle; +		std::string mGroupHandle; +		std::string mSIPURI; +		std::string mAlias; +		std::string mName; +		std::string mAlternateSIPURI; +		std::string mHash;			// Channel password +		std::string mErrorStatusString; +		std::queue<std::string> mTextMsgQueue; +		 +		LLUUID		mIMSessionID; +		LLUUID		mCallerID; +		int			mErrorStatusCode; +		int			mMediaStreamState; +		int			mTextStreamState; +		bool		mCreateInProgress;	// True if a Session.Create has been sent for this session and no response has been received yet. +		bool		mMediaConnectInProgress;	// True if a Session.MediaConnect has been sent for this session and no response has been received yet. +		bool		mVoiceInvitePending;	// True if a voice invite is pending for this session (usually waiting on a name lookup) +		bool		mTextInvitePending;		// True if a text invite is pending for this session (usually waiting on a name lookup) +		bool		mSynthesizedCallerID;	// True if the caller ID is a hash of the SIP URI -- this means we shouldn't do a name lookup. +		bool		mIsChannel;	// True for both group and spatial channels (false for p2p, PSTN) +		bool		mIsSpatial;	// True for spatial channels +		bool		mIsP2P; +		bool		mIncoming; +		bool		mVoiceEnabled; +		bool		mReconnect;	// Whether we should try to reconnect to this session if it's dropped +		// Set to true when the mute state of someone in the participant list changes. +		// The code will have to walk the list to find the changed participant(s). +		bool		mVolumeDirty; +		 +		bool		mParticipantsChanged; +		participantMap mParticipantsByURI; +		participantUUIDMap mParticipantsByUUID; +	}; + +	// internal state for a simple state machine.  This is used to deal with the asynchronous nature of some of the messages. +	// Note: if you change this list, please make corresponding changes to LLVivoxVoiceClient::state2string(). +	enum state +	{ +		stateDisableCleanup, +		stateDisabled,				// Voice is turned off. +		stateStart,					// Class is initialized, socket is created +		stateDaemonLaunched,		// Daemon has been launched +		stateConnecting,			// connect() call has been issued +		stateConnected,				// connection to the daemon has been made, send some initial setup commands. +		stateIdle,					// socket is connected, ready for messaging +		stateMicTuningStart, +		stateMicTuningRunning,		 +		stateMicTuningStop, +		stateConnectorStart,		// connector needs to be started +		stateConnectorStarting,		// waiting for connector handle +		stateConnectorStarted,		// connector handle received +		stateLoginRetry,			// need to retry login (failed due to changing password) +		stateLoginRetryWait,		// waiting for retry timer +		stateNeedsLogin,			// send login request +		stateLoggingIn,				// waiting for account handle +		stateLoggedIn,				// account handle received +		stateCreatingSessionGroup,	// Creating the main session group +		stateNoChannel,				//  +		stateJoiningSession,		// waiting for session handle +		stateSessionJoined,			// session handle received +		stateRunning,				// in session, steady state +		stateLeavingSession,		// waiting for terminate session response +		stateSessionTerminated,		// waiting for terminate session response +		 +		stateLoggingOut,			// waiting for logout response +		stateLoggedOut,				// logout response received +		stateConnectorStopping,		// waiting for connector stop +		stateConnectorStopped,		// connector stop received +		 +		// We go to this state if the login fails because the account needs to be provisioned. +		 +		// error states.  No way to recover from these yet. +		stateConnectorFailed, +		stateConnectorFailedWaiting, +		stateLoginFailed, +		stateLoginFailedWaiting, +		stateJoinSessionFailed, +		stateJoinSessionFailedWaiting, +		 +		stateJail					// Go here when all else has failed.  Nothing will be retried, we're done. +	}; +	 +	typedef std::map<std::string, sessionState*> sessionMap; +	 +	 +	 +	/////////////////////////////////////////////////////// +	// Private Member Functions +	////////////////////////////////////////////////////// +	 +	////////////////////////////// +	/// @name TVC/Server management and communication +	//@{ +	// Call this if the connection to the daemon terminates unexpectedly.  It will attempt to reset everything and relaunch. +	void daemonDied(); +	 +	// Call this if we're just giving up on voice (can't provision an account, etc.).  It will clean up and go away. +	void giveUp();	 +	 +	// write to the tvc +	bool writeString(const std::string &str); +	 +	void connectorCreate(); +	void connectorShutdown();	 +	void closeSocket(void);	 +	 +	void requestVoiceAccountProvision(S32 retries = 3); +	void login( +			   const std::string& account_name, +			   const std::string& password, +			   const std::string& voice_sip_uri_hostname, +			   const std::string& voice_account_server_uri); +	void loginSendMessage(); +	void logout(); +	void logoutSendMessage();	 +	 +	 +	//@} +	 +	//------------------------------------ +	// tuning +	 +	void tuningRenderStartSendMessage(const std::string& name, bool loop); +	void tuningRenderStopSendMessage(); + +	void tuningCaptureStartSendMessage(int duration); +	void tuningCaptureStopSendMessage(); + +	bool inTuningStates(); + +	//---------------------------------- +	// devices +	void clearCaptureDevices(); +	void addCaptureDevice(const std::string& name); +	void clearRenderDevices(); +	void addRenderDevice(const std::string& name);	 +	void buildSetAudioDevices(std::ostringstream &stream); +	 +	void getCaptureDevicesSendMessage(); +	void getRenderDevicesSendMessage(); +	 +	// local audio updates +	void buildLocalAudioUpdates(std::ostringstream &stream);		 + + +	///////////////////////////// +	// Response/Event handlers +	void connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle, std::string &versionID); +	void loginResponse(int statusCode, std::string &statusString, std::string &accountHandle, int numberOfAliases); +	void sessionCreateResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); +	void sessionGroupAddSessionResponse(std::string &requestId, int statusCode, std::string &statusString, std::string &sessionHandle); +	void sessionConnectResponse(std::string &requestId, int statusCode, std::string &statusString); +	void logoutResponse(int statusCode, std::string &statusString); +	void connectorShutdownResponse(int statusCode, std::string &statusString); + +	void accountLoginStateChangeEvent(std::string &accountHandle, int statusCode, std::string &statusString, int state); +	void mediaStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, int statusCode, std::string &statusString, int state, bool incoming); +	void textStreamUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, bool enabled, int state, bool incoming); +	void sessionAddedEvent(std::string &uriString, std::string &alias, std::string &sessionHandle, std::string &sessionGroupHandle, bool isChannel, bool incoming, std::string &nameString, std::string &applicationString); +	void sessionGroupAddedEvent(std::string &sessionGroupHandle); +	void sessionRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle); +	void participantAddedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString, std::string &displayNameString, int participantType); +	void participantRemovedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, std::string &nameString); +	void participantUpdatedEvent(std::string &sessionHandle, std::string &sessionGroupHandle, std::string &uriString, std::string &alias, bool isModeratorMuted, bool isSpeaking, int volume, F32 energy); +	void auxAudioPropertiesEvent(F32 energy); +	void buddyPresenceEvent(std::string &uriString, std::string &alias, std::string &statusString, std::string &applicationString); +	void messageEvent(std::string &sessionHandle, std::string &uriString, std::string &alias, std::string &messageHeader, std::string &messageBody, std::string &applicationString); +	void sessionNotificationEvent(std::string &sessionHandle, std::string &uriString, std::string ¬ificationType); +	void subscriptionEvent(std::string &buddyURI, std::string &subscriptionHandle, std::string &alias, std::string &displayName, std::string &applicationString, std::string &subscriptionType); +	 +	void buddyListChanged(); +	void muteListChanged(); +	void updateFriends(U32 mask); +		 +	///////////////////////////// +	// Sending updates of current state +	void updatePosition(void); +	void setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot); +	void setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot); +	bool channelFromRegion(LLViewerRegion *region, std::string &name); + +	void setEarLocation(S32 loc); + +	 +	///////////////////////////// +	// Accessors for data related to nearby speakers + +	// MBW -- XXX -- Not sure how to get this data out of the TVC +	BOOL getUsingPTT(const LLUUID& id); +	std::string getGroupID(const LLUUID& id);		// group ID if the user is in group chat (empty string if not applicable) + +	///////////////////////////// +	BOOL getAreaVoiceDisabled();		// returns true if the area the avatar is in is speech-disabled. +										// Use this to determine whether to show a "no speech" icon in the menu bar. +		 +	 +	// PTT +	void setPTTKey(std::string &key); +	 +	///////////////////////////// +	// Recording controls +	void recordingLoopStart(int seconds = 3600, int deltaFramesPerControlFrame = 200); +	void recordingLoopSave(const std::string& filename); +	void recordingStop(); +	 +	// Playback controls +	void filePlaybackStart(const std::string& filename); +	void filePlaybackStop(); +	void filePlaybackSetPaused(bool paused); +	void filePlaybackSetMode(bool vox = false, float speed = 1.0f); +	 +	participantState *findParticipantByID(const LLUUID& id); +	 + +	//////////////////////////////////////// +	// voice sessions. +	typedef std::set<sessionState*> sessionSet; +			 +	typedef sessionSet::iterator sessionIterator; +	sessionIterator sessionsBegin(void); +	sessionIterator sessionsEnd(void); + +	sessionState *findSession(const std::string &handle); +	sessionState *findSessionBeingCreatedByURI(const std::string &uri); +	sessionState *findSession(const LLUUID &participant_id); +	sessionState *findSessionByCreateID(const std::string &create_id); +	 +	sessionState *addSession(const std::string &uri, const std::string &handle = LLStringUtil::null); +	void setSessionHandle(sessionState *session, const std::string &handle = LLStringUtil::null); +	void setSessionURI(sessionState *session, const std::string &uri); +	void deleteSession(sessionState *session); +	void deleteAllSessions(void); + +	void verifySessionState(void); + +	void joinedAudioSession(sessionState *session); +	void leftAudioSession(sessionState *session); + +	// This is called in several places where the session _may_ need to be deleted. +	// It contains logic for whether to delete the session or keep it around. +	void reapSession(sessionState *session); +	 +	// Returns true if the session seems to indicate we've moved to a region on a different voice server +	bool sessionNeedsRelog(sessionState *session); +	 +	 +	////////////////////////////////////// +	// buddy list stuff, needed for SLIM later +	struct buddyListEntry +	{ +		buddyListEntry(const std::string &uri); +		std::string mURI; +		std::string mDisplayName; +		LLUUID	mUUID; +		bool mOnlineSL; +		bool mOnlineSLim; +		bool mCanSeeMeOnline; +		bool mHasBlockListEntry; +		bool mHasAutoAcceptListEntry; +		bool mNameResolved; +		bool mInSLFriends; +		bool mInVivoxBuddies; +		bool mNeedsNameUpdate; +	}; + +	typedef std::map<std::string, buddyListEntry*> buddyListMap; +	 +	// This should be called when parsing a buddy list entry sent by SLVoice.		 +	void processBuddyListEntry(const std::string &uri, const std::string &displayName); + +	buddyListEntry *addBuddy(const std::string &uri); +	buddyListEntry *addBuddy(const std::string &uri, const std::string &displayName); +	buddyListEntry *findBuddy(const std::string &uri); +	buddyListEntry *findBuddy(const LLUUID &id); +	buddyListEntry *findBuddyByDisplayName(const std::string &name); +	void deleteBuddy(const std::string &uri); +	void deleteAllBuddies(void); + +	void deleteAllBlockRules(void); +	void addBlockRule(const std::string &blockMask, const std::string &presenceOnly); +	void deleteAllAutoAcceptRules(void); +	void addAutoAcceptRule(const std::string &autoAcceptMask, const std::string &autoAddAsBuddy); +	void accountListBlockRulesResponse(int statusCode, const std::string &statusString);						 +	void accountListAutoAcceptRulesResponse(int statusCode, const std::string &statusString);						 +	 +	///////////////////////////// +	// session control messages + +	void accountListBlockRulesSendMessage(); +	void accountListAutoAcceptRulesSendMessage(); +	 +	void sessionGroupCreateSendMessage(); +	void sessionCreateSendMessage(sessionState *session, bool startAudio = true, bool startText = false); +	void sessionGroupAddSessionSendMessage(sessionState *session, bool startAudio = true, bool startText = false); +	void sessionMediaConnectSendMessage(sessionState *session);		// just joins the audio session +	void sessionTextConnectSendMessage(sessionState *session);		// just joins the text session +	void sessionTerminateSendMessage(sessionState *session); +	void sessionGroupTerminateSendMessage(sessionState *session); +	void sessionMediaDisconnectSendMessage(sessionState *session); +	void sessionTextDisconnectSendMessage(sessionState *session); + +	// Pokes the state machine to leave the audio session next time around. +	void sessionTerminate();	 +	 +	// Pokes the state machine to shut down the connector and restart it. +	void requestRelog(); +	 +	// Does the actual work to get out of the audio session +	void leaveAudioSession(); +	 +	void lookupName(const LLUUID &id); +	static void onAvatarNameLookup(const LLUUID& id, const std::string& first, const std::string& last, BOOL is_group); +	void avatarNameResolved(const LLUUID &id, const std::string &name); +		 +private: +	LLVoiceVersionInfo mVoiceVersion; +		 +	state mState; +	bool mSessionTerminateRequested; +	bool mRelogRequested; +	// Number of times (in a row) "stateJoiningSession" case for spatial channel is reached in stateMachine(). +	// The larger it is the greater is possibility there is a problem with connection to voice server. +	// Introduced while fixing EXT-4313. +	int mSpatialJoiningNum; +	 +	void setState(state inState); +	state getState(void)  { return mState; }; +	std::string state2string(state inState); +	 +	void stateMachine(); +	static void idle(void *user_data); +	 +	LLHost mDaemonHost; +	LLSocket::ptr_t mSocket; +	bool mConnected; +	 +	 +	LLPumpIO *mPump; +	friend class LLVivoxProtocolParser; +	 +	std::string mAccountName; +	std::string mAccountPassword; +	std::string mAccountDisplayName; +			 +	bool mTuningMode; +	float mTuningEnergy; +	std::string mTuningAudioFile; +	int mTuningMicVolume; +	bool mTuningMicVolumeDirty; +	int mTuningSpeakerVolume; +	bool mTuningSpeakerVolumeDirty; +	state mTuningExitState;					// state to return to when we leave tuning mode. +	 +	std::string mSpatialSessionURI; +	std::string mSpatialSessionCredentials; + +	std::string mMainSessionGroupHandle; // handle of the "main" session group. +	 +	std::string mChannelName;			// Name of the channel to be looked up  +	bool mAreaVoiceDisabled; +	sessionState *mAudioSession;		// Session state for the current audio session +	bool mAudioSessionChanged;			// set to true when the above pointer gets changed, so observers can be notified. + +	sessionState *mNextAudioSession;	// Session state for the audio session we're trying to join + +//		std::string mSessionURI;			// URI of the session we're in. +//		std::string mSessionHandle;		// returned by ? +	 +	S32 mCurrentParcelLocalID;			// Used to detect parcel boundary crossings +	std::string mCurrentRegionName;		// Used to detect parcel boundary crossings +	 +	std::string mConnectorHandle;	// returned by "Create Connector" message +	std::string mAccountHandle;		// returned by login message		 +	int 		mNumberOfAliases; +	U32 mCommandCookie; + +	std::string mVoiceAccountServerURI; +	std::string mVoiceSIPURIHostName; +	 +	int mLoginRetryCount; +	 +	sessionMap mSessionsByHandle;				// Active sessions, indexed by session handle.  Sessions which are being initiated may not be in this map. +	sessionSet mSessions;						// All sessions, not indexed.  This is the canonical session list. +	 +	bool mBuddyListMapPopulated; +	bool mBlockRulesListReceived; +	bool mAutoAcceptRulesListReceived; +	buddyListMap mBuddyListMap; +	 +	LLVoiceDeviceList mCaptureDevices; +	LLVoiceDeviceList mRenderDevices; + +	std::string mCaptureDevice; +	std::string mRenderDevice; +	bool mCaptureDeviceDirty; +	bool mRenderDeviceDirty; +	 +	// This should be called when the code detects we have changed parcels. +	// It initiates the call to the server that gets the parcel channel. +	void parcelChanged(); +	 +	void switchChannel(std::string uri = std::string(), bool spatial = true, bool no_reconnect = false, bool is_p2p = false, std::string hash = ""); +	void joinSession(sessionState *session); +	 +	std::string nameFromAvatar(LLVOAvatar *avatar); +	std::string nameFromID(const LLUUID &id); +	bool IDFromName(const std::string name, LLUUID &uuid); +	std::string displayNameFromAvatar(LLVOAvatar *avatar); +	std::string sipURIFromAvatar(LLVOAvatar *avatar); +	std::string sipURIFromName(std::string &name); +	 +	// Returns the name portion of the SIP URI if the string looks vaguely like a SIP URI, or an empty string if not. +	std::string nameFromsipURI(const std::string &uri);		 + +	bool inSpatialChannel(void); +	std::string getAudioSessionURI(); +	std::string getAudioSessionHandle(); +			 +	void sendPositionalUpdate(void); +	 +	void buildSetCaptureDevice(std::ostringstream &stream); +	void buildSetRenderDevice(std::ostringstream &stream); +	 +	void clearAllLists(); +	void checkFriend(const LLUUID& id); +	void sendFriendsListUpdates(); + +	// start a text IM session with the specified user +	// This will be asynchronous, the session may be established at a future time. +	sessionState* startUserIMSession(const LLUUID& uuid); +	void sendQueuedTextMessages(sessionState *session); +	 +	void enforceTether(void); +	 +	bool		mSpatialCoordsDirty; +	 +	LLVector3d	mCameraPosition; +	LLVector3d	mCameraRequestedPosition; +	LLVector3	mCameraVelocity; +	LLMatrix3	mCameraRot; + +	LLVector3d	mAvatarPosition; +	LLVector3	mAvatarVelocity; +	LLMatrix3	mAvatarRot; +	 +	bool		mPTTDirty; +	bool		mPTT; +	 +	bool		mUsePTT; +	bool		mPTTIsMiddleMouse; +	KEY			mPTTKey; +	bool		mPTTIsToggle; +	bool		mUserPTTState; +	bool		mMuteMic; +			 +	// Set to true when the friends list is known to have changed. +	bool		mFriendsListDirty; +	 +	enum +	{ +		earLocCamera = 0,		// ear at camera +		earLocAvatar,			// ear at avatar +		earLocMixed				// ear at avatar location/camera direction +	}; +	 +	S32			mEarLocation;   +	 +	bool		mSpeakerVolumeDirty; +	bool		mSpeakerMuteDirty; +	int			mSpeakerVolume; + +	int			mMicVolume; +	bool		mMicVolumeDirty; +	 +	bool		mVoiceEnabled; +	bool		mWriteInProgress; +	std::string mWriteString; +	size_t		mWriteOffset; +	 +	LLTimer		mUpdateTimer; +	 +	BOOL		mLipSyncEnabled; + +	typedef std::set<LLVoiceClientParticipantObserver*> observer_set_t; +	observer_set_t mParticipantObservers; + +	void notifyParticipantObservers(); + +	typedef std::set<LLVoiceClientStatusObserver*> status_observer_set_t; +	status_observer_set_t mStatusObservers; +	 +	void notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status); + +	typedef std::set<LLFriendObserver*> friend_observer_set_t; +	friend_observer_set_t mFriendObservers; +	void notifyFriendObservers(); +}; + +/**  + * @class LLVivoxProtocolParser + * @brief This class helps construct new LLIOPipe specializations + * @see LLIOPipe + * + * THOROUGH_DESCRIPTION + */ +class LLVivoxProtocolParser : public LLIOPipe +{ +	LOG_CLASS(LLVivoxProtocolParser); +public: +	LLVivoxProtocolParser(); +	virtual ~LLVivoxProtocolParser(); +	 +protected: +	/* @name LLIOPipe virtual implementations +	 */ +	//@{ +	/**  +	 * @brief Process the data in buffer +	 */ +	virtual EStatus process_impl( +								 const LLChannelDescriptors& channels, +								 buffer_ptr_t& buffer, +								 bool& eos, +								 LLSD& context, +								 LLPumpIO* pump); +	//@} +	 +	std::string 	mInput; +	 +	// Expat control members +	XML_Parser		parser; +	int				responseDepth; +	bool			ignoringTags; +	bool			isEvent; +	int				ignoreDepth; +	 +	// Members for processing responses. The values are transient and only valid within a call to processResponse(). +	bool			squelchDebugOutput; +	int				returnCode; +	int				statusCode; +	std::string		statusString; +	std::string		requestId; +	std::string		actionString; +	std::string		connectorHandle; +	std::string		versionID; +	std::string		accountHandle; +	std::string		sessionHandle; +	std::string		sessionGroupHandle; +	std::string		alias; +	std::string		applicationString; +	 +	// Members for processing events. The values are transient and only valid within a call to processResponse(). +	std::string		eventTypeString; +	int				state; +	std::string		uriString; +	bool			isChannel; +	bool			incoming; +	bool			enabled; +	std::string		nameString; +	std::string		audioMediaString; +	std::string     deviceString; +	std::string		displayNameString; +	int				participantType; +	bool			isLocallyMuted; +	bool			isModeratorMuted; +	bool			isSpeaking; +	int				volume; +	F32				energy; +	std::string		messageHeader; +	std::string		messageBody; +	std::string		notificationType; +	bool			hasText; +	bool			hasAudio; +	bool			hasVideo; +	bool			terminated; +	std::string		blockMask; +	std::string		presenceOnly; +	std::string		autoAcceptMask; +	std::string		autoAddAsBuddy; +	int				numberOfAliases; +	std::string		subscriptionHandle; +	std::string		subscriptionType; +	 +	 +	// Members for processing text between tags +	std::string		textBuffer; +	bool			accumulateText; +	 +	void			reset(); +	 +	void			processResponse(std::string tag); +	 +	static void XMLCALL ExpatStartTag(void *data, const char *el, const char **attr); +	static void XMLCALL ExpatEndTag(void *data, const char *el); +	static void XMLCALL ExpatCharHandler(void *data, const XML_Char *s, int len); +	 +	void			StartTag(const char *tag, const char **attr); +	void			EndTag(const char *tag); +	void			CharData(const char *buffer, int length); +	 +}; + + +#endif //LL_VIVOX_VOICE_CLIENT_H + + + diff --git a/indra/newview/llweb.cpp b/indra/newview/llweb.cpp index 1a64f9d881..aa03b1afd1 100644 --- a/indra/newview/llweb.cpp +++ b/indra/newview/llweb.cpp @@ -155,7 +155,7 @@ std::string LLWeb::expandURLSubstitutions(const std::string &url,  	substitution["VERSION_PATCH"] = LLVersionInfo::getPatch();  	substitution["VERSION_BUILD"] = LLVersionInfo::getBuild();  	substitution["CHANNEL"] = LLVersionInfo::getChannel(); -	substitution["GRID"] = LLViewerLogin::getInstance()->getGridLabel(); +	substitution["GRID"] = LLGridManager::getInstance()->getGridLabel();  	substitution["OS"] = LLAppViewer::instance()->getOSInfo().getOSStringSimple();  	substitution["SESSION_ID"] = gAgent.getSessionID();  	substitution["FIRST_LOGIN"] = gAgent.isFirstLogin(); diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp index 0b63f5efbd..58b9f5ce18 100644 --- a/indra/newview/llworld.cpp +++ b/indra/newview/llworld.cpp @@ -133,10 +133,11 @@ void LLWorld::destroyClass()  LLViewerRegion* LLWorld::addRegion(const U64 ®ion_handle, const LLHost &host)  {  	LLMemType mt(LLMemType::MTYPE_REGIONS); -	 +	llinfos << "Add region with handle: " << region_handle << " on host " << host << llendl;  	LLViewerRegion *regionp = getRegionFromHandle(region_handle);  	if (regionp)  	{ +		llinfos << "Region exists, removing it " << llendl;  		LLHost old_host = regionp->getHost();  		// region already exists!  		if (host == old_host && regionp->isAlive()) diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp index 7afe81b436..0c37bb6eb1 100644 --- a/indra/newview/llworldmapview.cpp +++ b/indra/newview/llworldmapview.cpp @@ -886,28 +886,36 @@ void LLWorldMapView::drawFrustum()  	F32 half_width_meters = far_clip_meters * tan( horiz_fov / 2 );  	F32 half_width_pixels = half_width_meters * meters_to_pixels; -	F32 ctr_x = getRect().getWidth() * 0.5f + sPanX; -	F32 ctr_y = getRect().getHeight() * 0.5f + sPanY; +	F32 ctr_x = getLocalRect().getWidth() * 0.5f + sPanX; +	F32 ctr_y = getLocalRect().getHeight() * 0.5f + sPanY;  	gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);  	// Since we don't rotate the map, we have to rotate the frustum.  	gGL.pushMatrix(); +	{  		gGL.translatef( ctr_x, ctr_y, 0 ); -		glRotatef( atan2( LLViewerCamera::getInstance()->getAtAxis().mV[VX], LLViewerCamera::getInstance()->getAtAxis().mV[VY] ) * RAD_TO_DEG, 0.f, 0.f, -1.f);  		// Draw triangle with more alpha in far pixels to make it   		// fade out in distance.  		gGL.begin( LLRender::TRIANGLES  ); +		{ +			LLVector2 cam_lookat(LLViewerCamera::instance().getAtAxis().mV[VX], LLViewerCamera::instance().getAtAxis().mV[VY]); +			LLVector2 cam_left(LLViewerCamera::instance().getLeftAxis().mV[VX], LLViewerCamera::instance().getLeftAxis().mV[VY]); +  			gGL.color4f(1.f, 1.f, 1.f, 0.25f);  			gGL.vertex2f( 0, 0 );  			gGL.color4f(1.f, 1.f, 1.f, 0.02f); -			gGL.vertex2f( -half_width_pixels, far_clip_pixels ); +			 +			LLVector2 vert = cam_lookat * far_clip_pixels + cam_left * half_width_pixels; +			gGL.vertex2f(vert.mV[VX], vert.mV[VY]); -			gGL.color4f(1.f, 1.f, 1.f, 0.02f); -			gGL.vertex2f(  half_width_pixels, far_clip_pixels ); +			vert = cam_lookat * far_clip_pixels - cam_left * half_width_pixels; +			gGL.vertex2f(vert.mV[VX], vert.mV[VY]); +		}  		gGL.end(); +	}  	gGL.popMatrix();  } diff --git a/indra/newview/llxmlrpclistener.cpp b/indra/newview/llxmlrpclistener.cpp index 15417614af..8237132ac5 100644 --- a/indra/newview/llxmlrpclistener.cpp +++ b/indra/newview/llxmlrpclistener.cpp @@ -28,6 +28,7 @@  #include "llerror.h"  #include "stringize.h"  #include "llxmlrpctransaction.h" +#include "llsecapi.h"  #if LL_WINDOWS  #pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally @@ -356,7 +357,22 @@ public:                                       << data["errorcode"].asString()                                       << " (" << data["error"].asString() << ")"                                       << LL_ENDL; -        // In addition to CURLE_OK, LLUserAuth distinguishes different error +		 +		switch (curlcode) +		{ +			case CURLE_SSL_PEER_CERTIFICATE: +			case CURLE_SSL_CACERT: +			{ +				LLPointer<LLCertificate> error_cert(mTransaction->getErrorCert()); +				if(error_cert) +				{ +					data["certificate"] = error_cert->getPem(); +				} +				break; +			} +			default: +				break; +		}          // values of 'curlcode':          // CURLE_COULDNT_RESOLVE_HOST,          // CURLE_SSL_PEER_CERTIFICATE, diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index c19be37e75..d75c8ff1fb 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -31,6 +31,9 @@   */  #include "llviewerprecompiledheaders.h" +#include <openssl/x509_vfy.h> +#include <openssl/ssl.h> +#include "llsecapi.h"  #include "llxmlrpctransaction.h"  #include "llxmlrpclistener.h" @@ -176,6 +179,8 @@ public:  	std::string			mResponseText;  	XMLRPC_REQUEST		mResponse; +	std::string         mCertStore; +	LLPointer<LLCertificate> mErrorCert;  	Impl(const std::string& uri, XMLRPC_REQUEST request, bool useGzip);  	Impl(const std::string& uri, @@ -190,7 +195,8 @@ public:  private:  	void init(XMLRPC_REQUEST request, bool useGzip); - +	static int _sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param); +	static CURLcode _sslCtxFunction(CURL * curl, void *sslctx, void *param);  	static size_t curlDownloadCallback(  		char* data, size_t size, size_t nmemb, void* user_data);  }; @@ -228,8 +234,74 @@ LLXMLRPCTransaction::Impl::Impl(const std::string& uri,      XMLRPC_RequestFree(request, 1);  } +// _sslCertVerifyCallback +// callback called when a cert verification is requested. +// calls SECAPI to validate the context +int LLXMLRPCTransaction::Impl::_sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param) +{ +	LLXMLRPCTransaction::Impl *transaction = (LLXMLRPCTransaction::Impl *)param; +	LLPointer<LLCertificateStore> store = gSecAPIHandler->getCertificateStore(transaction->mCertStore); +	LLPointer<LLCertificateChain> chain = gSecAPIHandler->getCertificateChain(ctx); +	LLSD validation_params = LLSD::emptyMap(); +	LLURI uri(transaction->mURI); +	validation_params[CERT_HOSTNAME] = uri.hostName(); +	try +	{ +		chain->validate(VALIDATION_POLICY_SSL, store, validation_params); +	} +	catch (LLCertValidationTrustException& cert_exception) +	{ +		// this exception is is handled differently than the general cert +		// exceptions, as we allow the user to actually add the certificate +		// for trust. +		// therefore we pass back a different error code +		// NOTE: We're currently 'wired' to pass around CURL error codes.  This is +		// somewhat clumsy, as we may run into errors that do not map directly to curl +		// error codes.  Should be refactored with login refactoring, perhaps. +		transaction->mCurlCode = CURLE_SSL_CACERT; +		// set the status directly.  set curl status generates error messages and we want +		// to use the fixed ones from the exceptions +		transaction->setStatus(StatusCURLError, cert_exception.getMessage(), std::string()); +		// We should probably have a more generic way of passing information +		// back to the error handlers. +		transaction->mErrorCert = cert_exception.getCert(); +		return 0;		 +	} +	catch (LLCertException& cert_exception) +	{ +		transaction->mCurlCode = CURLE_SSL_PEER_CERTIFICATE; +		// set the status directly.  set curl status generates error messages and we want +		// to use the fixed ones from the exceptions +		transaction->setStatus(StatusCURLError, cert_exception.getMessage(), std::string()); +		transaction->mErrorCert = cert_exception.getCert(); +		return 0; +	} +	catch (...) +	{ +		// any other odd error, we just handle as a connect error. +		transaction->mCurlCode = CURLE_SSL_CONNECT_ERROR; +		transaction->setCurlStatus(CURLE_SSL_CONNECT_ERROR); +		return 0; +	} +	return 1; +} +// _sslCtxFunction +// Callback function called when an SSL Context is created via CURL +// used to configure the context for custom cert validate(<, <#const & xs#>, <#T * #>, <#long #>)tion +// based on SECAPI +CURLcode LLXMLRPCTransaction::Impl::_sslCtxFunction(CURL * curl, void *sslctx, void *param) +{ +	SSL_CTX * ctx = (SSL_CTX *) sslctx; +	// disable any default verification for server certs +	SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); +	// set the verification callback. +	SSL_CTX_set_cert_verify_callback(ctx, _sslCertVerifyCallback, param); +	// the calls are void +	return CURLE_OK; +	 +}  void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip)  { @@ -237,6 +309,7 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip)  	{  		mCurlRequest = new LLCurlEasyRequest();  	} +	mErrorCert = NULL;  	if (gSavedSettings.getBOOL("BrowserProxyEnabled"))  	{ @@ -252,10 +325,13 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip)  //	mCurlRequest->setopt(CURLOPT_VERBOSE, 1); // usefull for debugging  	mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1);  	mCurlRequest->setWriteCallback(&curlDownloadCallback, (void*)this); -	mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, LLCurl::getSSLVerify()); -	mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, LLCurl::getSSLVerify() ? 2 : 0); +	BOOL verifySSLCert = !gSavedSettings.getBOOL("NoVerifySSLCert"); +	mCertStore = gSavedSettings.getString("CertStore"); +	mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, verifySSLCert); +	mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, verifySSLCert ? 2 : 0);  	// Be a little impatient about establishing connections.  	mCurlRequest->setopt(CURLOPT_CONNECTTIMEOUT, 40L); +	mCurlRequest->setSSLCtxCallback(_sslCtxFunction, (void *)this);  	/* Setting the DNS cache timeout to -1 disables it completely.  	   This might help with bug #503 */ @@ -341,11 +417,19 @@ bool LLXMLRPCTransaction::Impl::process()  		{  			if (result != CURLE_OK)  			{ -				setCurlStatus(result); -				llwarns << "LLXMLRPCTransaction CURL error " -						<< mCurlCode << ": " << mCurlRequest->getErrorString() << llendl; -				llwarns << "LLXMLRPCTransaction request URI: " -						<< mURI << llendl; +				if ((result != CURLE_SSL_PEER_CERTIFICATE) && +					(result != CURLE_SSL_CACERT)) +				{ +					// if we have a curl error that's not already been handled +					// (a non cert error), then generate the error message as +					// appropriate +					setCurlStatus(result); +				 +					llwarns << "LLXMLRPCTransaction CURL error " +					<< mCurlCode << ": " << mCurlRequest->getErrorString() << llendl; +					llwarns << "LLXMLRPCTransaction request URI: " +					<< mURI << llendl; +				}  				return true;  			} @@ -423,7 +507,6 @@ void LLXMLRPCTransaction::Impl::setStatus(EStatus status,  			case StatusComplete:  				mStatusMessage = "(done)";  				break; -				  			default:  				// Usually this means that there's a problem with the login server,  				// not with the client.  Direct user to status page. @@ -539,6 +622,11 @@ std::string LLXMLRPCTransaction::statusMessage()  	return impl.mStatusMessage;  } +LLPointer<LLCertificate> LLXMLRPCTransaction::getErrorCert() +{ +	return impl.mErrorCert; +} +  std::string LLXMLRPCTransaction::statusURI()  {  	return impl.mStatusURI; diff --git a/indra/newview/llxmlrpctransaction.h b/indra/newview/llxmlrpctransaction.h index c835423d67..8beb2e2623 100644 --- a/indra/newview/llxmlrpctransaction.h +++ b/indra/newview/llxmlrpctransaction.h @@ -38,6 +38,7 @@  typedef struct _xmlrpc_request* XMLRPC_REQUEST;  typedef struct _xmlrpc_value* XMLRPC_VALUE;  	// foward decl of types from xmlrpc.h (this usage is type safe) +class LLCertificate;  class LLXMLRPCValue  	// a c++ wrapper around XMLRPC_VALUE @@ -115,6 +116,8 @@ public:  	EStatus status(int* curlCode);  		// return status, and extended CURL code, if code isn't null +	 +	LLPointer<LLCertificate> getErrorCert();  	std::string statusMessage();  		// return a message string, suitable for showing the user  	std::string statusURI(); diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index eaf95aa54c..72b571991a 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -61,7 +61,7 @@  	value="0.26 0.345 0.263 1" />  	<color  	 name="Red" -	 value="0.729 0 0.121 1" /> +	 value="1 0 0 1" />  	<color  	 name="Blue"  	 value="0 0 1 1" /> @@ -197,7 +197,7 @@       value="0.5 0.5 0.5 1" />      <color       name="ColorPaletteEntry03" -     value="0.3344 0.5456 0.5159 1" /> +     value="0.5 0 0 1" />      <color       name="ColorPaletteEntry04"       value="0.5 0.5 0 1" /> @@ -239,7 +239,7 @@       reference="LtYellow" />      <color       name="ColorPaletteEntry17" -     reference="LtGreen" /> +     reference="White" />      <color       name="ColorPaletteEntry18"       reference="LtGray" /> @@ -248,7 +248,7 @@       reference="Red" />      <color       name="ColorPaletteEntry20" -     reference=".5 .5 1 0" /> +     reference="Yellow" />      <color       name="ColorPaletteEntry21"       reference="Green" /> diff --git a/indra/newview/skins/default/textures/icons/Generic_Group_Large.png b/indra/newview/skins/default/textures/icons/Generic_Group_Large.png Binary files differdeleted file mode 100644 index 4d4f1e1bee..0000000000 --- a/indra/newview/skins/default/textures/icons/Generic_Group_Large.png +++ /dev/null diff --git a/indra/newview/skins/default/textures/icons/Progress_1.png b/indra/newview/skins/default/textures/icons/Progress_1.png Binary files differdeleted file mode 100644 index 58b56003c4..0000000000 --- a/indra/newview/skins/default/textures/icons/Progress_1.png +++ /dev/null diff --git a/indra/newview/skins/default/textures/icons/Progress_10.png b/indra/newview/skins/default/textures/icons/Progress_10.png Binary files differdeleted file mode 100644 index 07fe0be8a3..0000000000 --- a/indra/newview/skins/default/textures/icons/Progress_10.png +++ /dev/null diff --git a/indra/newview/skins/default/textures/icons/Progress_11.png b/indra/newview/skins/default/textures/icons/Progress_11.png Binary files differdeleted file mode 100644 index 215d68cc46..0000000000 --- a/indra/newview/skins/default/textures/icons/Progress_11.png +++ /dev/null diff --git a/indra/newview/skins/default/textures/icons/Progress_12.png b/indra/newview/skins/default/textures/icons/Progress_12.png Binary files differdeleted file mode 100644 index d755588621..0000000000 --- a/indra/newview/skins/default/textures/icons/Progress_12.png +++ /dev/null diff --git a/indra/newview/skins/default/textures/icons/Progress_2.png b/indra/newview/skins/default/textures/icons/Progress_2.png Binary files differdeleted file mode 100644 index 6640ee227b..0000000000 --- a/indra/newview/skins/default/textures/icons/Progress_2.png +++ /dev/null diff --git a/indra/newview/skins/default/textures/icons/Progress_3.png b/indra/newview/skins/default/textures/icons/Progress_3.png Binary files differdeleted file mode 100644 index 5decbe977e..0000000000 --- a/indra/newview/skins/default/textures/icons/Progress_3.png +++ /dev/null diff --git a/indra/newview/skins/default/textures/icons/Progress_4.png b/indra/newview/skins/default/textures/icons/Progress_4.png Binary files differdeleted file mode 100644 index 56e81c17aa..0000000000 --- a/indra/newview/skins/default/textures/icons/Progress_4.png +++ /dev/null diff --git a/indra/newview/skins/default/textures/icons/Progress_5.png b/indra/newview/skins/default/textures/icons/Progress_5.png Binary files differdeleted file mode 100644 index a89bf2ac62..0000000000 --- a/indra/newview/skins/default/textures/icons/Progress_5.png +++ /dev/null diff --git a/indra/newview/skins/default/textures/icons/Progress_6.png b/indra/newview/skins/default/textures/icons/Progress_6.png Binary files differdeleted file mode 100644 index 233c479540..0000000000 --- a/indra/newview/skins/default/textures/icons/Progress_6.png +++ /dev/null diff --git a/indra/newview/skins/default/textures/icons/Progress_7.png b/indra/newview/skins/default/textures/icons/Progress_7.png Binary files differdeleted file mode 100644 index 631d7a6819..0000000000 --- a/indra/newview/skins/default/textures/icons/Progress_7.png +++ /dev/null diff --git a/indra/newview/skins/default/textures/icons/Progress_8.png b/indra/newview/skins/default/textures/icons/Progress_8.png Binary files differdeleted file mode 100644 index ac0e3f13f7..0000000000 --- a/indra/newview/skins/default/textures/icons/Progress_8.png +++ /dev/null diff --git a/indra/newview/skins/default/textures/icons/Progress_9.png b/indra/newview/skins/default/textures/icons/Progress_9.png Binary files differdeleted file mode 100644 index 17fb4a0335..0000000000 --- a/indra/newview/skins/default/textures/icons/Progress_9.png +++ /dev/null diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index fed326c25e..41bcc62220 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -1,4 +1,4 @@ -<!-- +<!--  This file contains metadata about how to load, display, and scale textures for rendering in the UI.  Images do *NOT* have to appear in this file in order to use them as textures in the UI...simply refer  to them by filename (relative to textures directory). @@ -46,13 +46,9 @@ with the same filename but different name    <texture name="AddItem_Press" file_name="icons/AddItem_Press.png" preload="false" />    <texture name="Arrow_Left_Off" file_name="navbar/Arrow_Left_Off.png" preload="true" /> -  <texture name="Arrow_Left_Press" file_name="navbar/Arrow_Left_Press.png" preload="true" />    <texture name="Arrow_Right_Off" file_name="navbar/Arrow_Right_Off.png" preload="true" /> -  <texture name="Arrow_Right_Press" file_name="navbar/Arrow_Right_Press.png" preload="true" />  <!-- -  <texture name="Arrow_Left" file_name="widgets/Arrow_Left.png" preload="true" /> -  <texture name="Arrow_Right" file_name="widgets/Arrow_Right.png" preload="true" />  -->    <texture name="Arrow_Small_Up" file_name="widgets/Arrow_Small_Up.png" preload="true" /> @@ -64,49 +60,28 @@ with the same filename but different name    <texture name="AudioMute_Off" file_name="icons/AudioMute_Off.png" preload="false" />    <texture name="AudioMute_Over" file_name="icons/AudioMute_Over.png" preload="false" /> -  <texture name="AudioMute_Press" file_name="icons/AudioMute_Press.png" preload="false" />    <texture name="Audio_Off" file_name="icons/Audio_Off.png" preload="false" /> -  <texture name="Audio_Over" file_name="icons/Audio_Over.png" preload="false" />    <texture name="Audio_Press" file_name="icons/Audio_Press.png" preload="false" />    <texture name="Avaline_Icon" file_name="icons/avaline_default_icon.jpg" preload="true" /> -  <texture name="BackArrow_Disabled" file_name="icons/BackArrow_Disabled.png" preload="false" />    <texture name="BackArrow_Off" file_name="icons/BackArrow_Off.png" preload="false" /> -  <texture name="BackArrow_Press" file_name="icons/BackArrow_Press.png" preload="false" />    <texture name="Blank" file_name="Blank.png" preload="false" /> -  <texture name="BottomTray_BG" file_name="bottomtray/BottomTray_BG.png" preload="false" /> -  <texture name="BottomTray_Scroll_Right" file_name="navbar/Arrow_Right_Off.png" preload="false" /> -  <texture name="BottomTray_Scroll_Left" file_name="navbar/Arrow_Left_Off.png" preload="false" /> -  <texture name="BuyArrow_Off" file_name="navbar/BuyArrow_Off.png" preload="true" scale.left="1" scale.top="1" scale.right="0" scale.bottom="0"  />    <texture name="BuyArrow_Over" file_name="navbar/BuyArrow_Over.png" preload="true" scale.left="0" scale.top="1" scale.right="0" scale.bottom="0"  />    <texture name="BuyArrow_Press" file_name="navbar/BuyArrow_Press.png" preload="true" scale.left="1" scale.top="1" scale.right="0" scale.bottom="0"  /> -  <texture name="Cam_Avatar_Disabled" file_name="bottomtray/Cam_Avatar_Disabled.png" preload="false" /> -  <texture name="Cam_Avatar_Over" file_name="bottomtray/Cam_Avatar_Over.png" preload="false" />    <texture name="Cam_Avatar_Off" file_name="bottomtray/Cam_Avatar_Off.png" preload="false" /> -  <texture name="Cam_Avatar_Press" file_name="bottomtray/Cam_Avatar_Press.png" preload="false" /> -  <texture name="Cam_FreeCam_Disabled" file_name="bottomtray/Cam_FreeCam_Disabled.png" preload="false" />    <texture name="Cam_FreeCam_Off" file_name="bottomtray/Cam_FreeCam_Off.png" preload="false" /> -  <texture name="Cam_FreeCam_Over" file_name="bottomtray/Cam_FreeCam_Over.png" preload="false" /> -  <texture name="Cam_FreeCam_Press" file_name="bottomtray/Cam_FreeCam_Press.png" preload="false" /> -  <texture name="Cam_Orbit_Disabled" file_name="bottomtray/Cam_Orbit_Disabled.png" preload="false" />    <texture name="Cam_Orbit_Off" file_name="bottomtray/Cam_Orbit_Off.png" preload="false" /> -  <texture name="Cam_Orbit_Over" file_name="bottomtray/Cam_Orbit_Over.png" preload="false" /> -  <texture name="Cam_Orbit_Press" file_name="bottomtray/Cam_Orbit_Press.png" preload="false" /> -  <texture name="Cam_Pan_Disabled" file_name="bottomtray/Cam_Pan_Disabled.png" preload="false" />    <texture name="Cam_Pan_Off" file_name="bottomtray/Cam_Pan_Off.png" preload="false" /> -  <texture name="Cam_Pan_Over" file_name="bottomtray/CCam_Pan_Over.png" preload="false" /> -  <texture name="Cam_Pan_Press" file_name="bottomtray/Cam_Pan_Press.png" preload="false" />    <texture name="Cam_Preset_Back_Off" file_name="bottomtray/Cam_Preset_Back_Off.png" preload="false" />    <texture name="Cam_Preset_Back_On" file_name="bottomtray/Cam_Preset_Back_On.png" preload="false" />    <texture name="Cam_Preset_Eye_Off" file_name="bottomtray/Cam_Preset_Eye_Off.png" preload="false" /> -  <texture name="Cam_Preset_Eye_On" file_name="bottomtray/Cam_Preset_Eye_On.png" preload="false" />    <texture name="Cam_Preset_Front_Off" file_name="bottomtray/Cam_Preset_Front_Off.png" preload="false" />    <texture name="Cam_Preset_Front_On" file_name="bottomtray/Cam_Preset_Front_On.png" preload="false" />    <texture name="Cam_Preset_Side_Off" file_name="bottomtray/Cam_Preset_Side_Off.png" preload="false" /> @@ -116,8 +91,6 @@ with the same filename but different name    <texture name="Cam_Rotate_Out" file_name="bottomtray/Cam_Rotate_Out.png" preload="false" />    <texture name="Cam_Tracking_In" file_name="bottomtray/Cam_Tracking_In.png" preload="false" />    <texture name="Cam_Tracking_Out" file_name="bottomtray/Cam_Tracking_Out.png" preload="false" /> -  <texture name="CameraView_Off" file_name="bottomtray/CameraView_Off.png" preload="false" /> -  <texture name="CameraView_Over" file_name="bottomtray/CameraView_Over.png" preload="false" />    <texture name="Checkbox_Off_Disabled" file_name="widgets/Checkbox_Disabled.png" preload="true" />    <texture name="Checkbox_On_Disabled" file_name="widgets/Checkbox_On_Disabled.png" preload="true" /> @@ -127,8 +100,6 @@ with the same filename but different name    <texture name="Checkbox_Press" file_name="widgets/Checkbox_Press.png" preload="true" />    <texture name="ComboButton_Disabled" file_name="widgets/ComboButton_Disabled.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" /> -  <texture name="ComboButton_Over" file_name="widgets/ComboButton_Over.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" /> -  <texture name="ComboButton_Press" file_name="widgets/ComboButton_Press.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" />    <texture name="ComboButton_Selected" file_name="widgets/ComboButton_Selected.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" />    <texture name="ComboButton_UpSelected" file_name="widgets/ComboButton_UpSelected.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" />    <texture name="ComboButton_Up_On_Selected" file_name="widgets/ComboButton_Up_On_Selected.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" /> @@ -136,25 +107,18 @@ with the same filename but different name    <texture name="ComboButton_UpOff" file_name="widgets/ComboButton_UpOff.png" preload="true" scale.left="2" scale.top="19" scale.right="18" scale.bottom="2" />    <texture name="Container" file_name="containers/Container.png" preload="false" /> -  <texture name="DisclosureArrow_Closed_Off" file_name="widgets/DisclosureArrow_Closed_Off.png" preload="true" /> -  <texture name="DisclosureArrow_Closed_Press" file_name="widgets/DisclosureArrow_Closed_Press.png" preload="true" />    <texture name="DisclosureArrow_Opened_Off" file_name="widgets/DisclosureArrow_Opened_Off.png" preload="true" /> -  <texture name="DisclosureArrow_Opened_Press" file_name="widgets/DisclosureArrow_Opened_Press.png" preload="true" />    <texture name="DownArrow" file_name="bottomtray/DownArrow.png" preload="false" />    <texture name="DropDown_Disabled" file_name="widgets/DropDown_Disabled.png" preload="true" scale.left="4" scale.top="19" scale.right="99" scale.bottom="4" /> -  <texture name="DropDown_Over" file_name="widgets/DropDown_Over.png" preload="true" scale.left="4" scale.top="19" scale.right="99" scale.bottom="4" />    <texture name="DropDown_Press" file_name="widgets/DropDown_Press.png" preload="true" scale.left="4" scale.top="19" scale.right="99" scale.bottom="4" /> -  <texture name="DropDown_Selected" file_name="widgets/DropDown_Selected.png" preload="true" scale.left="4" scale.top="19" scale.right="99" scale.bottom="4" />    <texture name="DropDown_On" file_name="widgets/DropDown_On.png" preload="true" scale.left="4" scale.top="19" scale.right="99" scale.bottom="4" />    <texture name="DropDown_Off" file_name="widgets/DropDown_Off.png" preload="true" scale.left="4" scale.top="19" scale.right="99" scale.bottom="4" />    <texture name="DropTarget" file_name="widgets/DropTarget.png" preload="false" />    <texture name="ExternalBrowser_Off" file_name="icons/ExternalBrowser_Off.png" preload="false" /> -  <texture name="ExternalBrowser_Over" file_name="icons/ExternalBrowser_Over.png" preload="false" /> -  <texture name="ExternalBrowser_Press" file_name="icons/ExternalBrowser_Press.png" preload="false" />    <texture name="Favorite_Star_Active" file_name="navbar/Favorite_Star_Active.png" preload="false" />    <texture name="Favorite_Star_Off" file_name="navbar/Favorite_Star_Off.png" preload="false" /> @@ -162,8 +126,6 @@ with the same filename but different name    <texture name="Favorite_Star_Over" file_name="navbar/Favorite_Star_Over.png" preload="false" />    <texture name="Favorite_Link_Over" file_name="navbar/Favorite_Link_Over.png" preload="false" /> -  <texture name="FileMenu_BarSelect" file_name="navbar/FileMenu_BarSelect.png" preload="false" scale.left="2" scale.top="0" scale.right="2" scale.bottom="0" /> -  <texture name="FileMenu_BG" file_name="navbar/FileMenu_BG.png" preload="false" />    <texture name="Flag" file_name="navbar/Flag.png" preload="false" /> @@ -174,22 +136,16 @@ with the same filename but different name    <texture name="Generic_Group" file_name="icons/Generic_Group.png" preload="false" />    <texture name="icon_group.tga" file_name="icons/Generic_Group.png" preload="false" /> -  <texture name="Generic_Group_Large" file_name="icons/Generic_Group_Large.png" preload="false" /> -  <texture name="Generic_Object_Medium" file_name="icons/Generic_Object_Medium.png" preload="false" />    <texture name="Generic_Object_Small" file_name="icons/Generic_Object_Small.png" preload="false" /> -  <texture name="Generic_Object_Large" file_name="icons/Generic_Object_Large.png" preload="false" />    <texture name="Generic_Person" file_name="icons/Generic_Person.png" preload="false" />    <texture name="Generic_Person_Large" file_name="icons/Generic_Person_Large.png" preload="false" />    <texture name="Health" file_name="icons/Health.png" preload="false" /> -  <texture name="Help_Off" file_name="navbar/Help_Off.png" preload="false" />    <texture name="Help_Press" file_name="navbar/Help_Press.png" preload="false" /> -  <texture name="History_Arrow" file_name="navbar/History_Arrow.png" preload="true" />    <texture name="Home_Off" file_name="navbar/Home_Off.png" preload="false" /> -  <texture name="Home_Press" file_name="navbar/Home_Press.png" preload="false" />    <texture name="Icon_Close_Foreground" file_name="windows/Icon_Close_Foreground.png" preload="true" />    <texture name="Icon_Close_Press" file_name="windows/Icon_Close_Press.png" preload="true" /> @@ -206,7 +162,6 @@ with the same filename but different name    <texture name="Icon_Help_Foreground" file_name="windows/Icon_Help_Foreground.png" preload="true" />    <texture name="Icon_Help_Press" file_name="windows/Icon_Help_Press.png" preload="true" /> -  <texture name="Icon_Info" file_name="windows/Icon_Info.png" preload="false" />    <texture name="Icon_Minimize_Foreground" file_name="windows/Icon_Minimize_Foreground.png" preload="true" />    <texture name="Icon_Minimize_Press" file_name="windows/Icon_Minimize_Press.png" preload="true" /> @@ -225,13 +180,11 @@ with the same filename but different name    <texture name="Inspector_Hover" file_name="windows/Inspector_Hover.png" preload="false" />    <texture name="Inspector_I" file_name="windows/Inspector_I.png" preload="false" /> -  <texture name="Inv_Acessories" file_name="icons/Inv_Accessories.png" preload="false" />    <texture name="Inv_Alpha" file_name="icons/Inv_Alpha.png" preload="false" />    <texture name="Inv_Animation" file_name="icons/Inv_Animation.png" preload="false" />    <texture name="Inv_BodyShape" file_name="icons/Inv_BodyShape.png" preload="false" />    <texture name="Inv_CallingCard" file_name="icons/Inv_CallingCard.png" preload="false" />    <texture name="Inv_Clothing" file_name="icons/Inv_Clothing.png" preload="false" /> -  <texture name="Inv_DangerousScript" file_name="icons/Inv_DangerousScript.png" preload="false" />    <texture name="Inv_Eye" file_name="icons/Inv_Eye.png" preload="false" />    <texture name="Inv_FolderClosed" file_name="icons/Inv_FolderClosed.png" preload="false" />    <texture name="Inv_FolderOpen" file_name="icons/Inv_FolderOpen.png" preload="false" /> @@ -239,7 +192,6 @@ with the same filename but different name    <texture name="Inv_Gloves" file_name="icons/Inv_Gloves.png" preload="false" />    <texture name="Inv_Hair" file_name="icons/Inv_Hair.png" preload="false" />    <texture name="Inv_LinkItem" file_name="icons/Inv_LinkItem.png" preload="false" /> -  <texture name="Inv_LinkItem_Broken" file_name="icons/Inv_LinkItem_Broken.png" preload="false" />    <texture name="Inv_LinkFolder" file_name="icons/Inv_LinkFolder.png" preload="false" />    <texture name="Inv_Jacket" file_name="icons/Inv_Jacket.png" preload="false" />    <texture name="Inv_LookFolderOpen" file_name="icons/Inv_LookFolderOpen.png" preload="false" /> @@ -259,7 +211,6 @@ with the same filename but different name    <texture name="Inv_Sound" file_name="icons/Inv_Sound.png" preload="false" />    <texture name="Inv_Tattoo" file_name="icons/Inv_Tattoo.png" preload="false" />    <texture name="Inv_Texture" file_name="icons/Inv_Texture.png" preload="false" /> -  <texture name="Inv_Trash" file_name="icons/Inv_Trash.png" preload="false" />    <texture name="Inv_Underpants" file_name="icons/Inv_Underpants.png" preload="false" />    <texture name="Inv_Undershirt" file_name="icons/Inv_Undershirt.png" preload="false" /> @@ -272,9 +223,7 @@ with the same filename but different name    <texture name="Lock" file_name="icons/Lock.png" preload="false" />    <texture name="Lock2" file_name="navbar/Lock.png" preload="false" /> -  <texture name="Login_Pod" file_name="windows/Login_Pod.png" preload="true" /> -  <texture name="Microphone_Mute" file_name="icons/Microphone_Mute.png" preload="false" />    <texture name="Microphone_On" file_name="icons/Microphone_On.png" preload="false" />    <texture name="MinusItem_Disabled" file_name="icons/MinusItem_Disabled.png" preload="false" /> @@ -283,18 +232,9 @@ with the same filename but different name    <texture name="menu_separator" file_name="navbar/FileMenu_Divider.png" scale.left="4" scale.top="166" scale.right="0" scale.bottom="0" /> -  <texture name="Move_Fly_Disabled" file_name="bottomtray/Move_Fly_Disabled.png" preload="false" />    <texture name="Move_Fly_Off" file_name="bottomtray/Move_Fly_Off.png" preload="false" /> -  <texture name="Move_Fly_Over" file_name="bottomtray/Move_Fly_Over.png" preload="false" /> -  <texture name="Move_Fly_Press" file_name="bottomtray/Move_Fly_Press.png" preload="false" /> -  <texture name="Move_Run_Disabled" file_name="bottomtray/Move_Run_Disabled.png" preload="false" />    <texture name="Move_Run_Off" file_name="bottomtray/Move_Run_Off.png" preload="false" /> -  <texture name="Move_Run_Over" file_name="bottomtray/Move_Run_Over.png" preload="false" /> -  <texture name="Move_Run_Press" file_name="bottomtray/Move_Run_Press.png" preload="false" /> -  <texture name="Move_Walk_Disabled" file_name="bottomtray/Move_Walk_Disabled.png" preload="false" />    <texture name="Move_Walk_Off" file_name="bottomtray/Move_Walk_Off.png" preload="false" /> -  <texture name="Move_Walk_Over" file_name="bottomtray/Move_Walk_Over.png" preload="false" /> -  <texture name="Move_Walk_Press" file_name="bottomtray/Move_Walk_Press.png" preload="false" />    <texture name="Movement_Backward_Off" file_name="bottomtray/Movement_Backward_Off.png" preload="false" />    <texture name="Movement_Backward_On" file_name="bottomtray/Movement_Backward_On.png" preload="false" />    <texture name="Movement_Down_Off" file_name="bottomtray/Movement_Down_Off.png" preload="false" /> @@ -311,10 +251,6 @@ with the same filename but different name    <texture name="NavBar_BG_NoFav" file_name="navbar/NavBar_BG_NoFav.png" preload="true" scale.left="1" scale.top="1" scale.right="0" scale.bottom="0" />    <texture name="NavBar_BG" file_name="navbar/NavBar_BG.png" preload="true" scale.left="1" scale.top="1" scale.right="0" scale.bottom="0" /> -  <texture name="NearbyVoice_Lvl1" file_name="bottomtray/NearbyVoice_Lvl1.png" preload="false" /> -  <texture name="NearbyVoice_Lvl2" file_name="bottomtray/NearbyVoice_Lvl2.png" preload="false" /> -  <texture name="NearbyVoice_Lvl3" file_name="bottomtray/NearbyVoice_Lvl3.png" preload="false" /> -  <texture name="NearbyVoice_On" file_name="bottomtray/NearbyVoice_On.png" preload="false" />    <texture name="Notices_Unread" file_name="bottomtray/Notices_Unread.png" preload="true" /> @@ -356,27 +292,15 @@ with the same filename but different name    <texture name="OptionsMenu_Off" file_name="icons/OptionsMenu_Off.png" preload="false" />    <texture name="OptionsMenu_Press" file_name="icons/OptionsMenu_Press.png" preload="false" /> -  <texture name="Overhead_Arrow_L" file_name="world/Overhead_Arrow_L.png" preload="false" /> -  <texture name="Overhead_Arrow_M" file_name="world/Overhead_Arrow_M.png" preload="false" /> -  <texture name="Overhead_Arrow_S" file_name="world/Overhead_Arrow_S.png" preload="false" /> -  <texture name="Overhead_L" file_name="world/Overhead_L.png" preload="false" /> -  <texture name="Overhead_M" file_name="world/Overhead_M.png" preload="false" /> -  <texture name="Overhead_S" file_name="world/Overhead_S.png" preload="false" /> -  <texture name="Parcel_Evry_Color" file_name="icons/Parcel_Evry_Color.png" preload="false" />    <texture name="Parcel_Exp_Color" file_name="icons/Parcel_Exp_Color.png" preload="false" /> -  <texture name="Parcel_M_Color" file_name="icons/Parcel_M_Color.png" preload="false" />   <texture name="Parcel_Build_Dark" file_name="icons/Parcel_Build_Dark.png" preload="false" />   <texture name="Parcel_BuildNo_Dark" file_name="icons/Parcel_BuildNo_Dark.png" preload="false" />   <texture name="Parcel_Damage_Dark" file_name="icons/Parcel_Damage_Dark.png" preload="false" />   <texture name="Parcel_DamageNo_Dark" file_name="icons/Parcel_DamageNo_Dark.png" preload="false" /> - <texture name="Parcel_Evry_Dark" file_name="icons/Parcel_Evry_Dark.png" preload="false" /> - <texture name="Parcel_Exp_Dark" file_name="icons/Parcel_Exp_Dark.png" preload="false" />   <texture name="Parcel_Fly_Dark" file_name="icons/Parcel_Fly_Dark.png" preload="false" />   <texture name="Parcel_FlyNo_Dark" file_name="icons/Parcel_FlyNo_Dark.png" preload="false" /> - <texture name="Parcel_ForSale_Dark" file_name="icons/Parcel_ForSale_Dark.png" preload="false" /> - <texture name="Parcel_ForSaleNo_Dark" file_name="icons/Parcel_ForSaleNo_Dark.png" preload="false" />   <texture name="Parcel_Health_Dark" file_name="icons/Parcel_Health_Dark.png" preload="false" />   <texture name="Parcel_M_Dark" file_name="icons/Parcel_M_Dark.png" preload="false" />   <texture name="Parcel_PG_Dark" file_name="icons/Parcel_PG_Dark.png" preload="false" /> @@ -388,22 +312,13 @@ with the same filename but different name   <texture name="Parcel_Voice_Dark" file_name="icons/Parcel_Voice_Dark.png" preload="false" />   <texture name="Parcel_VoiceNo_Dark" file_name="icons/Parcel_VoiceNo_Dark.png" preload="false" /> - <texture name="Parcel_Build_Light" file_name="icons/Parcel_Build_Light.png" preload="false" />   <texture name="Parcel_BuildNo_Light" file_name="icons/Parcel_BuildNo_Light.png" preload="false" /> - <texture name="Parcel_Damage_Light" file_name="icons/Parcel_Damage_Light.png" preload="false" /> - <texture name="Parcel_DamageNo_Light" file_name="icons/Parcel_DamageNo_Light.png" preload="false" /> - <texture name="Parcel_Evry_Light" file_name="icons/Parcel_Evry_Light.png" preload="false" /> - <texture name="Parcel_Exp_Light" file_name="icons/Parcel_Exp_Light.png" preload="false" /> - <texture name="Parcel_Fly_Light" file_name="icons/Parcel_Fly_Light.png" preload="false" />   <texture name="Parcel_FlyNo_Light" file_name="icons/Parcel_FlyNo_Light.png" preload="false" />   <texture name="Parcel_ForSale_Light" file_name="icons/Parcel_ForSale_Light.png" preload="false" /> - <texture name="Parcel_ForSaleNo_Light" file_name="icons/Parcel_ForSaleNo_Light.png" preload="false" />   <texture name="Parcel_M_Light" file_name="icons/Parcel_M_Light.png" preload="false" />   <texture name="Parcel_PG_Light" file_name="icons/Parcel_PG_Light.png" preload="false" /> - <texture name="Parcel_Push_Light" file_name="icons/Parcel_Push_Light.png" preload="false" />   <texture name="Parcel_PushNo_Light" file_name="icons/Parcel_PushNo_Light.png" preload="false" />   <texture name="Parcel_R_Light" file_name="icons/Parcel_R_Light.png" preload="false" /> - <texture name="Parcel_Scripts_Light" file_name="icons/Parcel_Scripts_Light.png" preload="false" />   <texture name="Parcel_ScriptsNo_Light" file_name="icons/Parcel_ScriptsNo_Dark.png" preload="false" />   <texture name="Parcel_Voice_Light" file_name="icons/Parcel_Voice_Light.png" preload="false" />   <texture name="Parcel_VoiceNo_Light" file_name="icons/Parcel_VoiceNo_Light.png" preload="false" /> @@ -415,18 +330,6 @@ with the same filename but different name    <texture name="Play_Over" file_name="icons/Play_Over.png" preload="false" />    <texture name="Play_Press" file_name="icons/Play_Press.png" preload="false" /> -  <texture name="Progress_1" file_name="icons/Progress_1.png" preload="false" /> -  <texture name="Progress_2" file_name="icons/Progress_2.png" preload="false" /> -  <texture name="Progress_3" file_name="icons/Progress_3.png" preload="false" /> -  <texture name="Progress_4" file_name="icons/Progress_4.png" preload="false" /> -  <texture name="Progress_5" file_name="icons/Progress_5.png" preload="false" /> -  <texture name="Progress_6" file_name="icons/Progress_6.png" preload="false" /> -  <texture name="Progress_7" file_name="icons/Progress_7.png" preload="false" /> -  <texture name="Progress_8" file_name="icons/Progress_8.png" preload="false" /> -  <texture name="Progress_9" file_name="icons/Progress_9.png" preload="false" /> -  <texture name="Progress_10" file_name="icons/Progress_10.png" preload="false" /> -  <texture name="Progress_11" file_name="icons/Progress_11.png" preload="false" /> -  <texture name="Progress_12" file_name="icons/Progress_12.png" preload="false" />    <texture name="ProgressBar" file_name="widgets/ProgressBar.png" preload="true" scale.left="4" scale.top="10" scale.right="48" scale.bottom="2" />    <texture name="ProgressTrack" file_name="widgets/ProgressTrack.png" preload="true" scale.left="4" scale.top="13" scale.right="148" scale.bottom="2" /> @@ -435,7 +338,6 @@ with the same filename but different name    <texture name="PushButton_Off" file_name="widgets/PushButton_Off.png" preload="true" scale.left="4" scale.top="19" scale.right="28" scale.bottom="4" />    <texture name="PushButton_On" file_name="widgets/PushButton_On.png" preload="true" scale.left="4" scale.top="19" scale.right="28" scale.bottom="4" />    <texture name="PushButton_On_Selected" file_name="widgets/PushButton_On_Selected.png" preload="true" scale.left="4" scale.top="19" scale.right="28" scale.bottom="4" /> -  <texture name="PushButton_On_Disabled" file_name="widgets/PushButton_On_Disabled.png" preload="true" scale.left="4" scale.top="19" scale.right="28" scale.bottom="4" />    <texture name="PushButton_Press" file_name="widgets/PushButton_Press.png" preload="true" scale.left="4" scale.top="19" scale.right="28" scale.bottom="4" />    <texture name="PushButton_Selected" file_name="widgets/PushButton_Selected.png" preload="true" scale.left="4" scale.top="19" scale.right="28" scale.bottom="4" />    <texture name="PushButton_Selected_Press" file_name="widgets/PushButton_Selected_Press.png" preload="true" scale.left="4" scale.top="19" scale.right="28" scale.bottom="4" /> @@ -448,12 +350,8 @@ with the same filename but different name    <texture name="RadioButton_Disabled" file_name="widgets/RadioButton_Disabled.png" preload="true" />    <texture name="RadioButton_On_Disabled" file_name="widgets/RadioButton_On_Disabled.png" preload="true" /> -  <texture name="Rec_Off" file_name="icons/Rec_Off.png" preload="false" /> -  <texture name="Rec_On" file_name="icons/Rec_On.png" preload="false" /> -  <texture name="Refresh_Disabled" file_name="icons/Refresh_Disabled.png" preload="false" />    <texture name="Refresh_Off" file_name="icons/Refresh_Off.png" preload="false" /> -  <texture name="Refresh_Press" file_name="icons/Refresh_Press.png" preload="false" />    <texture name="Resize_Corner" file_name="windows/Resize_Corner.png" preload="true" /> @@ -470,11 +368,6 @@ with the same filename but different name    <texture name="ScrollTrack_Vert" file_name="widgets/ScrollTrack_Vert.png" preload="true" scale.left="2" scale.top="40" scale.bottom="13" scale.right="0" />    <texture name="ScrollTrack_Horiz" file_name="widgets/ScrollTrack_Horiz.png" preload="true" scale.left="4" scale.top="0" scale.bottom="0" scale.right="2" /> -  <texture name="ScrubberThumb_Disabled" file_name="widgets/ScrubberThumb_Disabled.png" preload="false" /> -  <texture name="ScrubberThumb_Focus" file_name="widgets/ScrubberThumb_Focus.png" preload="false" /> -  <texture name="ScrubberThumb_Off" file_name="widgets/ScrubberThumb_Off.png" preload="false" /> -  <texture name="ScrubberThumb_Over" file_name="widgets/ScrubberThumb_Over.png" preload="false" /> -  <texture name="ScrubberThumb_Press" file_name="widgets/ScrubberThumb_Press.png" preload="false" />    <texture name="Search" file_name="navbar/Search.png" preload="false" /> @@ -487,8 +380,6 @@ with the same filename but different name    <texture name="SegmentedBtn_Left_Selected_Press" file_name="widgets/SegmentedBtn_Left_Selected_Press.png" preload="true" scale.left="4" scale.top="19" scale.right="22" scale.bottom="4" />    <texture name="SegmentedBtn_Left_Selected_Disabled" file_name="widgets/SegmentedBtn_Left_Selected_Disabled.png" preload="true" scale.left="4" scale.top="19" scale.right="22" scale.bottom="4" /> -  <texture name="SegmentedBtn_Middle_Off" file_name="widgets/SegmentedBtn_Middle_Off.png" preload="true" scale.left="4" scale.top="19" scale.right="22" scale.bottom="4" /> -  <texture name="SegmentedBtn_Middle_Press" file_name="widgets/SegmentedBtn_Middle_Press.png" preload="true" scale.left="4" scale.top="19" scale.right="22" scale.bottom="4" />    <texture name="SegmentedBtn_Middle_Disabled" file_name="widgets/SegmentedBtn_Middle_Disabled.png" preload="true" scale.left="4" scale.top="19" scale.right="22" scale.bottom="4" />    <texture name="SegmentedBtn_Middle_Selected" file_name="widgets/SegmentedBtn_Middle_Selected.png" preload="true" scale.left="4" scale.top="19" scale.right="22" scale.bottom="4" />    <texture name="SegmentedBtn_Middle_Selected_Press" file_name="widgets/SegmentedBtn_Middle_Selected_Press.png" preload="true" scale.left="4" scale.top="19" scale.right="22" scale.bottom="4" /> @@ -503,11 +394,7 @@ with the same filename but different name    <texture name="SegmentedBtn_Right_Selected_Disabled" file_name="widgets/SegmentedBtn_Right_Selected_Disabled.png" preload="true" scale.left="4" scale.top="19" scale.right="22" scale.bottom="4" />    <texture name="SkipBackward_Off" file_name="icons/SkipBackward_Off.png" preload="false" /> -  <texture name="SkipBackward_Over" file_name="icons/SkipBackward_Over.png" preload="false" /> -  <texture name="SkipBackward_Press" file_name="icons/SkipBackward_Press.png" preload="false" />    <texture name="SkipForward_Off" file_name="icons/SkipForward_Off.png" preload="false" /> -  <texture name="SkipForward_Over" file_name="icons/SkipForward_Over.png" preload="false" /> -  <texture name="SkipForward_Press" file_name="icons/SkipForward_Press.png" preload="false" />    <texture name="SliderTrack_Horiz" file_name="widgets/SliderTrack_Horiz.png" scale.left="4" scale.top="4" scale.right="100" scale.bottom="2" />    <texture name="SliderTrack_Vert" file_name="widgets/SliderTrack_Vert.png" scale.left="2" scale.top="100" scale.right="4" scale.bottom="4" /> @@ -520,63 +407,35 @@ with the same filename but different name    <texture name="Unknown_Icon" file_name="icons/unknown_icon.png" preload="true" />    <texture name="Snapshot_Off" file_name="bottomtray/Snapshot_Off.png" preload="true" scale.left="4" scale.top="19" scale.right="22" scale.bottom="4" /> -  <texture name="Snapshot_Over" file_name="bottomtray/Snapshot_Over.png" preload="false" /> -  <texture name="Snapshot_Press" file_name="bottomtray/Snapshot_Press.png" preload="false" />    <texture name="startup_logo"  file_name="windows/startup_logo.png" preload="true" /> -  <texture name="Stepper_Down_Disabled" file_name="widgets/Stepper_Down_Disabled.png" preload="false" />    <texture name="Stepper_Down_Off" file_name="widgets/Stepper_Down_Off.png" preload="false" />    <texture name="Stepper_Down_Press" file_name="widgets/Stepper_Down_Press.png" preload="false" /> -  <texture name="Stepper_Up_Disabled" file_name="widgets/Stepper_Up_Disabled.png" preload="false" />    <texture name="Stepper_Up_Off" file_name="widgets/Stepper_Up_Off.png" preload="false" />    <texture name="Stepper_Up_Press" file_name="widgets/Stepper_Up_Press.png" preload="false" />    <texture name="Stop_Off" file_name="icons/Stop_Off.png" preload="false" /> -  <texture name="Stop_Over" file_name="icons/Stop_Over.png" preload="false" /> -  <texture name="Stop_Press" file_name="icons/Stop_Press.png" preload="false" />    <texture name="StopReload_Off" file_name="icons/StopReload_Off.png" preload="false" />    <texture name="StopReload_Over" file_name="icons/StopReload_Over.png" preload="false" /> -  <texture name="StopReload_Press" file_name="icons/StopReload_Press.png" preload="false" /> -  <texture name="TabIcon_Appearance_Large" file_name="taskpanel/TabIcon_Appearance_Large.png" preload="false" />    <texture name="TabIcon_Appearance_Off" file_name="taskpanel/TabIcon_Appearance_Off.png" preload="false" /> -  <texture name="TabIcon_Appearance_Over" file_name="taskpanel/TabIcon_Appearance_Over.png" preload="false" />    <texture name="TabIcon_Appearance_Selected" file_name="taskpanel/TabIcon_Appearance_Selected.png" preload="false" />    <texture name="TabIcon_Close_Off" file_name="taskpanel/TabIcon_Close_Off.png" preload="false" /> -  <texture name="TabIcon_Close_Over" file_name="taskpanel/TabIcon_Close_Over.png" preload="false" /> -  <texture name="TabIcon_Inventory_Large" file_name="taskpanel/TabIcon_Inventory_Large.png" preload="false" /> -  <texture name="TabIcon_Inventory_Off" file_name="taskpanel/TabIcon_Inventory_Off.png" preload="false" /> -  <texture name="TabIcon_Inventory_Over" file_name="taskpanel/TabIcon_Inventory_Over.png" preload="false" /> -  <texture name="TabIcon_Inventory_Selected" file_name="taskpanel/TabIcon_Inventory_Selected.png" preload="false" /> -  <texture name="TabIcon_Home_Large" file_name="taskpanel/TabIcon_Home_Large.png" preload="false" />    <texture name="TabIcon_Home_Off" file_name="taskpanel/TabIcon_Home_Off.png" preload="false" /> -  <texture name="TabIcon_Home_Over" file_name="taskpanel/TabIcon_Home_Over.png" preload="false" />    <texture name="TabIcon_Home_Selected" file_name="taskpanel/TabIcon_Home_Selected.png" preload="false" /> -  <texture name="TabIcon_Me_Large" file_name="taskpanel/TabIcon_Me_Large.png" preload="false" />    <texture name="TabIcon_Me_Off" file_name="taskpanel/TabIcon_Me_Off.png" preload="false" /> -  <texture name="TabIcon_Me_Over" file_name="taskpanel/TabIcon_Me_Over.png" preload="false" />    <texture name="TabIcon_Me_Selected" file_name="taskpanel/TabIcon_Me_Selected.png" preload="false" />    <texture name="TabIcon_Open_Off" file_name="taskpanel/TabIcon_Open_Off.png" preload="false" /> -  <texture name="TabIcon_Open_Over" file_name="taskpanel/TabIcon_Open_Over.png" preload="false" /> -  <texture name="TabIcon_People_Large" file_name="taskpanel/TabIcon_People_Large.png" preload="false" />    <texture name="TabIcon_People_Off" file_name="taskpanel/TabIcon_People_Off.png" preload="false" /> -  <texture name="TabIcon_People_Over" file_name="taskpanel/TabIcon_People_Over.png" preload="false" />    <texture name="TabIcon_People_Selected" file_name="taskpanel/TabIcon_People_Selected.png" preload="false" />    <texture name="TabIcon_Places_Large" file_name="taskpanel/TabIcon_Places_Large.png" preload="false" />    <texture name="TabIcon_Places_Off" file_name="taskpanel/TabIcon_Places_Off.png" preload="false" /> -  <texture name="TabIcon_Places_Over" file_name="taskpanel/TabIcon_Places_Over.png" preload="false" />    <texture name="TabIcon_Places_Selected" file_name="taskpanel/TabIcon_Places_Selected.png" preload="false" /> -  <texture name="TabIcon_Things_Large" file_name="taskpanel/TabIcon_Things_Large.png" preload="false" />    <texture name="TabIcon_Things_Off" file_name="taskpanel/TabIcon_Things_Off.png" preload="false" /> -  <texture name="TabIcon_Things_Over" file_name="taskpanel/TabIcon_Things_Over.png" preload="false" />    <texture name="TabIcon_Things_Selected" file_name="taskpanel/TabIcon_Things_Selected.png" preload="false" /> -  <texture name="TabTop_Divider" file_name="containers/TabTop_Divider.png" preload="false" /> -  <texture name="TabTop_Left_Press" file_name="containers/TabTop_Left_Press.png" preload="false" /> -  <texture name="TabTop_Middle_Press" file_name="containers/TabTop_Middle_Press.png" preload="false" />    <texture name="TabTop_Right_Off" file_name="containers/TabTop_Right_Off.png" preload="false"  scale.left="8" scale.top="8" scale.right="62" scale.bottom="9" /> -  <texture name="TabTop_Right_Press" file_name="containers/TabTop_Right_Press.png" preload="false" />    <texture name="TabTop_Right_Selected" file_name="containers/TabTop_Right_Selected.png" preload="false"  scale.left="8" scale.top="8" scale.right="62" scale.bottom="9" />    <texture name="TabTop_Middle_Off" file_name="containers/TabTop_Middle_Off.png" preload="false" scale.left="8" scale.top="8" scale.right="120" scale.bottom="9" />    <texture name="TabTop_Middle_Selected" file_name="containers/TabTop_Middle_Selected.png" preload="false" scale.left="8" scale.top="8" scale.right="96" scale.bottom="9" /> @@ -585,8 +444,6 @@ with the same filename but different name    <texture name="TaskPanel_Tab_Off" file_name="taskpanel/TaskPanel_Tab_Off.png" preload="false" scale.left="4" scale.top="29" scale.right="36" scale.bottom="4" />    <texture name="TaskPanel_Tab_Selected" file_name="taskpanel/TaskPanel_Tab_Selected.png" preload="false" scale.left="5" scale.top="30" scale.right="36" scale.bottom="5" /> -  <texture name="TaskPanel_BG" file_name="taskpanel/TaskPanel_BG.png" preload="false" scale.left="4" scale.top="146" scale.right="146" scale.bottom="4" /> -  <texture name="TaskPanel_Tab_Unselected" file_name="taskpanel/TaskPanel_Tab_Over.png" preload="false" scale.left="5" scale.top="30" scale.right="36" scale.bottom="5" />    <texture name="TextField_Search_Disabled" file_name="widgets/TextField_Search_Disabled.png" preload="true" scale.left="9" scale.top="12" scale.right="248" scale.bottom="12" />    <texture name="TextField_Off" file_name="widgets/TextField_Off.png" preload="true" scale.left="9" scale.top="12" scale.right="248" scale.bottom="12" /> @@ -595,7 +452,6 @@ with the same filename but different name    <texture name="TextField_Disabled" file_name="widgets/TextField_Disabled.png" preload="true" scale.left="9" scale.top="12" scale.right="248" scale.bottom="12" />    <texture name="TextField_Active" file_name="widgets/TextField_Active.png" preload="true" scale.left="9" scale.top="12" scale.right="248" scale.bottom="12" /> -  <texture name="TimeBasedMediaBackground" file_name="windows/TimeBasedMediaBackground.png" preload="false" />    <texture name="Toast_CloseBtn" file_name="windows/Toast_CloseBtn.png" preload="true" />    <texture name="Toast_Background" file_name="windows/Toast_Background.png" preload="true" @@ -604,28 +460,19 @@ with the same filename but different name             scale.left="4" scale.top="28" scale.right="60" scale.bottom="4" />    <texture name="Tool_Create" file_name="build/Tool_Create.png" preload="false" /> -  <texture name="Tool_Create_Selected" file_name="build/Tool_Create_Selected.png" preload="false" />    <texture name="Tool_Dozer" file_name="build/Tool_Dozer.png" preload="false" /> -  <texture name="Tool_Dozer_Selected" file_name="build/Tool_Dozer_Selected.png" preload="false" />    <texture name="Tool_Face" file_name="build/Tool_Face.png" preload="false" /> -  <texture name="Tool_Face_Selected" file_name="build/Tool_Face_Selected.png" preload="false" />    <texture name="Tool_Grab" file_name="build/Tool_Grab.png" preload="false" /> -  <texture name="Tool_Grab_Selected" file_name="build/Tool_Grab_Selected.png" preload="false" />    <texture name="Tool_Zoom" file_name="build/Tool_Zoom.png" preload="false" /> -  <texture name="Tool_Zoom_Selected" file_name="build/Tool_Zoom_Selected.png" preload="false" /> -  <texture name="Toolbar_Divider" file_name="containers/Toolbar_Divider.png" preload="false" />    <texture name="Toolbar_Left_Off" file_name="containers/Toolbar_Left_Off.png" preload="false" scale.left="5" scale.bottom="4" scale.top="24" scale.right="30" />    <texture name="Toolbar_Left_Over" file_name="containers/Toolbar_Left_Over.png" preload="false" scale.left="5" scale.bottom="4" scale.top="24" scale.right="30" /> -  <texture name="Toolbar_Left_Press" file_name="containers/Toolbar_Left_Press.png" preload="false" scale.left="5" scale.bottom="4" scale.top="24" scale.right="30" />    <texture name="Toolbar_Left_Selected" file_name="containers/Toolbar_Left_Selected.png" preload="false" scale.left="5" scale.bottom="4" scale.top="24" scale.right="30" />    <texture name="Toolbar_Middle_Off" file_name="containers/Toolbar_Middle_Off.png" preload="false" scale.left="1" scale.bottom="2" scale.top="24" scale.right="30" />    <texture name="Toolbar_Middle_Over" file_name="containers/Toolbar_Middle_Over.png" preload="false" scale.left="1" scale.bottom="2" scale.top="24" scale.right="30" /> -  <texture name="Toolbar_Middle_Press" file_name="containers/Toolbar_Middle_Press.png" preload="false" scale.left="1" scale.bottom="2" scale.top="24" scale.right="30" />    <texture name="Toolbar_Middle_Selected" file_name="containers/Toolbar_Middle_Selected.png" preload="false" scale.left="1" scale.bottom="2" scale.top="24" scale.right="30" />    <texture name="Toolbar_Right_Off" file_name="containers/Toolbar_Right_Off.png" preload="false" scale.left="1" scale.bottom="4" scale.top="24" scale.right="26" />    <texture name="Toolbar_Right_Over" file_name="containers/Toolbar_Right_Over.png" preload="false" scale.left="1" scale.bottom="4" scale.top="24" scale.right="26" /> -  <texture name="Toolbar_Right_Press" file_name="containers/Toolbar_Right_Press.png" preload="false" scale.left="1" scale.bottom="4" scale.top="24" scale.right="26" />    <texture name="Toolbar_Right_Selected" file_name="containers/Toolbar_Right_Selected.png" preload="false" scale.left="1" scale.bottom="4" scale.top="24" scale.right="26" />    <texture name="Tooltip" file_name="widgets/Tooltip.png" preload="true" scale.left="2" scale.top="16" scale.right="100" scale.bottom="3" /> @@ -635,7 +482,6 @@ with the same filename but different name    <texture name="TrashItem_Press" file_name="icons/TrashItem_Press.png" preload="false" />    <texture name="Unread_Chiclet" file_name="bottomtray/Unread_Chiclet.png" preload="false" /> -  <texture name="Unread_Msg" file_name="bottomtray/Unread_Msg.png" preload="false" />    <texture name="Unread_IM" file_name="bottomtray/Unread_IM.png" preload="false" />      <texture name="Volume_Background" file_name="windows/Volume_Background.png" preload="false" @@ -650,10 +496,7 @@ with the same filename but different name    <texture name="WellButton_Lit" file_name="bottomtray/WellButton_Lit.png"  preload="true" scale.left="4" scale.top="19" scale.right="28" scale.bottom="4" />    <texture name="WellButton_Lit_Selected" file_name="bottomtray/WellButton_Lit_Selected.png" preload="true" scale.left="4" scale.top="19" scale.right="28" scale.bottom="4" /> -  <texture name="WebBasedMediaBackground" file_name="windows/WebBasedMediaBackground.png" preload="false" /> -  <texture name="Widget_DownArrow" file_name="icons/Widget_DownArrow.png" preload="true" /> -  <texture name="Widget_UpArrow" file_name="icons/Widget_UpArrow.png" preload="true" />    <texture name="Window_Background" file_name="windows/Window_Background.png" preload="true"             scale.left="4" scale.top="24" scale.right="26" scale.bottom="4" /> @@ -667,17 +510,7 @@ with the same filename but different name    <texture name="YouAreHere_Badge" file_name="icons/YouAreHere_Badge.png" preload="false" />    <texture name="Zoom_Off" file_name="icons/Zoom_Off.png" preload="false" /> -  <texture name="Zoom_Over" file_name="icons/Zoom_Over.png" preload="false" /> -  <texture name="Zoom_Press" file_name="icons/Zoom_Press.png" preload="false" />    <texture name="UnZoom_Off" file_name="icons/UnZoom_Off.png" preload="false" /> -  <texture name="UnZoom_Over" file_name="icons/UnZoom_Over.png" preload="false" /> -  <texture name="UnZoom_Press" file_name="icons/UnZoom_Press.png" preload="false" /> -  <texture name="PowerOn_Off" file_name="icons/PowerOn_Off.png" preload="false" /> -  <texture name="PowerOn_Over" file_name="icons/PowerOn_Over.png" preload="false" /> -  <texture name="PowerOn_Press" file_name="icons/PowerOn_Press.png" preload="false" /> -  <texture name="PowerOff_Off" file_name="icons/PowerOff_Off.png" preload="false" /> -  <texture name="PowerOff_Over" file_name="icons/PowerOff_Over.png" preload="false" /> -  <texture name="PowerOff_Press" file_name="icons/PowerOff_Press.png" preload="false" />    <texture name="pixiesmall.j2c" use_mips="true" />    <texture name="script_error.j2c" use_mips="true" /> @@ -687,7 +520,6 @@ with the same filename but different name    <texture name="transparent.j2c" use_mips="true" />    <!--WARNING OLD ART BELOW *do not use*--> -  <texture name="icn_chatbar.tga" />    <texture name="icn_media_web.tga" preload="true" />    <texture name="icn_media_movie.tga" preload="true" />    <texture name="icn_speaker-muted_dark.tga" /> @@ -717,8 +549,6 @@ with the same filename but different name    <texture name="icn_label_media.tga" />    <texture name="icn_rounded-text-field.tga" scale.left="14" scale.bottom="16" scale.top="16" scale.right="114" /> -  <texture name="toggle_button_off" file_name="toggle_button_off.png" preload="true" /> -  <texture name="toggle_button_selected" file_name="toggle_button_selected.png" preload="true" />    <texture name="color_swatch_alpha.tga" preload="true" />    <texture name="button_anim_pause.tga" /> @@ -728,15 +558,11 @@ with the same filename but different name    <texture name="crosshairs.tga" />    <texture name="direction_arrow.tga" file_name="world/BeaconArrow.png" /> -  <texture name="icon_auction.tga" />    <texture name="icon_avatar_offline.tga" />    <texture name="icon_avatar_online.tga" />    <texture name="icon_day_cycle.tga" />    <texture name="icon_diurnal.tga" /> -  <texture name="icon_event.tga" /> -  <texture name="icon_event_mature.tga" />    <texture name="icon_for_sale.tga" /> -  <texture name="icon_place_for_sale.tga" />    <texture name="icon_top_pick.tga" />    <texture name="lag_status_critical.tga" /> @@ -747,7 +573,6 @@ with the same filename but different name    <texture name="map_avatar_16.tga" />    <texture name="map_avatar_8.tga" /> -  <texture name="map_avatar_you_8.tga" />    <texture name="map_event.tga" />    <texture name="map_event_mature.tga" />    <texture name="map_home.tga" /> @@ -756,15 +581,10 @@ with the same filename but different name    <texture name="map_track_16.tga" />    <texture name="notify_caution_icon.tga" /> -  <texture name="notify_next.png" preload="true" /> -  <texture name="notify_box_icon.tga" />    <texture name="icn_active-speakers-dot-lvl0.tga" />    <texture name="icn_active-speakers-dot-lvl1.tga" />    <texture name="icn_active-speakers-dot-lvl2.tga" /> -  <texture name="icn_active-speakers-typing1.tga" /> -  <texture name="icn_active-speakers-typing2.tga" /> -  <texture name="icn_active-speakers-typing3.tga" />    <texture name="icn_voice_ptt-off.tga" />    <texture name="icn_voice_ptt-on.tga" /> @@ -780,5 +600,4 @@ with the same filename but different name    <texture name="default_profile_picture.j2c" />    <texture name="locked_image.j2c" /> -  <texture name="media_floater_border_16.png" scale_top="12" scale_left="4" scale_bottom="4" scale_right="12" />  </textures> diff --git a/indra/newview/skins/default/xui/en/alert_line_editor.xml b/indra/newview/skins/default/xui/en/alert_line_editor.xml index 97991153d8..82bf5fc8da 100644 --- a/indra/newview/skins/default/xui/en/alert_line_editor.xml +++ b/indra/newview/skins/default/xui/en/alert_line_editor.xml @@ -1,7 +1,6 @@  <?xml version="1.0" encoding="utf-8" standalone="yes" ?>  <line_editor    select_on_focus="false" -  handle_edit_keys_directly="false"    revert_on_esc="true"    commit_on_focus_lost="true"    ignore_tab="true" diff --git a/indra/newview/skins/default/xui/en/floater_about.xml b/indra/newview/skins/default/xui/en/floater_about.xml index d03231a3fa..a55201fd53 100644 --- a/indra/newview/skins/default/xui/en/floater_about.xml +++ b/indra/newview/skins/default/xui/en/floater_about.xml @@ -49,7 +49,7 @@ libcurl Version: [LIBCURL_VERSION]  J2C Decoder Version: [J2C_VERSION]  Audio Driver Version: [AUDIO_DRIVER_VERSION]  Qt Webkit Version: [QT_WEBKIT_VERSION] -Vivox Version: [VIVOX_VERSION] +Voice Server Version: [VOICE_VERSION]  </floater.string>    <floater.string       name="none"> diff --git a/indra/newview/skins/default/xui/en/floater_about_land.xml b/indra/newview/skins/default/xui/en/floater_about_land.xml index 51bd7a0a16..59f1889808 100644 --- a/indra/newview/skins/default/xui/en/floater_about_land.xml +++ b/indra/newview/skins/default/xui/en/floater_about_land.xml @@ -636,7 +636,6 @@ Leyla Linden               </text>               length="1"               enabled="false"               follows="all" -             handle_edit_keys_directly="true"               height="200"               layout="topleft"               left="10" diff --git a/indra/newview/skins/default/xui/en/floater_buy_land.xml b/indra/newview/skins/default/xui/en/floater_buy_land.xml index 125b080519..df44b61632 100644 --- a/indra/newview/skins/default/xui/en/floater_buy_land.xml +++ b/indra/newview/skins/default/xui/en/floater_buy_land.xml @@ -356,7 +356,6 @@ supports [AMOUNT2] objects       length="1"       enabled="false"       follows="top|right" -     handle_edit_keys_directly="false"        height="237"       layout="topleft"       left="444" diff --git a/indra/newview/skins/default/xui/en/floater_outfit_save_as.xml b/indra/newview/skins/default/xui/en/floater_outfit_save_as.xml index a2938e8574..1d73d516d0 100644 --- a/indra/newview/skins/default/xui/en/floater_outfit_save_as.xml +++ b/indra/newview/skins/default/xui/en/floater_outfit_save_as.xml @@ -45,7 +45,6 @@ as a new Outfit:       border_style="line"       border_thickness="1"       follows="left|top" -     handle_edit_keys_directly="true"       height="23"       layout="topleft"       left_delta="0" diff --git a/indra/newview/skins/default/xui/en/floater_preview_notecard.xml b/indra/newview/skins/default/xui/en/floater_preview_notecard.xml index e9099211a3..14c0081c0d 100644 --- a/indra/newview/skins/default/xui/en/floater_preview_notecard.xml +++ b/indra/newview/skins/default/xui/en/floater_preview_notecard.xml @@ -72,7 +72,6 @@       max_length="65536"       name="Notecard Editor"       allow_html="false"  -     handle_edit_keys_directly="true"       tab_group="1"       top="46"       width="392" diff --git a/indra/newview/skins/default/xui/en/floater_test_text_editor.xml b/indra/newview/skins/default/xui/en/floater_test_text_editor.xml index b730f0e511..548e24efba 100644 --- a/indra/newview/skins/default/xui/en/floater_test_text_editor.xml +++ b/indra/newview/skins/default/xui/en/floater_test_text_editor.xml @@ -14,6 +14,7 @@     name="test_text_editor"     tool_tip="text editor"     top="25" +   word_wrap="true"      width="200">      Text Editor    </text_editor> diff --git a/indra/newview/skins/default/xui/en/floater_ui_preview.xml b/indra/newview/skins/default/xui/en/floater_ui_preview.xml index 3a981adfdf..3b10a57c50 100644 --- a/indra/newview/skins/default/xui/en/floater_ui_preview.xml +++ b/indra/newview/skins/default/xui/en/floater_ui_preview.xml @@ -238,7 +238,6 @@               border_thickness="1"               follows="left|bottom"               font="SansSerif" -             handle_edit_keys_directly="true"               height="20"               layout="topleft"               left_delta="100" @@ -278,7 +277,6 @@               border_thickness="1"               follows="left|bottom"               font="SansSerif" -             handle_edit_keys_directly="true"               height="20"               layout="topleft"               left_delta="100" @@ -320,7 +318,6 @@               border_thickness="1"               follows="left|bottom"               font="SansSerif" -             handle_edit_keys_directly="true"               height="20"               layout="topleft"               left_delta="65" diff --git a/indra/newview/skins/default/xui/en/floater_wearable_save_as.xml b/indra/newview/skins/default/xui/en/floater_wearable_save_as.xml index b4b57f2dbc..71812bd1a6 100644 --- a/indra/newview/skins/default/xui/en/floater_wearable_save_as.xml +++ b/indra/newview/skins/default/xui/en/floater_wearable_save_as.xml @@ -44,7 +44,6 @@       border_style="line"       border_thickness="1"       follows="left|top" -     handle_edit_keys_directly="true"       height="23"       layout="topleft"       left_delta="0" diff --git a/indra/newview/skins/default/xui/en/menu_edit.xml b/indra/newview/skins/default/xui/en/menu_edit.xml new file mode 100644 index 0000000000..68f3cb532c --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_edit.xml @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<menu create_jump_keys="true" +      label="Edit" +      name="Edit" +      visible="false"> +  <menu_item_call +   label="Undo" +   name="Undo" +   shortcut="control|Z"> +    <menu_item_call.on_click +     function="Edit.Undo" /> +    <menu_item_call.on_enable +     function="Edit.EnableUndo" /> +  </menu_item_call> +  <menu_item_call +   label="Redo" +   name="Redo" +   shortcut="control|Y"> +    <menu_item_call.on_click +     function="Edit.Redo" /> +    <menu_item_call.on_enable +     function="Edit.EnableRedo" /> +  </menu_item_call> +  <menu_item_separator/> +  <menu_item_call +   label="Cut" +   name="Cut" +   shortcut="control|X"> +    <menu_item_call.on_click +     function="Edit.Cut" /> +    <menu_item_call.on_enable +     function="Edit.EnableCut" /> +  </menu_item_call> +  <menu_item_call +   label="Copy" +   name="Copy" +   shortcut="control|C"> +    <menu_item_call.on_click +     function="Edit.Copy" /> +    <menu_item_call.on_enable +     function="Edit.EnableCopy" /> +  </menu_item_call> +  <menu_item_call +   label="Paste" +   name="Paste" +   shortcut="control|V"> +    <menu_item_call.on_click +     function="Edit.Paste" /> +    <menu_item_call.on_enable +     function="Edit.EnablePaste" /> +  </menu_item_call> +  <menu_item_call +   label="Delete" +   name="Delete" +   shortcut="Del"> +    <menu_item_call.on_click +     function="Edit.Delete" /> +    <menu_item_call.on_enable +     function="Edit.EnableDelete" /> +  </menu_item_call> +  <menu_item_call +   label="Duplicate" +   name="Duplicate" +   shortcut="control|D"> +    <menu_item_call.on_click +     function="Edit.Duplicate" /> +    <menu_item_call.on_enable +     function="Edit.EnableDuplicate" /> +  </menu_item_call> +  <menu_item_separator/> +  <menu_item_call +   label="Select All" +   name="Select All" +   shortcut="control|A"> +    <menu_item_call.on_click +     function="Edit.SelectAll" /> +    <menu_item_call.on_enable +     function="Edit.EnableSelectAll" /> +  </menu_item_call> +  <menu_item_call +   label="Deselect" +   name="Deselect" +   shortcut="control|E"> +    <menu_item_call.on_click +     function="Edit.Deselect" /> +    <menu_item_call.on_enable +     function="Edit.EnableDeselect" /> +  </menu_item_call> +</menu>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/menu_login.xml b/indra/newview/skins/default/xui/en/menu_login.xml index ba74104594..4655fa8c46 100644 --- a/indra/newview/skins/default/xui/en/menu_login.xml +++ b/indra/newview/skins/default/xui/en/menu_login.xml @@ -29,14 +29,6 @@               function="File.Quit" />          </menu_item_call>      </menu> -<!-- Edit menu merged into the Me menu above -    <menu -     create_jump_keys="true" -     label="Edit" -     name="Edit" -     width="153"> -    </menu> --->      <menu       create_jump_keys="true"       label="Help" @@ -59,105 +51,24 @@               parameter="sl_about" />          </menu_item_call>      </menu> +    <menu_item_check +      label="Show Debug Menu" +      name="Show Debug Menu" +      visible="false"  +      shortcut="control|alt|D"> +      <on_check +       function="CheckControl" +       parameter="UseDebugMenus" /> +      <on_click +       function="ToggleControl" +       parameter="UseDebugMenus" /> +    </menu_item_check>      <menu        visible="false"       create_jump_keys="true"       label="Debug"       name="Debug"       tear_off="true"> -      <!-- Need a copy of the edit menu here so keyboard shortcuts like -           control-C work to copy text at login screen and About dialog (for QA) -      --> -      <menu -       create_jump_keys="true" -       label="Edit" -       name="Edit" -       tear_off="true"> -        <menu_item_call -         label="Undo" -         name="Undo" -         shortcut="control|Z"> -          <menu_item_call.on_click -           function="Edit.Undo" /> -          <menu_item_call.on_enable -           function="Edit.EnableUndo" /> -        </menu_item_call> -        <menu_item_call -         label="Redo" -         name="Redo" -         shortcut="control|Y"> -          <menu_item_call.on_click -           function="Edit.Redo" /> -          <menu_item_call.on_enable -           function="Edit.EnableRedo" /> -        </menu_item_call> -        <menu_item_separator /> -        <menu_item_call -         label="Cut" -         name="Cut" -         shortcut="control|X"> -          <menu_item_call.on_click -           function="Edit.Cut" /> -          <menu_item_call.on_enable -           function="Edit.EnableCut" /> -        </menu_item_call> -        <menu_item_call -         label="Copy" -         name="Copy" -         shortcut="control|C"> -          <menu_item_call.on_click -           function="Edit.Copy" /> -          <menu_item_call.on_enable -           function="Edit.EnableCopy" /> -        </menu_item_call> -        <menu_item_call -         label="Paste" -         name="Paste" -         shortcut="control|V"> -          <menu_item_call.on_click -           function="Edit.Paste" /> -          <menu_item_call.on_enable -           function="Edit.EnablePaste" /> -        </menu_item_call> -        <menu_item_call -         label="Delete" -         name="Delete" -         shortcut="Del"> -          <menu_item_call.on_click -           function="Edit.Delete" /> -          <menu_item_call.on_enable -           function="Edit.EnableDelete" /> -        </menu_item_call> -        <menu_item_call -         label="Duplicate" -         name="Duplicate" -         shortcut="control|D"> -          <menu_item_call.on_click -           function="Edit.Duplicate" /> -          <menu_item_call.on_enable -           function="Edit.EnableDuplicate" /> -        </menu_item_call> -        <menu_item_separator /> -        <menu_item_call -         label="Select All" -         name="Select All" -         shortcut="control|A"> -          <menu_item_call.on_click -           function="Edit.SelectAll" /> -          <menu_item_call.on_enable -           function="Edit.EnableSelectAll" /> -        </menu_item_call> -        <menu_item_call -         label="Deselect" -         name="Deselect" -         shortcut="control|E"> -          <menu_item_call.on_click -           function="Edit.Deselect" /> -          <menu_item_call.on_enable -           function="Edit.EnableDeselect" /> -        </menu_item_call> -      </menu> -      <menu_item_separator />        <menu_item_call           label="Show Debug Settings"           name="Debug Settings"> @@ -270,5 +181,27 @@             function="Advanced.WebBrowserTest"             parameter="http://join.secondlife.com/"/>          </menu_item_call> +      <menu_item_separator/> +      <menu_item_check +        label="Show Grid Picker" +        name="Show Grid Picker" +        visible="false"  +        shortcut="control|shift|G"> +        <on_check +         function="CheckControl" +         parameter="ForceShowGrid" /> +        <on_click +         function="ToggleControl" +         parameter="ForceShowGrid" /> +      </menu_item_check> +      <menu_item_call +        label="Show Notifications Console" +        name="Show Notifications Console" +        visible="false" +        shortcut="control|shift|5"> +        <on_click +         function="Floater.Toggle" +         parameter="notifications_console" /> +      </menu_item_call>      </menu>  </menu_bar> diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 551e49daf5..8a062f867a 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -2,16 +2,13 @@  <menu_bar   bg_visible="false"   follows="left|top|right" - layout="topleft"   name="Main Menu">      <menu       label="Me" -     layout="topleft"       name="Me"       tear_off="true">          <menu_item_call           label="Preferences" -         layout="topleft"           name="Preferences"           shortcut="control|P">              <menu_item_call.on_click @@ -20,7 +17,6 @@          </menu_item_call>           <menu_item_call               label="My Dashboard" -             layout="topleft"               name="Manage My Account">                  <menu_item_call.on_click                   function="PromptShowURL" @@ -29,16 +25,13 @@        </menu_item_call>          <menu_item_call           label="Buy L$" -         layout="topleft"           name="Buy and Sell L$">              <menu_item_call.on_click               function="BuyCurrency" />          </menu_item_call> -        <menu_item_separator -         layout="topleft" /> +        <menu_item_separator/>          <menu_item_call           label="My Profile" -         layout="topleft"           name="Profile">              <menu_item_call.on_click               function="ShowAgentProfile" @@ -46,7 +39,6 @@          </menu_item_call>          <menu_item_call           label="My Appearance" -         layout="topleft"           name="Appearance">              <menu_item_call.on_click               function="CustomizeAvatar" /> @@ -56,7 +48,6 @@          <menu_item_check           label="My Inventory"           name="Inventory" -         layout="topleft"           shortcut="control|shift|I"  		 visible="false">              <menu_item_check.on_check @@ -69,7 +60,6 @@          <menu_item_check           label="My Inventory"           name="ShowSidetrayInventory" -         layout="topleft"           shortcut="control|I"  		 visible="true">              <menu_item_check.on_check @@ -81,7 +71,6 @@          </menu_item_check>          <menu_item_check           label="My Gestures" -         layout="topleft"           name="Gestures"           shortcut="control|G">              <menu_item_check.on_check @@ -93,21 +82,17 @@          </menu_item_check>          <menu           label="My Status" -         layout="topleft"           name="Status"           tear_off="true">              <menu_item_call               label="Away" -             layout="topleft"               name="Set Away">                  <menu_item_call.on_click                   function="World.SetAway" />              </menu_item_call> -            <menu_item_separator -             layout="topleft"/> +          <menu_item_separator/>              <menu_item_call               label="Busy" -             layout="topleft"               name="Set Busy">                  <menu_item_call.on_click                   function="World.SetBusy"/> @@ -115,7 +100,6 @@          </menu>          <menu_item_call           label="Request Admin Status" -         layout="topleft"           name="Request Admin Options"           shortcut="control|alt|G"  		 visible="false"> @@ -124,18 +108,15 @@          </menu_item_call>          <menu_item_call           label="Leave Admin Status" -         layout="topleft"           name="Leave Admin Options"           shortcut="control|alt|shift|G"  		 visible="false">              <menu_item_call.on_click               function="Advanced.LeaveAdminStatus" />          </menu_item_call> -        <menu_item_separator -         layout="topleft" /> +        <menu_item_separator/>          <menu_item_call           label="Quit [APP_NAME]" -         layout="topleft"           name="Quit"           shortcut="control|Q">              <menu_item_call.on_click @@ -144,12 +125,10 @@      </menu>      <menu       label="Communicate" -     layout="topleft"       name="Communicate"       tear_off="true">          <menu_item_call           label="My Friends" -         layout="topleft"           name="My Friends"           shortcut="control|shift|F">              <menu_item_call.on_click @@ -158,25 +137,21 @@              </menu_item_call>          <menu_item_call           label="My Groups" -         layout="topleft"           name="My Groups"           shortcut="control|shift|G">              <menu_item_call.on_click               function="SideTray.PanelPeopleTab"               parameter="groups_panel" />          </menu_item_call> -        <menu_item_separator -         layout="topleft" /> +        <menu_item_separator/>          <!--menu_item_call           label="Chat" -         layout="topleft"           name="Chat">              <menu_item_call.on_click               function="World.Chat" />          </menu_item_call-->          <menu_item_check           label="Nearby Chat" -         layout="topleft"           name="Nearby Chat"           shortcut="control|H"           use_mac_ctrl="true"> @@ -189,7 +164,6 @@          </menu_item_check>          <menu_item_call           label="Nearby People" -         layout="topleft"           name="Active Speakers"           shortcut="control|shift|A">              <menu_item_call.on_click @@ -199,12 +173,10 @@      </menu>      <menu       label="World" -     layout="topleft"       name="World"       tear_off="true">              <menu_item_check           label="Mini-Map" -         layout="topleft"           name="Mini-Map"           shortcut="control|shift|M">              <menu_item_check.on_check @@ -216,7 +188,6 @@          </menu_item_check>           <menu_item_check           label="World Map" -         layout="topleft"           name="World Map"           shortcut="control|M"           use_mac_ctrl="true"> @@ -229,7 +200,6 @@          </menu_item_check>          <menu_item_call           label="Snapshot" -         layout="topleft"           name="Take Snapshot"           shortcut="control|shift|S">              <menu_item_call.on_click @@ -238,7 +208,6 @@          </menu_item_call>        <menu_item_call               label="Landmark This Place" -             layout="topleft"               name="Create Landmark Here">                  <menu_item_call.on_click                   function="World.CreateLandmark" /> @@ -248,7 +217,6 @@        <menu             create_jump_keys="true"             label="Place Profile" -           layout="topleft"             name="Land"             tear_off="true">          <menu_item_call @@ -260,7 +228,6 @@          </menu_item_call>          <menu_item_call           label="About Land" -         layout="topleft"           name="About Land">              <menu_item_call.on_click               function="Floater.Show" @@ -268,18 +235,15 @@          </menu_item_call>          <menu_item_call           label="Region/Estate" -         layout="topleft"           name="Region/Estate">              <menu_item_call.on_click               function="Floater.Show"               parameter="region_info" />          </menu_item_call>          </menu> -        <menu_item_separator -         layout="topleft" /> +        <menu_item_separator/>          <menu_item_call               label="Buy This Land" -             layout="topleft"               name="Buy Land">                  <menu_item_call.on_click                   function="Land.Buy" /> @@ -288,7 +252,6 @@              </menu_item_call>          <menu_item_call               label="My Land" -             layout="topleft"               name="My Land">                  <menu_item_call.on_click                   function="Floater.Show" @@ -297,12 +260,10 @@          <menu             create_jump_keys="true"             label="Show" -           layout="topleft"             name="LandShow"             tear_off="true">           <menu_item_check           label="Move Controls" -         layout="topleft"           name="Movement Controls">              <menu_item_check.on_check               function="Floater.Visible" @@ -312,7 +273,6 @@          </menu_item_check>          <menu_item_check           label="View Controls" -         layout="topleft"           name="Camera Controls">              <menu_item_check.on_check               function="Floater.Visible" @@ -322,7 +282,6 @@          </menu_item_check>            <menu_item_check               label="Ban Lines" -             layout="topleft"               name="Ban Lines">              <menu_item_check.on_check                 control="ShowBanLines" /> @@ -332,7 +291,6 @@            </menu_item_check>             <menu_item_check                   label="Beacons" -                 layout="topleft"                   name="beacons"                   shortcut="control|alt|shift|N">                      <menu_item_check.on_check @@ -344,7 +302,6 @@                  </menu_item_check>            <menu_item_check               label="Property Lines" -             layout="topleft"               name="Property Lines"               shortcut="control|alt|shift|P">              <menu_item_check.on_check @@ -355,7 +312,6 @@            </menu_item_check>            <menu_item_check               label="Land Owners" -             layout="topleft"               name="Land Owners">              <menu_item_check.on_check                 control="ShowParcelOwners" /> @@ -382,11 +338,9 @@                 control="NavBarShowParcelProperties" />            </menu_item_check>          </menu> -        <menu_item_separator -         layout="topleft" /> +        <menu_item_separator/>  	    <menu_item_call  	     label="Teleport Home" -	     layout="topleft"  	     name="Teleport Home"  	     shortcut="control|shift|H">  		<menu_item_call.on_click @@ -396,7 +350,6 @@  	    </menu_item_call>              <menu_item_call               label="Set Home to Here" -             layout="topleft"               name="Set Home to Here">                  <menu_item_call.on_click                   function="World.SetHomeLocation" /> @@ -405,7 +358,6 @@              </menu_item_call>      <!--    <menu_item_check           label="Show Navigation Bar" -         layout="topleft"           name="ShowNavbarNavigationPanel">             <menu_item_check.on_click               function="ToggleControl" @@ -416,7 +368,6 @@          </menu_item_check>         <menu_item_check           label="Show Favorites Bar" -         layout="topleft"           name="ShowNavbarFavoritesPanel">             <menu_item_check.on_click               function="ToggleControl" @@ -425,19 +376,15 @@               function="CheckControl"               parameter="ShowNavbarFavoritesPanel" />          </menu_item_check> -        <menu_item_separator -         layout="topleft" />--> -        <menu_item_separator -         layout="topleft" /> +        <menu_item_separator/> +        <menu_item_separator/>      <menu           create_jump_keys="true"           label="Sun" -         layout="topleft"           name="Environment Settings"           tear_off="true">              <menu_item_call               label="Sunrise" -             layout="topleft"               name="Sunrise">                  <menu_item_call.on_click                   function="World.EnvSettings" @@ -445,7 +392,6 @@              </menu_item_call>              <menu_item_call               label="Midday" -             layout="topleft"               name="Noon"               shortcut="control|shift|Y">                  <menu_item_call.on_click @@ -454,7 +400,6 @@              </menu_item_call>              <menu_item_call               label="Sunset" -             layout="topleft"               name="Sunset"               shortcut="control|shift|N">                  <menu_item_call.on_click @@ -463,7 +408,6 @@              </menu_item_call>              <menu_item_call               label="Midnight" -             layout="topleft"               name="Midnight">                  <menu_item_call.on_click                   function="World.EnvSettings" @@ -471,17 +415,14 @@              </menu_item_call>              <menu_item_call               label="Estate Time" -             layout="topleft"               name="Revert to Region Default">                  <menu_item_call.on_click                   function="World.EnvSettings"                   parameter="default" />              </menu_item_call> -            <menu_item_separator -             layout="topleft" /> +            <menu_item_separator/>              <menu_item_call               label="Environment Editor" -             layout="topleft"               name="Environment Editor">                  <menu_item_call.on_click                   function="World.EnvSettings" @@ -492,13 +433,11 @@      <menu       create_jump_keys="true"       label="Build" -     layout="topleft"       name="BuildTools"       tear_off="true"       visible="true">         <menu_item_check           label="Build" -         layout="topleft"           name="Show Build Tools"           shortcut="control|B">              <menu_item_check.on_check @@ -511,12 +450,10 @@         <menu            create_jump_keys="true"            label="Select Build Tool" -          layout="topleft"            name="Select Tool"            tear_off="true">           <menu_item_call  			label="Focus Tool" -			layout="topleft"  			name="Focus"  			shortcut="control|1">             <menu_item_call.on_click @@ -525,7 +462,6 @@           </menu_item_call>           <menu_item_call  			label="Move Tool" -			layout="topleft"  			name="Move"  			shortcut="control|2">             <menu_item_call.on_click @@ -534,7 +470,6 @@           </menu_item_call>           <menu_item_call  			label="Edit Tool" -			layout="topleft"  			name="Edit"  			shortcut="control|3">             <menu_item_call.on_click @@ -543,7 +478,6 @@           </menu_item_call>           <menu_item_call  			label="Create Tool" -			layout="topleft"  			name="Create"  			shortcut="control|4">             <menu_item_call.on_click @@ -552,7 +486,6 @@           </menu_item_call>           <menu_item_call  			label="Land Tool" -			layout="topleft"  			name="Land"  			shortcut="control|5">             <menu_item_call.on_click @@ -560,112 +493,8 @@                parameter="land" />           </menu_item_call>  	   </menu> -        <menu -         create_jump_keys="true" -         label="Edit" -         layout="topleft" -         name="Edit" -         tear_off="true"> -            <menu_item_call -             label="Undo" -             layout="topleft" -             name="Undo" -             shortcut="control|Z"> -                <menu_item_call.on_click -                 function="Edit.Undo" /> -                <menu_item_call.on_enable -                 function="Edit.EnableUndo" /> -            </menu_item_call> -            <menu_item_call -             label="Redo" -             layout="topleft" -             name="Redo" -             shortcut="control|Y"> -                <menu_item_call.on_click -                 function="Edit.Redo" /> -                <menu_item_call.on_enable -                 function="Edit.EnableRedo" /> -            </menu_item_call> -            <menu_item_separator -             layout="topleft" /> -            <menu_item_call -             label="Cut" -             layout="topleft" -             name="Cut" -             shortcut="control|X"> -                <menu_item_call.on_click -                 function="Edit.Cut" /> -                <menu_item_call.on_enable -                 function="Edit.EnableCut" /> -            </menu_item_call> -            <menu_item_call -             label="Copy" -             layout="topleft" -             name="Copy" -             shortcut="control|C"> -                <menu_item_call.on_click -                 function="Edit.Copy" /> -                <menu_item_call.on_enable -                 function="Edit.EnableCopy" /> -            </menu_item_call> -            <menu_item_call -             label="Paste" -             layout="topleft" -             name="Paste" -             shortcut="control|V"> -                <menu_item_call.on_click -                 function="Edit.Paste" /> -                <menu_item_call.on_enable -                 function="Edit.EnablePaste" /> -            </menu_item_call> -            <menu_item_call -             label="Delete" -             layout="topleft" -             name="Delete" -             shortcut="Del"> -                <menu_item_call.on_click -                 function="Edit.Delete" /> -                <menu_item_call.on_enable -                 function="Edit.EnableDelete" /> -            </menu_item_call> -            <menu_item_call -             label="Duplicate" -             layout="topleft" -             name="Duplicate" -             shortcut="control|D"> -                <menu_item_call.on_click -                 function="Edit.Duplicate" /> -                <menu_item_call.on_enable -                 function="Edit.EnableDuplicate" /> -            </menu_item_call> -            <menu_item_separator -             layout="topleft" /> -            <menu_item_call -             label="Select All" -             layout="topleft" -             name="Select All" -             shortcut="control|A"> -                <menu_item_call.on_click -                 function="Edit.SelectAll" /> -                <menu_item_call.on_enable -                 function="Edit.EnableSelectAll" /> -            </menu_item_call> -            <menu_item_call -             label="Deselect" -             layout="topleft" -             name="Deselect" -             shortcut="control|E"> -                <menu_item_call.on_click -                 function="Edit.Deselect" /> -                <menu_item_call.on_enable -                 function="Edit.EnableDeselect" /> -            </menu_item_call> -        </menu> -        <menu_item_separator -           layout="topleft" />          <menu_item_call             label="Link" -           layout="topleft"             name="Link"             shortcut="control|L">            <menu_item_call.on_click @@ -675,7 +504,6 @@          </menu_item_call>          <menu_item_call             label="Unlink" -           layout="topleft"             name="Unlink"             shortcut="control|shift|L">            <menu_item_call.on_click @@ -685,7 +513,6 @@          </menu_item_call>          <menu_item_check               label="Edit Linked Parts" -             layout="topleft"               name="Edit Linked Parts">                  <menu_item_check.on_check                   control="EditLinkedParts" /> @@ -695,11 +522,9 @@                  <menu_item_check.on_enable                   function="Tools.EnableToolNotPie" />              </menu_item_check> -        <menu_item_separator -           layout="topleft" /> +        <menu_item_separator/>          <menu_item_call             label="Focus on Selection" -           layout="topleft"             name="Focus on Selection"             shortcut="H">            <menu_item_call.on_click @@ -710,7 +535,6 @@          </menu_item_call>          <menu_item_call             label="Zoom to Selection" -           layout="topleft"             name="Zoom to Selection"             shortcut="shift|H">            <menu_item_call.on_click @@ -719,17 +543,14 @@            <menu_item_call.on_enable               function="Tools.SomethingSelectedNoHUD" />          </menu_item_call> -        <menu_item_separator -           layout="topleft" /> +        <menu_item_separator/>          <menu           create_jump_keys="true"           label="Object" -         layout="topleft"           name="Object"           tear_off="true">            <menu_item_call               label="Buy" -             layout="topleft"               name="Menu Object Buy">              <menu_item_call.on_click                 function="Tools.BuyOrTake"/> @@ -740,7 +561,6 @@            </menu_item_call>            <menu_item_call               label="Take" -             layout="topleft"               name="Menu Object Take">              <menu_item_call.on_click                 function="Tools.BuyOrTake"/> @@ -751,7 +571,6 @@            </menu_item_call>            <menu_item_call  			 label="Take Copy" -			 layout="topleft"  			 name="Take Copy">  			<menu_item_call.on_click                 function="Tools.TakeCopy" /> @@ -760,7 +579,6 @@            </menu_item_call>            <menu_item_call  			 label="Save Back to My Inventory" -			 layout="topleft"  			 name="Save Object Back to My Inventory">  			<menu_item_call.on_click                 function="Tools.SaveToInventory" /> @@ -769,7 +587,6 @@            </menu_item_call>            <menu_item_call  			 label="Save Back to Object Contents" -			 layout="topleft"  			 name="Save Object Back to Object Contents">  			<menu_item_call.on_click                 function="Tools.SaveToObjectInventory" /> @@ -780,12 +597,10 @@          <menu             create_jump_keys="true"             label="Scripts" -           layout="topleft"             name="Scripts"             tear_off="true">            <menu_item_call               label="Recompile Scripts (Mono)" -             layout="topleft"               name="Mono">              <menu_item_call.on_click                 function="Tools.SelectedScriptAction" @@ -795,7 +610,6 @@            </menu_item_call>            <menu_item_call               label="Recompile Scripts (LSL)" -             layout="topleft"               name="LSL">              <menu_item_call.on_click                 function="Tools.SelectedScriptAction" @@ -805,7 +619,6 @@            </menu_item_call>            <menu_item_call               label="Reset Scripts" -             layout="topleft"               name="Reset Scripts">              <menu_item_call.on_click                 function="Tools.SelectedScriptAction" @@ -815,7 +628,6 @@            </menu_item_call>            <menu_item_call               label="Set Scripts to Running" -             layout="topleft"               name="Set Scripts to Running">              <menu_item_call.on_click                 function="Tools.SelectedScriptAction" @@ -825,7 +637,6 @@            </menu_item_call>            <menu_item_call               label="Set Scripts to Not Running" -             layout="topleft"               name="Set Scripts to Not Running">              <menu_item_call.on_click                 function="Tools.SelectedScriptAction" @@ -834,17 +645,14 @@                 function="EditableSelected" />            </menu_item_call>          </menu> -        <menu_item_separator -           layout="topleft" /> +        <menu_item_separator/>          <menu           create_jump_keys="true"           label="Options" -         layout="topleft"           name="Options"           tear_off="true">              <menu_item_call               label="Set Default Upload Permissions" -             layout="topleft"               name="perm prefs">                  <menu_item_call.on_click                   function="Floater.Toggle" @@ -852,7 +660,6 @@              </menu_item_call>  	   <menu_item_check  	       label="Show Advanced Permissions" -	       layout="topleft"  	       name="DebugPermissions">  			  <menu_item_check.on_check  				 function="CheckControl" @@ -861,11 +668,9 @@  				 function="ToggleControl"  				 parameter="DebugPermissions" />  			</menu_item_check> -            <menu_item_separator -             layout="topleft" /> +            <menu_item_separator/>              <menu_item_check                   label="Select Only My Objects" -                 layout="topleft"                   name="Select Only My Objects">                      <menu_item_check.on_check                       control="SelectOwnedOnly" /> @@ -875,7 +680,6 @@                  </menu_item_check>                  <menu_item_check                   label="Select Only Movable Objects" -                 layout="topleft"                   name="Select Only Movable Objects">                      <menu_item_check.on_check                       control="SelectMovableOnly" /> @@ -885,18 +689,15 @@                  </menu_item_check>                  <menu_item_check                   label="Select By Surrounding" -                 layout="topleft"                   name="Select By Surrounding">                      <menu_item_check.on_check                       control="RectangleSelectInclusive" />                      <menu_item_check.on_click                       function="Tools.SelectBySurrounding" />              </menu_item_check> -          <menu_item_separator -           layout="topleft" /> +          <menu_item_separator/>                  <menu_item_check                   label="Show Hidden Selection" -                 layout="topleft"                   name="Show Hidden Selection">                      <menu_item_check.on_check                       control="RenderHiddenSelections" /> @@ -905,7 +706,6 @@                  </menu_item_check>                  <menu_item_check                   label="Show Light Radius for Selection" -                 layout="topleft"                   name="Show Light Radius for Selection">                      <menu_item_check.on_check                       control="RenderLightRadius" /> @@ -914,7 +714,6 @@                  </menu_item_check>                  <menu_item_check                   label="Show Selection Beam" -                 layout="topleft"                   name="Show Selection Beam">                      <menu_item_check.on_check                       control="ShowSelectionBeam" /> @@ -922,11 +721,9 @@                       function="ToggleControl"                       parameter="ShowSelectionBeam" />                  </menu_item_check> -        <menu_item_separator -           layout="topleft" /> +        <menu_item_separator/>                  <menu_item_check                   label="Snap to Grid" -                 layout="topleft"                   name="Snap to Grid"                   shortcut="G">                      <menu_item_check.on_check @@ -939,7 +736,6 @@                  </menu_item_check>                  <menu_item_call                   label="Snap Object XY to Grid" -                 layout="topleft"                   name="Snap Object XY to Grid"                   shortcut="shift|X">                      <menu_item_call.on_click @@ -949,7 +745,6 @@                  </menu_item_call>                  <menu_item_call                   label="Use Selection for Grid" -                 layout="topleft"                   name="Use Selection for Grid"                   shortcut="shift|G">                      <menu_item_call.on_click @@ -959,7 +754,6 @@                  </menu_item_call>                  <menu_item_call                   label="Grid Options" -                 layout="topleft"                   name="Grid Options"                   shortcut="control|shift|B">                      <menu_item_call.on_click @@ -972,12 +766,10 @@          <menu           create_jump_keys="true"           label="Select Linked Parts" -         layout="topleft"           name="Select Linked Parts"           tear_off="true">              <menu_item_call               label="Select Next Part" -             layout="topleft"               name="Select Next Part"  	     shortcut="control|.">                  <menu_item_call.on_click @@ -988,7 +780,6 @@              </menu_item_call>              <menu_item_call               label="Select Previous Part" -             layout="topleft"               name="Select Previous Part"  	     shortcut="control|,">                  <menu_item_call.on_click @@ -999,7 +790,6 @@              </menu_item_call>              <menu_item_call               label="Include Next Part" -             layout="topleft"               name="Include Next Part"  	     shortcut="control|shift|.">                  <menu_item_call.on_click @@ -1010,7 +800,6 @@              </menu_item_call>              <menu_item_call               label="Include Previous Part" -             layout="topleft"               name="Include Previous Part"  	     shortcut="control|shift|,">                  <menu_item_call.on_click @@ -1023,48 +812,40 @@      </menu>      <menu       label="Help" -     layout="topleft"       name="Help"       tear_off="true">          <menu_item_call           label="[SECOND_LIFE] Help" -         layout="topleft"           name="Second Life Help"           shortcut="F1">              <menu_item_call.on_click               function="ShowHelp"               parameter="f1_help" />          </menu_item_call> -  <!--      <menu_item_call +        <menu_item_call           label="Tutorial" -         layout="topleft"           name="Tutorial">              <menu_item_call.on_click               function="Floater.Show"               parameter="hud" />          </menu_item_call>--> -        <menu_item_separator -             layout="topleft" /> +        <menu_item_separator/>          <menu_item_call               label="Report Abuse" -             layout="topleft"               name="Report Abuse">                  <menu_item_call.on_click                   function="ReportAbuse" />              </menu_item_call>          <menu_item_call               label="Report Bug" -             layout="topleft"               name="Report Bug">                  <menu_item_call.on_click                   function="ShowHelp"                   parameter="report_bug" />              </menu_item_call> -        <menu_item_separator -             layout="topleft" /> +        <menu_item_separator/>          <menu_item_call           label="About [APP_NAME]" -         layout="topleft"           name="About Second Life">              <menu_item_call.on_click               function="Floater.Show" @@ -1073,20 +854,28 @@      </menu>      <menu       label="Advanced" -     layout="topleft"       name="Advanced"       tear_off="true"       visible="false"> +        <menu_item_check +         label="Show Advanced Menu" +         name="Show Advanced Menu" +         shortcut="control|alt|D"> +          <on_check +           function="CheckControl" +           parameter="UseDebugMenus" /> +          <on_click +           function="ToggleControl" +           parameter="UseDebugMenus" /> +        </menu_item_check>          <menu_item_call           label="Stop Animating Me" -         layout="topleft"           name="Stop Animating My Avatar">              <menu_item_call.on_click               function="Tools.StopAllAnimations" />          </menu_item_call>          <menu_item_call           label="Rebake Textures" -         layout="topleft"           name="Rebake Texture"           shortcut="control|alt|R">              <menu_item_call.on_click @@ -1094,7 +883,6 @@          </menu_item_call>          <menu_item_call             label="Set UI Size to Default" -           layout="topleft"             name="Set UI Size to Default">            <menu_item_call.on_click               function="View.DefaultUISize" /> @@ -1109,7 +897,6 @@          <menu_item_separator/>          <menu_item_check           label="Limit Select Distance" -         layout="topleft"           name="Limit Select Distance">              <menu_item_check.on_check               function="CheckControl" @@ -1120,7 +907,6 @@          </menu_item_check>          <menu_item_check           label="Disable Camera Constraints" -         layout="topleft"           name="Disable Camera Distance">              <menu_item_check.on_check               function="CheckControl" @@ -1129,11 +915,9 @@               function="ToggleControl"               parameter="DisableCameraConstraints" />          </menu_item_check> -        <menu_item_separator -         layout="topleft" /> +        <menu_item_separator/>          <menu_item_check           label="High-res Snapshot" -         layout="topleft"           name="HighResSnapshot">              <menu_item_check.on_check               function="CheckControl" @@ -1144,7 +928,6 @@          </menu_item_check>          <menu_item_check           label="Quiet Snapshots to Disk" -         layout="topleft"           name="QuietSnapshotsToDisk">              <menu_item_check.on_check               function="CheckControl" @@ -1155,7 +938,6 @@          </menu_item_check>          <menu_item_check           label="Compress Snapshots to Disk" -         layout="topleft"           name="CompressSnapshotsToDisk">              <menu_item_check.on_check               function="CheckControl" @@ -1164,17 +946,14 @@               function="ToggleControl"               parameter="CompressSnapshotsToDisk" />          </menu_item_check> -        <menu_item_separator -         layout="topleft" /> +        <menu_item_separator/>          <menu           create_jump_keys="true"           label="Performance Tools" -         layout="topleft"           name="Performance Tools"           tear_off="true">              <menu_item_call               label="Lag Meter" -             layout="topleft"               name="Lag Meter">                  <menu_item_call.on_click                   function="Floater.Show" @@ -1182,7 +961,6 @@              </menu_item_call>              <menu_item_check               label="Statistics Bar" -             layout="topleft"               name="Statistics Bar"               shortcut="control|shift|1">                  <menu_item_check.on_check @@ -1194,7 +972,6 @@              </menu_item_check>        <menu_item_check          label="Show Avatar Rendering Cost" -        layout="topleft"          name="Avatar Rendering Cost">             <menu_item_check.on_check              function="Advanced.CheckInfoDisplay" @@ -1207,12 +984,10 @@          <menu           create_jump_keys="true"           label="Highlighting and Visibility" -         layout="topleft"           name="Highlighting and Visibility"           tear_off="true">           <menu_item_check                   label="Cheesy Beacon" -                 layout="topleft"                   name="Cheesy Beacon">                      <menu_item_check.on_check                       function="CheckControl" @@ -1223,7 +998,6 @@                  </menu_item_check>              <menu_item_check               label="Hide Particles" -             layout="topleft"               name="Hide Particles"               shortcut="control|alt|shift|=">                  <menu_item_check.on_check @@ -1235,7 +1009,6 @@              </menu_item_check>              <menu_item_check               label="Hide Selected" -             layout="topleft"               name="Hide Selected">                  <menu_item_check.on_check                   function="CheckControl" @@ -1246,7 +1019,6 @@              </menu_item_check>              <menu_item_check               label="Highlight Transparent" -             layout="topleft"               name="Highlight Transparent"               shortcut="control|alt|T">                  <menu_item_check.on_check @@ -1256,7 +1028,6 @@              </menu_item_check>              <menu_item_check               label="Show HUD Attachments" -             layout="topleft"               name="Show HUD Attachments"               shortcut="alt|shift|H">                  <menu_item_check.on_check @@ -1266,7 +1037,6 @@              </menu_item_check>              <menu_item_check               label="Show Mouselook Crosshairs" -             layout="topleft"               name="ShowCrosshairs">                  <menu_item_check.on_check                   function="CheckControl" @@ -1278,12 +1048,10 @@    <!-- <menu           create_jump_keys="true"           label="Hover Tips" -         layout="topleft"           name="Hover Tips"           tear_off="true">              <menu_item_check               label="Show Tips" -             layout="topleft"               name="Show Tips"               shortcut="control|shift|T">                  <menu_item_check.on_check @@ -1291,11 +1059,9 @@                  <menu_item_check.on_click                   function="View.ShowHoverTips" />              </menu_item_check> -            <menu_item_separator -             layout="topleft" />--> +            <menu_item_separator/>              <menu_item_check               label="Show Land Tooltips" -             layout="topleft"               name="Land Tips">                  <menu_item_check.on_check                   control="ShowLandHoverTip" /> @@ -1305,9 +1071,8 @@                  <menu_item_check.on_enable                   function="View.CheckShowHoverTips" />              </menu_item_check> - <!--           <menu_item_check +           <menu_item_check               label="Show Tips On All Objects" -             layout="topleft"               name="Tips On All Objects">                  <menu_item_check.on_check                   control="ShowAllObjectHoverTip" /> @@ -1324,12 +1089,10 @@          <menu           create_jump_keys="true"           label="Rendering Types" -         layout="topleft"           name="Rendering Types"           tear_off="true">              <menu_item_check               label="Simple" -             layout="topleft"               name="Simple"               shortcut="control|alt|shift|1">                  <menu_item_check.on_check @@ -1341,7 +1104,6 @@              </menu_item_check>              <menu_item_check               label="Alpha" -             layout="topleft"               name="Alpha"               shortcut="control|alt|shift|2">                  <menu_item_check.on_check @@ -1353,7 +1115,6 @@              </menu_item_check>              <menu_item_check               label="Tree" -             layout="topleft"               name="Tree"               shortcut="control|alt|shift|3">                  <menu_item_check.on_check @@ -1365,7 +1126,6 @@              </menu_item_check>              <menu_item_check               label="Avatars" -             layout="topleft"               name="Character"               shortcut="control|alt|shift|4">                  <menu_item_check.on_check @@ -1377,7 +1137,6 @@              </menu_item_check>              <menu_item_check               label="SurfacePath" -             layout="topleft"               name="SurfacePath"               shortcut="control|alt|shift|5">                  <menu_item_check.on_check @@ -1389,7 +1148,6 @@              </menu_item_check>              <menu_item_check               label="Sky" -             layout="topleft"               name="Sky"               shortcut="control|alt|shift|6">                  <menu_item_check.on_check @@ -1401,7 +1159,6 @@              </menu_item_check>              <menu_item_check               label="Water" -             layout="topleft"               name="Water"               shortcut="control|alt|shift|7">                  <menu_item_check.on_check @@ -1413,7 +1170,6 @@              </menu_item_check>              <menu_item_check               label="Ground" -             layout="topleft"               name="Ground"               shortcut="control|alt|shift|8">                  <menu_item_check.on_check @@ -1425,7 +1181,6 @@              </menu_item_check>              <menu_item_check               label="Volume" -             layout="topleft"               name="Volume"               shortcut="control|alt|shift|9">                  <menu_item_check.on_check @@ -1437,7 +1192,6 @@              </menu_item_check>              <menu_item_check               label="Grass" -             layout="topleft"               name="Grass"               shortcut="control|alt|shift|0">                  <menu_item_check.on_check @@ -1449,7 +1203,6 @@              </menu_item_check>              <menu_item_check               label="Clouds" -             layout="topleft"               name="Clouds"               shortcut="control|alt|shift|-">                  <menu_item_check.on_check @@ -1461,7 +1214,6 @@              </menu_item_check>              <menu_item_check               label="Particles" -             layout="topleft"               name="Particles"               shortcut="control|alt|shift|=">                  <menu_item_check.on_check @@ -1473,7 +1225,6 @@              </menu_item_check>              <menu_item_check               label="Bump" -             layout="topleft"               name="Bump"               shortcut="control|alt|shift|\">                  <menu_item_check.on_check @@ -1487,12 +1238,10 @@          <menu           create_jump_keys="true"           label="Rendering Features" -         layout="topleft"           name="Rendering Features"           tear_off="true">              <menu_item_check               label="UI" -             layout="topleft"               name="UI"               shortcut="control|alt|F1">                  <menu_item_check.on_check @@ -1504,7 +1253,6 @@              </menu_item_check>              <menu_item_check               label="Selected" -             layout="topleft"               name="Selected"               shortcut="control|alt|F2">                  <menu_item_check.on_check @@ -1516,7 +1264,6 @@              </menu_item_check>              <menu_item_check               label="Highlighted" -             layout="topleft"               name="Highlighted"               shortcut="control|alt|F3">                  <menu_item_check.on_check @@ -1528,7 +1275,6 @@              </menu_item_check>              <menu_item_check               label="Dynamic Textures" -             layout="topleft"               name="Dynamic Textures"               shortcut="control|alt|F4">                  <menu_item_check.on_check @@ -1540,7 +1286,6 @@              </menu_item_check>              <menu_item_check               label="Foot Shadows" -             layout="topleft"               name="Foot Shadows"               shortcut="control|alt|F5">                  <menu_item_check.on_check @@ -1552,7 +1297,6 @@              </menu_item_check>              <menu_item_check               label="Fog" -             layout="topleft"               name="Fog"               shortcut="control|alt|F6">                  <menu_item_check.on_check @@ -1564,7 +1308,6 @@              </menu_item_check>              <menu_item_check               label="Test FRInfo" -             layout="topleft"               name="Test FRInfo"               shortcut="control|alt|F8">                  <menu_item_check.on_check @@ -1576,7 +1319,6 @@              </menu_item_check>              <menu_item_check               label="Flexible Objects" -             layout="topleft"               name="Flexible Objects"               shortcut="control|alt|F9">                  <menu_item_check.on_check @@ -1589,7 +1331,6 @@          </menu>          <menu_item_check           label="Run Multiple Threads" -         layout="topleft"           name="Run Multiple Threads">              <menu_item_check.on_check               function="CheckControl" @@ -1600,7 +1341,6 @@          </menu_item_check>          <menu_item_call           label="Clear Group Cache" -         layout="topleft"           name="ClearGroupCache">              <menu_item_call.on_click               function="Advanced.ClearGroupCache" @@ -1608,7 +1348,6 @@          </menu_item_call>          <menu_item_check           label="Mouse Smoothing" -         layout="topleft"           name="Mouse Smoothing">              <menu_item_check.on_check               function="CheckControl" @@ -1617,17 +1356,14 @@               function="ToggleControl"               parameter="MouseSmooth" />          </menu_item_check> -        <menu_item_separator -         layout="topleft" /> +        <menu_item_separator/>          <menu           label="Shortcuts" -         layout="topleft"           name="Shortcuts"           tear_off="true"           visible="false">            <menu_item_call               label="Image (L$[COST])..." -             layout="topleft"               name="Upload Image"               shortcut="control|U">              <menu_item_call.on_click @@ -1638,7 +1374,6 @@              </menu_item_call>              <menu_item_check                 label="Search" -               layout="topleft"                 name="Search"                 shortcut="control|F">              <menu_item_check.on_check @@ -1651,7 +1386,6 @@              <menu_item_call               enabled="false"               label="Release Keys" -             layout="topleft"               name="Release Keys">                  <menu_item_call.on_click                   function="Tools.ReleaseKeys" @@ -1662,16 +1396,13 @@              </menu_item_call>              <menu_item_call               label="Set UI Size to Default" -             layout="topleft"               name="Set UI Size to Default">                  <menu_item_call.on_click                   function="View.DefaultUISize" />              </menu_item_call> -            <menu_item_separator -             layout="topleft" /> +            <menu_item_separator/>              <menu_item_check               label="Always Run" -             layout="topleft"               name="Always Run"               shortcut="control|R">                  <menu_item_check.on_check @@ -1681,7 +1412,6 @@              </menu_item_check>              <menu_item_check               label="Fly" -             layout="topleft"               name="Fly"               shortcut="Home">                  <menu_item_check.on_check @@ -1691,11 +1421,9 @@                  <menu_item_check.on_enable                   function="Agent.enableFlying" />              </menu_item_check> -            <menu_item_separator -             layout="topleft" /> +            <menu_item_separator/>              <menu_item_call               label="Close Window" -             layout="topleft"               name="Close Window"               shortcut="control|W">                  <menu_item_call.on_click @@ -1705,7 +1433,6 @@              </menu_item_call>              <menu_item_call               label="Close All Windows" -             layout="topleft"               name="Close All Windows"               shortcut="control|shift|W">                  <menu_item_call.on_click @@ -1713,22 +1440,18 @@                  <menu_item_call.on_enable                   function="File.EnableCloseAllWindows" />              </menu_item_call> -            <menu_item_separator -             layout="topleft" /> +            <menu_item_separator/>              <menu_item_call               label="Snapshot to Disk" -             layout="topleft"               name="Snapshot to Disk"               shortcut="control|`"               use_mac_ctrl="true">                  <menu_item_call.on_click                   function="File.TakeSnapshotToDisk" />              </menu_item_call> -            <menu_item_separator -             layout="topleft" /> +            <menu_item_separator/>              <menu_item_call               label="Mouselook" -             layout="topleft"               name="Mouselook"               shortcut="M">                  <menu_item_call.on_click @@ -1738,7 +1461,6 @@              </menu_item_call>              <menu_item_check               label="Joystick Flycam" -             layout="topleft"               name="Joystick Flycam"               shortcut="alt|shift|F">                  <menu_item_check.on_check @@ -1750,7 +1472,6 @@              </menu_item_check>              <menu_item_call               label="Reset View" -             layout="topleft"               name="Reset View"               shortcut="Esc">                  <menu_item_call.on_click @@ -1758,7 +1479,6 @@              </menu_item_call>              <menu_item_call               label="Look at Last Chatter" -             layout="topleft"               name="Look at Last Chatter"               shortcut="control|\">                  <menu_item_call.on_click @@ -1766,17 +1486,14 @@                  <menu_item_call.on_enable                   function="View.EnableLastChatter" />              </menu_item_call> -            <menu_item_separator -             layout="topleft" /> +            <menu_item_separator/>              <menu               create_jump_keys="true"               label="Select Build Tool" -             layout="topleft"               name="Select Tool"               tear_off="true">                  <menu_item_call                   label="Focus Tool" -                 layout="topleft"                   name="Focus"                   shortcut="control|1">                      <menu_item_call.on_click @@ -1785,7 +1502,6 @@                  </menu_item_call>                  <menu_item_call                   label="Move Tool" -                 layout="topleft"                   name="Move"                   shortcut="control|2">                      <menu_item_call.on_click @@ -1794,7 +1510,6 @@                  </menu_item_call>                  <menu_item_call                   label="Edit Tool" -                 layout="topleft"                   name="Edit"                   shortcut="control|3">                      <menu_item_call.on_click @@ -1803,7 +1518,6 @@                  </menu_item_call>                  <menu_item_call                   label="Create Tool" -                 layout="topleft"                   name="Create"                   shortcut="control|4">                      <menu_item_call.on_click @@ -1812,7 +1526,6 @@                  </menu_item_call>                  <menu_item_call                   label="Land Tool" -                 layout="topleft"                   name="Land"                   shortcut="control|5">                      <menu_item_call.on_click @@ -1820,11 +1533,9 @@                       parameter="land" />                  </menu_item_call>              </menu> -            <menu_item_separator -             layout="topleft" /> +            <menu_item_separator/>              <menu_item_call               label="Zoom In" -             layout="topleft"               name="Zoom In"               shortcut="control|0">                  <menu_item_call.on_click @@ -1832,7 +1543,6 @@              </menu_item_call>              <menu_item_call               label="Zoom Default" -             layout="topleft"               name="Zoom Default"               shortcut="control|9">                  <menu_item_call.on_click @@ -1840,17 +1550,14 @@              </menu_item_call>              <menu_item_call               label="Zoom Out" -             layout="topleft"               name="Zoom Out"               shortcut="control|8">                  <menu_item_call.on_click                   function="View.ZoomOut" />              </menu_item_call> -            <menu_item_separator -             layout="topleft" /> +            <menu_item_separator/>              <menu_item_call               label="Toggle Fullscreen" -             layout="topleft"               name="Toggle Fullscreen"               >                 <!-- Note: shortcut="alt|Enter" was deleted from the preceding node--> @@ -1858,11 +1565,9 @@                   function="View.Fullscreen" />              </menu_item_call>          </menu> -        <menu_item_separator -         layout="topleft" /> +        <menu_item_separator/>          <menu_item_call           label="Show Debug Settings" -         layout="topleft"           name="Debug Settings">              <menu_item_call.on_click               function="Advanced.ShowDebugSettings" @@ -1870,7 +1575,6 @@          </menu_item_call>       <menu_item_check           label="Show Develop Menu" -         layout="topleft"           name="Debug Mode"           shortcut="control|alt|Q">              <menu_item_check.on_check @@ -1880,23 +1584,21 @@               function="ToggleControl"               parameter="QAMode" />          </menu_item_check> +          </menu>      <menu       create_jump_keys="true"       label="Develop" -     layout="topleft"       name="Develop"       tear_off="true"       visible="false">          <menu           create_jump_keys="true"           label="Consoles" -         layout="topleft"           name="Consoles"           tear_off="true">              <menu_item_check               label="Texture Console" -             layout="topleft"               name="Texture Console"               shortcut="control|shift|3"               use_mac_ctrl="true"> @@ -1909,7 +1611,6 @@              </menu_item_check>              <menu_item_check               label="Debug Console" -             layout="topleft"               name="Debug Console"               shortcut="control|shift|4"               use_mac_ctrl="true"> @@ -1922,7 +1623,6 @@              </menu_item_check>              <menu_item_call               label="Notifications Console" -             layout="topleft"               name="Notifications"               shortcut="control|shift|5">                <menu_item_call.on_click @@ -1931,7 +1631,6 @@              </menu_item_call>              <menu_item_check                 label="Texture Size Console" -               layout="topleft"                 name="Texture Size"                 shortcut="control|shift|6">                <menu_item_check.on_check @@ -1943,7 +1642,6 @@              </menu_item_check>              <menu_item_check                 label="Texture Category Console" -               layout="topleft"                 name="Texture Category"                 shortcut="control|shift|7">                <menu_item_check.on_check @@ -1955,7 +1653,6 @@              </menu_item_check>              <menu_item_check               label="Fast Timers" -             layout="topleft"               name="Fast Timers"               shortcut="control|shift|9"               use_mac_ctrl="true"> @@ -1968,7 +1665,6 @@              </menu_item_check>              <menu_item_check               label="Memory" -             layout="topleft"               name="Memory"               shortcut="control|shift|0"               use_mac_ctrl="true"> @@ -1979,11 +1675,9 @@                   function="Advanced.ToggleConsole"                   parameter="memory view" />              </menu_item_check> -            <menu_item_separator -             layout="topleft" /> +            <menu_item_separator/>              <menu_item_call               label="Region Info to Debug Console" -             layout="topleft"               name="Region Info to Debug Console">                  <menu_item_call.on_click                   function="Advanced.DumpInfoToConsole" @@ -1991,7 +1685,6 @@              </menu_item_call>              <menu_item_call               label="Group Info to Debug Console" -             layout="topleft"               name="Group Info to Debug Console">                  <menu_item_call.on_click                   function="Advanced.DumpInfoToConsole" @@ -1999,17 +1692,14 @@              </menu_item_call>              <menu_item_call               label="Capabilities Info to Debug Console" -             layout="topleft"               name="Capabilities Info to Debug Console">                  <menu_item_call.on_click                   function="Advanced.DumpInfoToConsole"                   parameter="capabilities" />              </menu_item_call> -            <menu_item_separator -         layout="topleft" /> +            <menu_item_separator/>              <menu_item_check               label="Camera" -             layout="topleft"               name="Camera">                  <menu_item_check.on_check                   function="Advanced.CheckHUDInfo" @@ -2020,7 +1710,6 @@              </menu_item_check>              <menu_item_check               label="Wind" -             layout="topleft"               name="Wind">                  <menu_item_check.on_check                   function="Advanced.CheckHUDInfo" @@ -2031,7 +1720,6 @@              </menu_item_check>              <menu_item_check               label="FOV" -             layout="topleft"               name="FOV">                  <menu_item_check.on_check                   function="Advanced.CheckHUDInfo" @@ -2042,7 +1730,6 @@              </menu_item_check>              <menu_item_check               label="Badge" -             layout="topleft"               name="Badge"  			 shortcut="alt|control|shift|h">                  <menu_item_check.on_check @@ -2056,12 +1743,10 @@          <menu           create_jump_keys="true"           label="Show Info" -         layout="topleft"           name="Display Info"           tear_off="true">              <menu_item_check               label="Show Time" -             layout="topleft"               name="Show Time">                  <menu_item_check.on_check                   function="CheckControl" @@ -2072,7 +1757,6 @@              </menu_item_check>              <menu_item_check               label="Show Render Info" -             layout="topleft"               name="Show Render Info">                  <menu_item_check.on_check                   function="CheckControl" @@ -2083,7 +1767,6 @@              </menu_item_check>              <menu_item_check               label="Show Matrices" -             layout="topleft"               name="Show Matrices">                  <menu_item_check.on_check                   function="CheckControl" @@ -2094,7 +1777,6 @@              </menu_item_check>              <menu_item_check               label="Show Color Under Cursor" -             layout="topleft"               name="Show Color Under Cursor">                  <menu_item_check.on_check                   function="CheckControl" @@ -2103,11 +1785,9 @@                   function="ToggleControl"                   parameter="DebugShowColor" />              </menu_item_check> -            <menu_item_separator -             layout="topleft" /> +            <menu_item_separator/>              <menu_item_check               label="Show Updates to Objects" -             layout="topleft"               name="Show Updates"               shortcut="control|alt|shift|U">                  <menu_item_check.on_check @@ -2117,17 +1797,14 @@                   function="Advanced.ToggleShowObjectUpdates" />              </menu_item_check>          </menu> -        <menu_item_separator -         layout="topleft" /> +        <menu_item_separator/>          <menu           create_jump_keys="true"           label="Force an Error" -         layout="topleft"           name="Force Errors"           tear_off="true">              <menu_item_call               label="Force Breakpoint" -             layout="topleft"               name="Force Breakpoint"               shortcut="control|alt|shift|B">                  <menu_item_call.on_click @@ -2135,49 +1812,42 @@              </menu_item_call>              <menu_item_call               label="Force LLError And Crash" -             layout="topleft"               name="Force LLError And Crash">                  <menu_item_call.on_click                   function="Advanced.ForceErrorLlerror" />              </menu_item_call>              <menu_item_call               label="Force Bad Memory Access" -             layout="topleft"               name="Force Bad Memory Access">                  <menu_item_call.on_click                   function="Advanced.ForceErrorBadMemoryAccess" />              </menu_item_call>              <menu_item_call               label="Force Infinite Loop" -             layout="topleft"               name="Force Infinite Loop">                  <menu_item_call.on_click                   function="Advanced.ForceErrorInfiniteLoop" />              </menu_item_call>              <menu_item_call               label="Force Driver Crash" -             layout="topleft"               name="Force Driver Carsh">                  <menu_item_call.on_click                   function="Advanced.ForceErrorDriverCrash" />              </menu_item_call>              <menu_item_call               label="Force Software Exception" -             layout="topleft"               name="Force Software Exception">                  <menu_item_call.on_click                   function="Advanced.ForceErrorSoftwareException" />              </menu_item_call>              <menu_item_call               label="Force Disconnect Viewer" -             layout="topleft"               name="Force Disconnect Viewer">                  <menu_item_call.on_click                   function="Advanced.ForceErrorDisconnectViewer" />              </menu_item_call>              <menu_item_call               label="Simulate a Memory Leak" -             layout="topleft"               name="Memory Leaking Simulation">                 <menu_item_call.on_click                  function="Floater.Show" @@ -2187,12 +1857,10 @@          <menu           create_jump_keys="true"           label="Render Tests" -         layout="topleft"           name="Render Tests"           tear_off="true">              <menu_item_check               label="Camera Offset" -             layout="topleft"               name="Camera Offset">                  <menu_item_check.on_check                   function="CheckControl" @@ -2203,7 +1871,6 @@              </menu_item_check>              <menu_item_check               label="Randomize Framerate" -             layout="topleft"               name="Randomize Framerate">                  <menu_item_check.on_check                   function="Advanced.CheckRandomizeFramerate" @@ -2213,7 +1880,6 @@              </menu_item_check>              <menu_item_check               label="Periodic Slow Frame" -             layout="topleft"               name="Periodic Slow Frame">                  <menu_item_check.on_check                   function="Advanced.CheckPeriodicSlowFrame" @@ -2224,7 +1890,6 @@              </menu_item_check>              <menu_item_check               label="Frame Test" -             layout="topleft"               name="Frame Test">                  <menu_item_check.on_check                   function="Advanced.CheckFrameTest" @@ -2236,12 +1901,10 @@        <menu          create_jump_keys="true"          label="Render Metadata" -        layout="topleft"          name="Render Metadata"          tear_off="true">          <menu_item_check           label="Bounding Boxes" -         layout="topleft"           name="Bounding Boxes">          <menu_item_check.on_check           function="Advanced.CheckInfoDisplay" @@ -2252,7 +1915,6 @@          </menu_item_check>          <menu_item_check           label="Octree" -         layout="topleft"           name="Octree">            <menu_item_check.on_check             function="Advanced.CheckInfoDisplay" @@ -2263,7 +1925,6 @@          </menu_item_check>          <menu_item_check           label="Shadow Frusta" -         layout="topleft"           name="Shadow Frusta">            <menu_item_check.on_check             function="Advanced.CheckInfoDisplay" @@ -2274,7 +1935,6 @@          </menu_item_check>          <menu_item_check           label="Occlusion" -         layout="topleft"           name="Occlusion">            <menu_item_check.on_check             function="Advanced.CheckInfoDisplay" @@ -2285,7 +1945,6 @@          </menu_item_check>          <menu_item_check           label="Render Batches" -         layout="topleft"           name="Render Batches">            <menu_item_check.on_check             function="Advanced.CheckInfoDisplay" @@ -2296,7 +1955,6 @@          </menu_item_check>          <menu_item_check           label="Texture Anim" -         layout="topleft"           name="Texture Anim">            <menu_item_check.on_check             function="Advanced.CheckInfoDisplay" @@ -2307,7 +1965,6 @@          </menu_item_check>          <menu_item_check           label="Texture Priority" -         layout="topleft"           name="Texture Priority">            <menu_item_check.on_check             function="Advanced.CheckInfoDisplay" @@ -2318,7 +1975,6 @@          </menu_item_check>          <menu_item_check           label="Texture Area" -         layout="topleft"           name="Texture Area">            <menu_item_check.on_check             function="Advanced.CheckInfoDisplay" @@ -2329,7 +1985,6 @@          </menu_item_check>          <menu_item_check           label="Face Area" -         layout="topleft"           name="Face Area">            <menu_item_check.on_check             function="Advanced.CheckInfoDisplay" @@ -2340,7 +1995,6 @@          </menu_item_check>          <menu_item_check           label="Lights" -         layout="topleft"           name="Lights">            <menu_item_check.on_check             function="Advanced.CheckInfoDisplay" @@ -2351,7 +2005,6 @@          </menu_item_check>          <menu_item_check           label="Collision Skeleton" -         layout="topleft"           name="Collision Skeleton">            <menu_item_check.on_check             function="Advanced.CheckInfoDisplay" @@ -2362,7 +2015,6 @@          </menu_item_check>          <menu_item_check           label="Raycast" -         layout="topleft"           name="Raycast">            <menu_item_check.on_check             function="Advanced.CheckInfoDisplay" @@ -2375,7 +2027,6 @@          <menu           create_jump_keys="true"           label="Rendering" -         layout="topleft"           name="Rendering"           tear_off="true">              <menu_item_check @@ -2517,7 +2168,6 @@              </menu_item_check>              <menu_item_check                label="Full Res Textures" -             layout="topleft"               name="Rull Res Textures">                  <menu_item_check.on_check                   function="CheckControl" @@ -2528,7 +2178,6 @@              </menu_item_check>              <menu_item_check                 label="Audit Textures" -               layout="topleft"                 name="Audit Textures">                <menu_item_check.on_check                 function="CheckControl" @@ -2582,12 +2231,10 @@          <menu           create_jump_keys="true"           label="Network" -         layout="topleft"           name="Network"           tear_off="true">              <menu_item_check               label="Pause Agent" -             layout="topleft"               name="AgentPause">                  <menu_item_check.on_check                   function="CheckControl" @@ -2596,27 +2243,22 @@                   function="ToggleControl"                   parameter="AgentPause" />              </menu_item_check> -            <menu_item_separator -             layout="topleft" /> +            <menu_item_separator/>              <menu_item_call               label="Enable Message Log" -             layout="topleft"               name="Enable Message Log">                  <menu_item_call.on_click                   function="Advanced.EnableMessageLog" />              </menu_item_call>              <menu_item_call               label="Disable Message Log" -             layout="topleft"               name="Disable Message Log">                  <menu_item_call.on_click                   function="Advanced.DisableMessageLog" />              </menu_item_call> -            <menu_item_separator -             layout="topleft" /> +            <menu_item_separator/>              <menu_item_check               label="Velocity Interpolate Objects" -             layout="topleft"               name="Velocity Interpolate Objects">                  <menu_item_check.on_check                   function="CheckControl" @@ -2627,7 +2269,6 @@              </menu_item_check>              <menu_item_check               label="Ping Interpolate Object Positions" -             layout="topleft"               name="Ping Interpolate Object Positions">                  <menu_item_check.on_check                   function="CheckControl" @@ -2636,11 +2277,9 @@                   function="ToggleControl"                   parameter="PingInterpolate" />              </menu_item_check> -            <menu_item_separator -             layout="topleft" /> +            <menu_item_separator/>              <menu_item_call               label="Drop a Packet" -             layout="topleft"               name="Drop a Packet"               shortcut="control|alt|L">                  <menu_item_call.on_click @@ -2649,14 +2288,12 @@          </menu>          <menu_item_call           label="Dump Scripted Camera" -         layout="topleft"           name="Dump Scripted Camera">              <menu_item_call.on_click               function="Advanced.DumpScriptedCamera" />          </menu_item_call>          <menu_item_call               label="Bumps, Pushes & Hits" -             layout="topleft"               name="Bumps, Pushes &amp; Hits">                  <menu_item_call.on_click                   function="Floater.Show" @@ -2666,12 +2303,10 @@          <menu           create_jump_keys="true"           label="Recorder" -         layout="topleft"           name="Recorder"           tear_off="true">              <menu_item_call               label="Start Playback" -             layout="topleft"               name="Start Playback">                  <menu_item_call.on_click                   function="Advanced.AgentPilot" @@ -2679,7 +2314,6 @@              </menu_item_call>              <menu_item_call               label="Stop Playback" -             layout="topleft"               name="Stop Playback">                  <menu_item_call.on_click                   function="Advanced.AgentPilot" @@ -2687,7 +2321,6 @@              </menu_item_call>              <menu_item_check               label="Loop Playback" -             layout="topleft"               name="Loop Playback">                  <menu_item_check.on_check                   function="Advanced.CheckAgentPilotLoop" @@ -2697,7 +2330,6 @@              </menu_item_check>              <menu_item_call               label="Start Record" -             layout="topleft"               name="Start Record">                  <menu_item_call.on_click                   function="Advanced.AgentPilot" @@ -2705,7 +2337,6 @@              </menu_item_call>              <menu_item_call               label="Stop Record" -             layout="topleft"               name="Stop Record">                  <menu_item_call.on_click                   function="Advanced.AgentPilot" @@ -2716,12 +2347,10 @@          <menu           create_jump_keys="true"           label="World" -         layout="topleft"           name="World"           tear_off="true">              <menu_item_check               label="Sim Sun Override" -             layout="topleft"               name="Sim Sun Override">                  <menu_item_check.on_check                   function="CheckControl" @@ -2732,7 +2361,6 @@              </menu_item_check>              <menu_item_check               label="Cheesy Beacon" -             layout="topleft"               name="Cheesy Beacon">                  <menu_item_check.on_check                   function="CheckControl" @@ -2743,7 +2371,6 @@              </menu_item_check>              <menu_item_check               label="Fixed Weather" -             layout="topleft"               name="Fixed Weather">                  <menu_item_check.on_check                   function="CheckControl" @@ -2754,7 +2381,6 @@              </menu_item_check>              <menu_item_call               label="Dump Region Object Cache" -             layout="topleft"               name="Dump Region Object Cache">                  <menu_item_call.on_click                   function="Advanced.DumpRegionObjectCache" /> @@ -2763,12 +2389,10 @@          <menu           create_jump_keys="true"           label="UI" -         layout="topleft"           name="UI"           tear_off="true">           <!--   <menu_item_check               label="New Bottom Bar" -             layout="topleft"               name="New Bottom Bar">                  <menu_item_check.on_check                   function="CheckControl" @@ -2779,7 +2403,6 @@              </menu_item_check>-->              <menu_item_call               label="Web Browser Test" -             layout="topleft"               name="Web Browser Test">                  <menu_item_call.on_click                   function="Advanced.WebBrowserTest" @@ -2787,14 +2410,12 @@              </menu_item_call>              <menu_item_call               label="Dump SelectMgr" -             layout="topleft"               name="Dump SelectMgr">                  <menu_item_call.on_click                   function="Advanced.DumpSelectMgr" />              </menu_item_call>              <menu_item_call               label="Dump Inventory" -             layout="topleft"               name="Dump Inventory">                  <menu_item_call.on_click                   function="Advanced.DumpInventory" /> @@ -2807,14 +2428,12 @@              </menu_item_call>              <menu_item_call               label="Dump Focus Holder" -             layout="topleft"               name="Dump Focus Holder">                  <menu_item_call.on_click                   function="Advanced.DumpFocusHolder" />              </menu_item_call>              <menu_item_call               label="Print Selected Object Info" -             layout="topleft"               name="Print Selected Object Info"               shortcut="control|shift|P">                  <menu_item_call.on_click @@ -2822,7 +2441,6 @@              </menu_item_call>              <menu_item_call               label="Print Agent Info" -             layout="topleft"               name="Print Agent Info"               shortcut="shift|P">                  <menu_item_call.on_click @@ -2830,7 +2448,6 @@              </menu_item_call>              <menu_item_call               label="Memory Stats" -             layout="topleft"               name="Memory Stats"               shortcut="control|alt|shift|M">                  <menu_item_call.on_click @@ -2838,7 +2455,6 @@              </menu_item_call>              <menu_item_check               label="Double-ClickAuto-Pilot" -             layout="topleft"               name="Double-ClickAuto-Pilot">                  <menu_item_check.on_check                   function="CheckControl" @@ -2851,7 +2467,6 @@              <menu_item_separator />              <menu_item_check               label="Debug SelectMgr" -             layout="topleft"               name="Debug SelectMgr">                  <menu_item_check.on_check                   function="CheckControl" @@ -2862,7 +2477,6 @@              </menu_item_check>              <menu_item_check               label="Debug Clicks" -             layout="topleft"               name="Debug Clicks">                  <menu_item_check.on_check                   function="Advanced.CheckDebugClicks" @@ -2873,7 +2487,6 @@              </menu_item_check>              <menu_item_check               label="Debug Views" -             layout="topleft"               name="Debug Views">                  <menu_item_check.on_check                   function="Advanced.CheckDebugViews" /> @@ -2882,7 +2495,6 @@              </menu_item_check>              <menu_item_check               label="Debug Name Tooltips" -             layout="topleft"               name="Debug Name Tooltips">                  <menu_item_check.on_check                   function="Advanced.CheckXUINameTooltips" @@ -2892,7 +2504,6 @@              </menu_item_check>              <menu_item_check               label="Debug Mouse Events" -             layout="topleft"               name="Debug Mouse Events">                  <menu_item_check.on_check                   function="Advanced.CheckDebugMouseEvents" @@ -2902,7 +2513,6 @@              </menu_item_check>              <menu_item_check               label="Debug Keys" -             layout="topleft"               name="Debug Keys">                  <menu_item_check.on_check                   function="Advanced.CheckDebugKeys" @@ -2912,7 +2522,6 @@              </menu_item_check>              <menu_item_check               label="Debug WindowProc" -             layout="topleft"               name="Debug WindowProc">                  <menu_item_check.on_check                   function="Advanced.CheckDebugWindowProc" @@ -2929,14 +2538,12 @@           tear_off="true">              <menu_item_call                 label="Reload Color Settings" -               layout="topleft"                 name="Reload Color Settings">                <menu_item_call.on_click                 function="Advanced.ReloadColorSettings" />              </menu_item_call>              <menu_item_call               label="Show Font Test" -             layout="topleft"               name="Show Font Test">                  <menu_item_call.on_click                   function="Floater.Show" @@ -2944,21 +2551,18 @@              </menu_item_call>              <menu_item_call               label="Load from XML" -             layout="topleft"               name="Load from XML">                  <menu_item_call.on_click                   function="Advanced.LoadUIFromXML" />              </menu_item_call>              <menu_item_call               label="Save to XML" -             layout="topleft"               name="Save to XML">                  <menu_item_call.on_click                   function="Advanced.SaveUIToXML" />              </menu_item_call>              <menu_item_check               label="Show XUI Names" -             layout="topleft"               name="Show XUI Names">                  <menu_item_check.on_check                   function="Advanced.CheckXUINames" @@ -2968,7 +2572,6 @@              </menu_item_check>            <menu_item_call             label="Send Test IMs" -           layout="topleft"             name="Send Test IMs">              <menu_item_call.on_click               function="Advanced.SendTestIMs" /> @@ -2977,18 +2580,15 @@          <menu           create_jump_keys="true"           label="Avatar" -         layout="topleft"           name="Character"           tear_off="true">              <menu               create_jump_keys="true"               label="Grab Baked Texture" -             layout="topleft"               name="Grab Baked Texture"               tear_off="true">                  <menu_item_call                   label="Iris" -                 layout="topleft"                   name="Iris">                      <menu_item_call.on_click                       function="Advanced.GrabBakedTexture" @@ -2999,7 +2599,6 @@                  </menu_item_call>                  <menu_item_call                   label="Head" -                 layout="topleft"                   name="Head">                      <menu_item_call.on_click                       function="Advanced.GrabBakedTexture" @@ -3010,7 +2609,6 @@                  </menu_item_call>                  <menu_item_call                   label="Upper Body" -                 layout="topleft"                   name="Upper Body">                      <menu_item_call.on_click                       function="Advanced.GrabBakedTexture" @@ -3021,7 +2619,6 @@                  </menu_item_call>                  <menu_item_call                   label="Lower Body" -                 layout="topleft"                   name="Lower Body">                      <menu_item_call.on_click                       function="Advanced.GrabBakedTexture" @@ -3032,7 +2629,6 @@                  </menu_item_call>                  <menu_item_call                   label="Skirt" -                 layout="topleft"                   name="Skirt">                      <menu_item_call.on_click                       function="Advanced.GrabBakedTexture" @@ -3045,19 +2641,16 @@              <menu               create_jump_keys="true"               label="Character Tests" -             layout="topleft"               name="Character Tests"               tear_off="true">                  <menu_item_call                   label="Appearance To XML" -                 layout="topleft"                   name="Appearance To XML">                      <menu_item_call.on_click                       function="Advanced.AppearanceToXML" />                  </menu_item_call>                  <menu_item_call                   label="Toggle Character Geometry" -                 layout="topleft"                   name="Toggle Character Geometry">                      <menu_item_call.on_click                       function="Advanced.ToggleCharacterGeometry" /> @@ -3066,28 +2659,24 @@                  </menu_item_call>                  <menu_item_call                   label="Test Male" -                 layout="topleft"                   name="Test Male">                      <menu_item_call.on_click                       function="Advanced.TestMale" />                  </menu_item_call>                  <menu_item_call                   label="Test Female" -                 layout="topleft"                   name="Test Female">                      <menu_item_call.on_click                       function="Advanced.TestFemale" />                  </menu_item_call>                  <menu_item_call                   label="Toggle PG" -                 layout="topleft"                   name="Toggle PG">                      <menu_item_call.on_click                       function="Advanced.TogglePG" />                  </menu_item_call>                  <menu_item_check                   label="Allow Select Avatar" -                 layout="topleft"                   name="Allow Select Avatar">                      <menu_item_check.on_check                       function="CheckControl" @@ -3099,14 +2688,12 @@              </menu>              <menu_item_call               label="Force Params to Default" -             layout="topleft"               name="Force Params to Default">                  <menu_item_call.on_click                   function="Advanced.ForceParamsToDefault" />              </menu_item_call>              <menu_item_check               label="Animation Info" -             layout="topleft"               name="Animation Info">                  <menu_item_check.on_check                   function="Advanced.CheckAnimationInfo" @@ -3117,7 +2704,6 @@              </menu_item_check>              <menu_item_check               label="Slow Motion Animations" -             layout="topleft"               name="Slow Motion Animations">                  <menu_item_check.on_check                   function="CheckControl" @@ -3128,7 +2714,6 @@              </menu_item_check>              <menu_item_check               label="Show Look At" -             layout="topleft"               name="Show Look At">                  <menu_item_check.on_check                   function="Advanced.CheckShowLookAt" @@ -3138,7 +2723,6 @@              </menu_item_check>              <menu_item_check               label="Show Point At" -             layout="topleft"               name="Show Point At">                  <menu_item_check.on_check                   function="Advanced.CheckShowPointAt" @@ -3148,7 +2732,6 @@              </menu_item_check>              <menu_item_check               label="Debug Joint Updates" -             layout="topleft"               name="Debug Joint Updates">                  <menu_item_check.on_check                   function="Advanced.CheckDebugJointUpdates" @@ -3158,7 +2741,6 @@              </menu_item_check>              <menu_item_check               label="Disable LOD" -             layout="topleft"               name="Disable LOD">                  <menu_item_check.on_check                   function="Advanced.CheckDisableLOD" @@ -3168,7 +2750,6 @@              </menu_item_check>              <menu_item_check               label="Debug Character Vis" -             layout="topleft"               name="Debug Character Vis">                  <menu_item_check.on_check                   function="Advanced.CheckDebugCharacterVis" @@ -3178,7 +2759,6 @@              </menu_item_check>              <menu_item_check               label="Show Collision Skeleton" -             layout="topleft"               name="Show Collision Skeleton">                  <menu_item_check.on_check                   function="Advanced.CheckInfoDisplay" @@ -3189,7 +2769,6 @@              </menu_item_check>              <menu_item_check               label="Display Agent Target" -             layout="topleft"               name="Display Agent Target">                  <menu_item_check.on_check                   function="Advanced.CheckInfoDisplay" @@ -3201,7 +2780,6 @@  <!-- Appears not to exist anymore              <menu_item_check               label="Debug Rotation" -             layout="topleft"               name="Debug Rotation">                  <menu_item_check.on_check                   function="CheckControl" @@ -3213,14 +2791,12 @@  -->              <menu_item_call               label="Dump Attachments" -             layout="topleft"               name="Dump Attachments">                  <menu_item_call.on_click                   function="Advanced.DumpAttachments" />              </menu_item_call>              <menu_item_call               label="Debug Avatar Textures" -             layout="topleft"               name="Debug Avatar Textures"               shortcut="control|alt|shift|A">                  <menu_item_call.on_click @@ -3228,18 +2804,15 @@              </menu_item_call>              <menu_item_call               label="Dump Local Textures" -             layout="topleft"               name="Dump Local Textures"               shortcut="alt|shift|M">                  <menu_item_call.on_click                   function="Advanced.DumpAvatarLocalTextures" />              </menu_item_call>          </menu> -        <menu_item_separator -         layout="topleft" /> +        <menu_item_separator/>          <menu_item_check           label="HTTP Textures" -         layout="topleft"           name="HTTP Textures">              <menu_item_check.on_check               function="CheckControl" @@ -3250,14 +2823,12 @@          </menu_item_check>          <menu_item_call           label="Compress Images" -         layout="topleft"           name="Compress Images">              <menu_item_call.on_click               function="Advanced.CompressImage" />          </menu_item_call>          <menu_item_check           label="Output Debug Minidump" -         layout="topleft"           name="Output Debug Minidump">              <menu_item_check.on_check               function="CheckControl" @@ -3268,7 +2839,6 @@          </menu_item_check>          <menu_item_check           label="Console Window on next Run" -         layout="topleft"           name="Console Window">              <menu_item_check.on_check               function="CheckControl" @@ -3277,11 +2847,9 @@               function="ToggleControl"               parameter="ShowConsoleWindow" />          </menu_item_check> -        <menu_item_separator -         layout="topleft" /> +        <menu_item_separator/>          <menu_item_check           label="Show Admin Menu" -         layout="topleft"           name="View Admin Options">              <menu_item_check.on_check               function="Advanced.CheckViewAdminOptions" @@ -3291,7 +2859,6 @@          </menu_item_check>          <menu_item_call           label="Request Admin Status" -         layout="topleft"           name="Request Admin Options"           shortcut="control|alt|G">              <menu_item_call.on_click @@ -3299,7 +2866,6 @@          </menu_item_call>          <menu_item_call           label="Leave Admin Status" -         layout="topleft"           name="Leave Admin Options"           shortcut="control|alt|shift|G">              <menu_item_call.on_click @@ -3309,18 +2875,15 @@      <menu       create_jump_keys="true"       label="Admin" -     layout="topleft"       name="Admin"       tear_off="true"       visible="false">          <menu           create_jump_keys="true"           label="Object" -         layout="topleft"           tear_off="true">              <menu_item_call               label="Take Copy" -             layout="topleft"               name="Take Copy"               shortcut="control|alt|shift|O">                  <menu_item_call.on_click @@ -3330,7 +2893,6 @@              </menu_item_call>              <menu_item_call               label="Force Owner To Me" -             layout="topleft"               name="Force Owner To Me">                  <menu_item_call.on_click                   function="Admin.HandleObjectOwnerSelf" /> @@ -3339,7 +2901,6 @@              </menu_item_call>              <menu_item_call               label="Force Owner Permissive" -             layout="topleft"               name="Force Owner Permissive">                  <menu_item_call.on_click                   function="Admin.HandleObjectOwnerPermissive" /> @@ -3348,7 +2909,6 @@              </menu_item_call>              <menu_item_call               label="Delete" -             layout="topleft"               name="Delete"               shortcut="control|alt|shift|Del">                  <menu_item_call.on_click @@ -3358,7 +2918,6 @@              </menu_item_call>              <menu_item_call               label="Lock" -             layout="topleft"               name="Lock"               shortcut="control|alt|shift|L">                  <menu_item_call.on_click @@ -3368,7 +2927,6 @@              </menu_item_call>              <menu_item_call               label="Get Assets IDs" -             layout="topleft"               name="Get Assets IDs"               shortcut="control|alt|shift|I">                  <menu_item_call.on_click @@ -3380,12 +2938,10 @@          <menu           create_jump_keys="true"           label="Parcel" -         layout="topleft"           name="Parcel"           tear_off="true">              <menu_item_call               label="Force Owner To Me" -             layout="topleft"               name="Owner To Me">                  <menu_item_call.on_click                   function="Admin.HandleForceParcelOwnerToMe" /> @@ -3394,7 +2950,6 @@              </menu_item_call>              <menu_item_call               label="Set to Linden Content" -             layout="topleft"               name="Set to Linden Content"               shortcut="control|alt|shift|C">                  <menu_item_call.on_click @@ -3404,7 +2959,6 @@              </menu_item_call>              <menu_item_call               label="Claim Public Land" -             layout="topleft"               name="Claim Public Land">                  <menu_item_call.on_click                   function="Admin.HandleClaimPublicLand" /> @@ -3415,12 +2969,10 @@          <menu           create_jump_keys="true"           label="Region" -         layout="topleft"           name="Region"           tear_off="true">              <menu_item_call               label="Dump Temp Asset Data" -             layout="topleft"               name="Dump Temp Asset Data">                  <menu_item_call.on_click                   function="Admin.HandleRegionDumpTempAssetData" /> @@ -3429,7 +2981,6 @@              </menu_item_call>              <menu_item_call               label="Save Region State" -             layout="topleft"               name="Save Region State">                  <menu_item_call.on_click                   function="Admin.OnSaveState" /> @@ -3439,7 +2990,6 @@          </menu>          <menu_item_call           label="God Tools" -         layout="topleft"           name="God Tools">              <menu_item_call.on_click               function="Floater.Show" @@ -3451,34 +3001,29 @@      <menu       create_jump_keys="true"       label="Admin" -     layout="topleft"       name="Deprecated"       tear_off="true"       visible="false">          <menu           create_jump_keys="true"           label="Attach Object" -         layout="topleft"           mouse_opaque="false"           name="Attach Object"           tear_off="true" />          <menu           create_jump_keys="true"           label="Detach Object" -         layout="topleft"           mouse_opaque="false"           name="Detach Object"           tear_off="true" />          <menu           create_jump_keys="true"           label="Take Off Clothing" -         layout="topleft"           mouse_opaque="false"           name="Take Off Clothing"           tear_off="true">              <menu_item_call               label="Shirt" -             layout="topleft"               name="Shirt">                  <menu_item_call.on_click                   function="Edit.TakeOff" @@ -3489,7 +3034,6 @@              </menu_item_call>              <menu_item_call               label="Pants" -             layout="topleft"               name="Pants">                  <menu_item_call.on_click                   function="Edit.TakeOff" @@ -3500,7 +3044,6 @@              </menu_item_call>              <menu_item_call               label="Shoes" -             layout="topleft"               name="Shoes">                  <menu_item_call.on_click                   function="Edit.TakeOff" @@ -3511,7 +3054,6 @@              </menu_item_call>              <menu_item_call               label="Socks" -             layout="topleft"               name="Socks">                  <menu_item_call.on_click                   function="Edit.TakeOff" @@ -3522,7 +3064,6 @@              </menu_item_call>              <menu_item_call               label="Jacket" -             layout="topleft"               name="Jacket">                  <menu_item_call.on_click                   function="Edit.TakeOff" @@ -3533,7 +3074,6 @@              </menu_item_call>              <menu_item_call               label="Gloves" -             layout="topleft"               name="Gloves">                  <menu_item_call.on_click                   function="Edit.TakeOff" @@ -3544,7 +3084,6 @@              </menu_item_call>              <menu_item_call               label="Undershirt" -             layout="topleft"               name="Menu Undershirt">                  <menu_item_call.on_click                   function="Edit.TakeOff" @@ -3555,7 +3094,6 @@              </menu_item_call>              <menu_item_call               label="Underpants" -             layout="topleft"               name="Menu Underpants">                  <menu_item_call.on_click                   function="Edit.TakeOff" @@ -3566,7 +3104,6 @@              </menu_item_call>              <menu_item_call               label="Skirt" -             layout="topleft"               name="Skirt">                  <menu_item_call.on_click                   function="Edit.TakeOff" @@ -3577,7 +3114,6 @@              </menu_item_call>              <menu_item_call               label="Alpha" -             layout="topleft"               name="Alpha">                  <menu_item_call.on_click                   function="Edit.TakeOff" @@ -3588,7 +3124,6 @@              </menu_item_call>              <menu_item_call               label="Tattoo" -             layout="topleft"               name="Tattoo">                  <menu_item_call.on_click                   function="Edit.TakeOff" @@ -3599,7 +3134,6 @@              </menu_item_call>              <menu_item_call               label="All Clothes" -             layout="topleft"               name="All Clothes">                  <menu_item_call.on_click                   function="Edit.TakeOff" @@ -3609,12 +3143,10 @@          <menu           create_jump_keys="true"           label="Help" -         layout="topleft"           name="Help"           tear_off="true">              <menu_item_call               label="Official Linden Blog" -             layout="topleft"               name="Official Linden Blog">                  <menu_item_call.on_click                   function="PromptShowURL" @@ -3623,7 +3155,6 @@              </menu_item_call>              <menu_item_call               label="Scripting Portal" -             layout="topleft"               name="Scripting Portal">                  <menu_item_call.on_click                   function="PromptShowURL" @@ -3633,12 +3164,10 @@              <menu               create_jump_keys="true"               label="Bug Reporting" -             layout="topleft"               name="Bug Reporting"               tear_off="true">                  <menu_item_call                   label="Public Issue Tracker" -                 layout="topleft"                   name="Public Issue Tracker">                      <menu_item_call.on_click                       function="PromptShowURL" @@ -3647,18 +3176,15 @@                  </menu_item_call>                  <menu_item_call                   label="Public Issue Tracker Help" -                 layout="topleft"                   name="Publc Issue Tracker Help">                      <menu_item_call.on_click                       function="PromptShowURL"                       name="PublicIssueTrackerHelp_url"                       parameter="WebLaunchPublicIssueHelp,http://wiki.secondlife.com/wiki/Issue_tracker" />                  </menu_item_call> -                <menu_item_separator -                 layout="topleft" /> +                <menu_item_separator/>                  <menu_item_call                   label="Bug Reporting 101" -                 layout="topleft"                   name="Bug Reporing 101">                      <menu_item_call.on_click                       function="PromptShowURL" @@ -3667,7 +3193,6 @@                  </menu_item_call>                  <menu_item_call                   label="Security Issues" -                 layout="topleft"                   name="Security Issues">                      <menu_item_call.on_click                       function="PromptShowURL" @@ -3676,7 +3201,6 @@                  </menu_item_call>                  <menu_item_call                   label="QA Wiki" -                 layout="topleft"                   name="QA Wiki">                      <menu_item_call.on_click                       function="PromptShowURL" diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 3d41989e54..c42b846dbb 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -1384,6 +1384,18 @@ Unable to encode file: [FILE]    <notification     icon="alertmodal.tga" +   name="CorruptedProtectedDataStore" +   type="alertmodal"> +  We are unable to read your protected data so it is being reset. +   This may happen when you change network setup. + +    <usetemplate +     name="okbutton" +     yestext="OK"/> +  </notification> +     +  <notification +   icon="alertmodal.tga"     name="CorruptResourceFile"     type="alertmodal">  Corrupt resource file: [FILE] @@ -2024,6 +2036,28 @@ Would you be my friend?    </notification>    <notification + icon="alertmodal.tga" + label="Save Outfit" + name="SaveOutfitAs" + type="alertmodal"> +    Save what I'm wearing as a new Outfit: +    <form name="form"> +      <input name="message" type="text"> +        [DESC] (new) +      </input> +      <button +       default="true" +       index="0" +       name="Offer" +       text="OK"/> +      <button +       index="1" +       name="Cancel" +       text="Cancel"/> +    </form> +  </notification> + +  <notification     icon="alertmodal.tga"     name="RemoveFromFriends"     type="alertmodal"> @@ -2395,6 +2429,57 @@ Please choose the male or female avatar. You can change your mind later.       notext="Female"       yestext="Male"/>    </notification> +  <notification icon="alertmodal.tga" +		name="CantTeleportToGrid" +		type="alertmodal"> +Could not teleport to [SLURL] as it's on a different grid ([GRID]) than the current grid ([CURRENT_GRID]).  Please close your viewer and try again. +    <usetemplate +     name="okbutton" +     yestext="OK"/> +  </notification> + +  <notification icon="alertmodal.tga" +		name="GeneralCertificateError" +		type="alertmodal"> +Could not connect to the server. +[REASON] + +SubjectName: [SUBJECT_NAME_STRING] +IssuerName: [ISSUER_NAME_STRING] +Valid From: [VALID_FROM] +Valid To: [VALID_TO] +MD5 Fingerprint: [SHA1_DIGEST] +SHA1 Fingerprint: [MD5_DIGEST] +Key Usage: [KEYUSAGE] +Extended Key Usage: [EXTENDEDKEYUSAGE] +Subject Key Identifier: [SUBJECTKEYIDENTIFIER] +    <usetemplate +     name="okbutton" +     yestext="OK"/> +   </notification> + +  <notification icon="alertmodal.tga" +		name="TrustCertificateError" +		type="alertmodal"> +The certification authority for this server is not known. + +Certificate Information: +SubjectName: [SUBJECT_NAME_STRING] +IssuerName: [ISSUER_NAME_STRING] +Valid From: [VALID_FROM] +Valid To: [VALID_TO] +MD5 Fingerprint: [SHA1_DIGEST] +SHA1 Fingerprint: [MD5_DIGEST] +Key Usage: [KEYUSAGE] +Extended Key Usage: [EXTENDEDKEYUSAGE] +Subject Key Identifier: [SUBJECTKEYIDENTIFIER] + +Would you like to trust this authority? +    <usetemplate +     name="okcancelbuttons" +     notext="Cancel" +     yestext="Trust"/> +  </notification>    <notification     icon="alertmodal.tga" diff --git a/indra/newview/skins/default/xui/en/panel_login.xml b/indra/newview/skins/default/xui/en/panel_login.xml index cc68ec2bdc..539c5f785c 100644 --- a/indra/newview/skins/default/xui/en/panel_login.xml +++ b/indra/newview/skins/default/xui/en/panel_login.xml @@ -48,52 +48,31 @@ auto_resize="false"  follows="left|bottom"  name="login"  layout="topleft" -width="695" -min_width="695" +width="850" +min_width="850"  user_resize="false"  height="80">  <text  follows="left|bottom"  font="SansSerifSmall"  height="16" -name="first_name_text" +name="username_text"  top="20"  left="20"  width="150"> -First name: +Username:  </text>  <line_editor  follows="left|bottom" -handle_edit_keys_directly="true"  height="22" -label="First" +label="Username"  left_delta="0"  max_length="31" -name="first_name_edit" +name="username_edit"  select_on_focus="true" -tool_tip="[SECOND_LIFE] First Name" +tool_tip="[SECOND_LIFE] Username"  top_pad="0" -   width="135" /> -  <text -   follows="left|bottom" -   font="SansSerifSmall" -   height="16" -   left_pad="8" -   name="last_name_text" -   top="20" -   width="150"> -    Last name:   </text> -<line_editor -follows="left|bottom" -handle_edit_keys_directly="true" -height="22" -label="Last" -max_length="31" -name="last_name_edit" -select_on_focus="true" -tool_tip="[SECOND_LIFE] Last Name" -  top_pad="0" -  width="135" /> +width="150" />  <text  follows="left|bottom"  font="SansSerifSmall" @@ -101,12 +80,11 @@ height="15"  left_pad="8"  name="password_text"  top="20" -    width="150"> +    width="135">         Password:  </text>  <line_editor  follows="left|bottom" -handle_edit_keys_directly="true"    height="22"    max_length="16"  name="password_edit" @@ -122,26 +100,14 @@ label="Remember password"    top_pad="3"    name="remember_check"   width="135" /> -<button -  follows="left|bottom" -  height="23" -  image_unselected="PushButton_On" -  image_selected="PushButton_On_Selected" -  label="Log In" -  label_color="White" -  layout="topleft" -  left_pad="10" -  name="connect_btn" -  top="35" -  width="90" />    <text    follows="left|bottom"    font="SansSerifSmall"    height="15" -  left_pad="18" +  left_pad="10"    name="start_location_text"  top="20" -  width="130"> +  width="250">         Start at:   </text>  <combo_box @@ -152,7 +118,7 @@ control_name="LoginLocation"  max_chars="128"  top_pad="0"  name="start_location_combo" -     width="135"> +     width="250">  <combo_box.item  label="My last location"  name="MyLastLocation" @@ -165,16 +131,37 @@ name="MyHome"  label="<Type region name>"  name="Typeregionname"   value="" />  </combo_box> +<button +  height="23" +  image_unselected="PushButton_On" +  image_selected="PushButton_On_Selected" +  label="Log In" +  label_color="White" +  layout="topleft" +  left_pad="10" +  name="connect_btn" +  top="35" +  width="90" /> +  <text +  follows="left|bottom" +  font="SansSerifSmall" +  height="15" +  left_pad="10" +  name="start_location_text" +top="20" +  width="150"> +       Grid Name: + </text>  <combo_box +follows="left|bottom"  allow_text_entry="true"  font="SansSerifSmall" -   follows="left|right|bottom" -   height="23" -layout="topleft" -top_pad="2" +height="23"  name="server_combo" -width="135" -  visible="false" /> +top_pad="0" +width="150" +max_chars="255" +visible="false" />  </layout_panel>  <layout_panel  follows="right|bottom" diff --git a/indra/newview/skins/default/xui/en/panel_main_inventory.xml b/indra/newview/skins/default/xui/en/panel_main_inventory.xml index 6c81107dde..1b04d01abf 100644 --- a/indra/newview/skins/default/xui/en/panel_main_inventory.xml +++ b/indra/newview/skins/default/xui/en/panel_main_inventory.xml @@ -9,22 +9,22 @@   min_width="240"   name="main inventory panel"   width="330"> -    <panel.string -     name="Itemcount"> -    </panel.string> -    <panel.string -     name="ItemcountFetching"> -        Fetching [ITEM_COUNT] Items... [FILTER] -    </panel.string> -    <panel.string -     name="ItemcountCompleted"> -        [ITEM_COUNT] Items [FILTER] -    </panel.string> -    <panel.string -     name="ItemcountUnknown"> +  <panel.string +   name="Itemcount"> +  </panel.string> +  <panel.string +   name="ItemcountFetching"> +    Fetching [ITEM_COUNT] Items... [FILTER] +  </panel.string> +  <panel.string +   name="ItemcountCompleted"> +    [ITEM_COUNT] Items [FILTER] +  </panel.string> +  <panel.string +   name="ItemcountUnknown"> -    </panel.string> -	<text +  </panel.string> +  <text  		     type="string"  		     length="1"  		     follows="left|top" @@ -36,487 +36,124 @@  		     text_color="EmphasisColor"  		     top_pad="0"  		     width="300"> -	        Items: -    </text> -    <menu_bar -     bg_visible="false" -     follows="left|top|right" -     height="20" +    Items: +  </text> +  <filter_editor +   text_pad_left="10" +   follows="left|top|right" + height="23" +   label="Filter Inventory" +   layout="topleft" +   left="10" +   max_length="300" +   name="inventory search editor" +   top="3" +   width="303" /> +  <tab_container +     bg_alpha_color="DkGray" +     bg_opaque_color="DkGray" +     background_visible="true" +     background_opaque="true" +     follows="all" +     halign="center" +     height="339"       layout="topleft" -     left="10" -     mouse_opaque="false" -     name="Inventory Menu" -     top="+10" -	 visible="true" -     width="290"> -        <menu -         height="101" -         label="File" -         layout="topleft" -         left="0" -         mouse_opaque="false" -         name="File" -         tear_off="true" -         top="-117" -         width="128"> -            <menu_item_call -             label="Open" -             layout="topleft" -             name="Open"> -                <menu_item_call.on_click -                 function="Inventory.DoToSelected" -                 parameter="open" /> -            </menu_item_call> -            <menu -             create_jump_keys="true" -             label="Upload" -             layout="topleft" -             name="upload" -             tear_off="true"> -                <menu_item_call -                 label="Image (L$[COST])..." -                 layout="topleft" -                 name="Upload Image" -                 shortcut="control|U"> -                    <menu_item_call.on_click -                     function="File.UploadImage" -                     parameter="" /> -                    <menu_item_call.on_enable -                     function="File.EnableUpload" /> -                </menu_item_call> -                <menu_item_call -                 label="Sound (L$[COST])..." -                 layout="topleft" -                 name="Upload Sound"> -                    <menu_item_call.on_click -                     function="File.UploadSound" -                     parameter="" /> -                    <menu_item_call.on_enable -                     function="File.EnableUpload" /> -                </menu_item_call> -                <menu_item_call -                 label="Animation (L$[COST])..." -                 layout="topleft" -                 name="Upload Animation"> -                    <menu_item_call.on_click -                     function="File.UploadAnim" -                     parameter="" /> -                    <menu_item_call.on_enable -                     function="File.EnableUpload" /> -                </menu_item_call> -                <menu_item_call -                 label="Bulk (L$[COST] per file)..." -                 layout="topleft" -                 name="Bulk Upload"> -                    <menu_item_call.on_click -                     function="File.UploadBulk" -                     parameter="" /> -                </menu_item_call> -                <menu_item_separator -                 layout="topleft" /> -            </menu> -            <menu_item_separator -             layout="topleft" /> -            <menu_item_call -             label="New Window" -             layout="topleft" -             name="New Window"> -                <menu_item_call.on_click -                 function="Inventory.NewWindow" /> -            </menu_item_call> -            <menu_item_separator -             layout="topleft" -             name="separator2" /> -            <menu_item_call -             label="Show Filters" -             layout="topleft" -             name="Show Filters"> -                <menu_item_call.on_click -                 function="Inventory.ShowFilters" /> -            </menu_item_call> -            <menu_item_call -             label="Reset Filters" -             layout="topleft" -             name="Reset Current"> -                <menu_item_call.on_click -                 function="Inventory.ResetFilters" /> -            </menu_item_call> -            <menu_item_call -             label="Close All Folders" -             layout="topleft" -             name="Close All Folders"> -                <menu_item_call.on_click -                 function="Inventory.CloseAllFolders" /> -            </menu_item_call> -            <menu_item_separator -             layout="topleft" -             name="separator3" /> -            <menu_item_call -             label="Empty Trash" -             layout="topleft" -             name="Empty Trash"> -                <menu_item_call.on_click -                 function="Inventory.EmptyTrash" /> -            </menu_item_call> -            <menu_item_call -             label="Empty Lost And Found" -             layout="topleft" -             name="Empty Lost And Found"> -                <menu_item_call.on_click -                 function="Inventory.EmptyLostAndFound" /> -            </menu_item_call> -        </menu> -        <menu -         height="121" -         label="Create" -         layout="topleft" -         left="0" -         mouse_opaque="false" -         name="Create" -         tear_off="true" -         top="-201" -         width="121"> -            <menu_item_call -             label="New Folder" -             layout="topleft" -             name="New Folder"> -                <menu_item_call.on_click -                 function="Inventory.DoCreate" -                 parameter="category" /> -            </menu_item_call> -            <menu_item_call -             label="New Script" -             layout="topleft" -             name="New Script"> -                <menu_item_call.on_click -                 function="Inventory.DoCreate" -                 parameter="lsl" /> -            </menu_item_call> -            <menu_item_call -             label="New Notecard" -             layout="topleft" -             name="New Note"> -                <menu_item_call.on_click -                 function="Inventory.DoCreate" -                 parameter="notecard" /> -            </menu_item_call> -            <menu_item_call -             label="New Gesture" -             layout="topleft" -             name="New Gesture"> -                <menu_item_call.on_click -                 function="Inventory.DoCreate" -                 parameter="gesture" /> -            </menu_item_call> -            <menu -             height="175" -             label="New Clothes" -             layout="topleft" -             left_delta="0" -             mouse_opaque="false" -             name="New Clothes" -             top_pad="514" -             width="125"> -                <menu_item_call -                 label="New Shirt" -                 layout="topleft" -                 name="New Shirt"> -                    <menu_item_call.on_click -                     function="Inventory.DoCreate" -                     parameter="shirt" /> -                </menu_item_call> -                <menu_item_call -                 label="New Pants" -                 layout="topleft" -                 name="New Pants"> -                    <menu_item_call.on_click -                     function="Inventory.DoCreate" -                     parameter="pants" /> -                </menu_item_call> -                <menu_item_call -                 label="New Shoes" -                 layout="topleft" -                 name="New Shoes"> -                    <menu_item_call.on_click -                     function="Inventory.DoCreate" -                     parameter="shoes" /> -                </menu_item_call> -                <menu_item_call -                 label="New Socks" -                 layout="topleft" -                 name="New Socks"> -                    <menu_item_call.on_click -                     function="Inventory.DoCreate" -                     parameter="socks" /> -                </menu_item_call> -                <menu_item_call -                 label="New Jacket" -                 layout="topleft" -                 name="New Jacket"> -                    <menu_item_call.on_click -                     function="Inventory.DoCreate" -                     parameter="jacket" /> -                </menu_item_call> -                <menu_item_call -                 label="New Skirt" -                 layout="topleft" -                 name="New Skirt"> -                    <menu_item_call.on_click -                     function="Inventory.DoCreate" -                     parameter="skirt" /> -                </menu_item_call> -                <menu_item_call -                 label="New Gloves" -                 layout="topleft" -                 name="New Gloves"> -                    <menu_item_call.on_click -                     function="Inventory.DoCreate" -                     parameter="gloves" /> -                </menu_item_call> -                <menu_item_call -                 label="New Undershirt" -                 layout="topleft" -                 name="New Undershirt"> -                    <menu_item_call.on_click -                     function="Inventory.DoCreate" -                     parameter="undershirt" /> -                </menu_item_call> -                <menu_item_call -                 label="New Underpants" -                 layout="topleft" -                 name="New Underpants"> -                    <menu_item_call.on_click -                     function="Inventory.DoCreate" -                     parameter="underpants" /> -                </menu_item_call> -                <menu_item_call -                 label="New Alpha" -                 layout="topleft" -                 name="New Alpha"> -                    <menu_item_call.on_click -                     function="Inventory.DoCreate" -                     parameter="alpha" /> -                </menu_item_call> -                <menu_item_call -                 label="New Tattoo" -                 layout="topleft" -                 name="New Tattoo"> -                    <menu_item_call.on_click -                     function="Inventory.DoCreate" -                     parameter="tattoo" /> -                </menu_item_call> -            </menu> -            <menu -             height="85" -             label="New Body Parts" -             layout="topleft" -             left_delta="0" -             mouse_opaque="false" -             name="New Body Parts" -             top_pad="514" -             width="118"> -                <menu_item_call -                 label="New Shape" -                 layout="topleft" -                 name="New Shape"> -                    <menu_item_call.on_click -                     function="Inventory.DoCreate" -                     parameter="shape" /> -                </menu_item_call> -                <menu_item_call -                 label="New Skin" -                 layout="topleft" -                 name="New Skin"> -                    <menu_item_call.on_click -                     function="Inventory.DoCreate" -                     parameter="skin" /> -                </menu_item_call> -                <menu_item_call -                 label="New Hair" -                 layout="topleft" -                 name="New Hair"> -                    <menu_item_call.on_click -                     function="Inventory.DoCreate" -                     parameter="hair" /> -                </menu_item_call> -                <menu_item_call -                 label="New Eyes" -                 layout="topleft" -                 name="New Eyes"> -                    <menu_item_call.on_click -                     function="Inventory.DoCreate" -                     parameter="eyes" /> -                </menu_item_call> -            </menu> -        </menu> -        <menu -         height="49" -         label="Sort" -         layout="topleft" -         left="0" -         mouse_opaque="false" -         name="Sort" -         tear_off="true" -         top="-113" -         width="118"> -            <menu_item_check -             control_name="Inventory.SortByName" -             label="By Name" -             layout="topleft" -             name="By Name"> -                <menu_item_check.on_click -                 function="Inventory.SetSortBy" -                 parameter="name" /> -            </menu_item_check> -            <menu_item_check -             control_name="Inventory.SortByDate" -             label="By Date" -             layout="topleft" -             name="By Date"> -                <menu_item_check.on_click -                 function="Inventory.SetSortBy" -                 parameter="date" /> -            </menu_item_check> -            <menu_item_separator -             layout="topleft" /> -            <menu_item_check -             control_name="Inventory.FoldersAlwaysByName" -             label="Folders Always By Name" -             layout="topleft" -             name="Folders Always By Name"> -                <menu_item_check.on_click -                 function="Inventory.SetSortBy" -                 parameter="foldersalwaysbyname" /> -            </menu_item_check> -            <menu_item_check -             control_name="Inventory.SystemFoldersToTop" -             label="System Folders To Top" -             layout="topleft" -             name="System Folders To Top"> -                <menu_item_check.on_click -                 function="Inventory.SetSortBy" -                 parameter="systemfolderstotop" /> -            </menu_item_check> -        </menu> -    </menu_bar> -    <filter_editor -     text_pad_left="10" -     follows="left|top|right" -	 height="23" -     label="Filter Inventory" +     left="7" +     name="inventory filter tabs" +     tab_height="30" +     tab_position="top" +     tab_min_width="100" +     top_pad="10" +     width="312"> +    <inventory_panel +        bg_opaque_color="DkGray2" +   bg_alpha_color="DkGray2" +   background_visible="true" +   background_opaque="true" +     border="false" +     bevel_style="none" +     follows="all" +     height="338" +     label="MY INVENTORY" +     help_topic="my_inventory_tab"       layout="topleft" -     left="10" -     max_length="300" -     name="inventory search editor" -     top="+31" -     width="303" /> -    <tab_container -       bg_alpha_color="DkGray" -       bg_opaque_color="DkGray" -       background_visible="true" -       background_opaque="true" -       follows="all" -       halign="center" -       height="311" -       layout="topleft" -       left="7" -       name="inventory filter tabs" -       tab_height="30" -       tab_position="top" -       tab_min_width="100" -       top_pad="10" -       width="312"> -        <inventory_panel -            bg_opaque_color="DkGray2" -       bg_alpha_color="DkGray2" -       background_visible="true" -       background_opaque="true" -	       border="false" -	       bevel_style="none" -         follows="all" -         height="310" -         label="MY INVENTORY" -         help_topic="my_inventory_tab" -         layout="topleft" -         left="0" -         name="All Items" -         sort_order_setting="InventorySortOrder" -         top="16" -         width="288" /> -        <inventory_panel -            bg_opaque_color="DkGray2" -       bg_alpha_color="DkGray2" -       background_visible="true" -       background_opaque="true" -	       border="false" -	       bevel_style="none" -         follows="all" -         height="310" -         label="RECENT" -         help_topic="recent_inventory_tab" -         layout="topleft" -         left_delta="0" -         name="Recent Items" -         width="290" /> -    </tab_container> - -    <panel -       background_visible="true" +     left="0" +     name="All Items" +     sort_order_setting="InventorySortOrder" +     top="16" +     width="288" /> +    <inventory_panel +        bg_opaque_color="DkGray2" +   bg_alpha_color="DkGray2" +   background_visible="true" +   background_opaque="true" +     border="false"       bevel_style="none" -     follows="left|right|bottom" -     height="27" +     follows="all" +     height="338" +     label="RECENT" +     help_topic="recent_inventory_tab"       layout="topleft" -     top_pad="-1" -     left="10" -     name="bottom_panel" -     width="310"> -        <button -         follows="bottom|left" -         tool_tip="Show additional options" -         height="25" -         image_hover_unselected="Toolbar_Left_Over" -         image_overlay="OptionsMenu_Off" -         image_selected="Toolbar_Left_Selected" -         image_unselected="Toolbar_Left_Off" -         layout="topleft" -         left="0" -         name="options_gear_btn" -         top="1" -         width="31" /> -        <button -         follows="bottom|left" -         height="25" -         image_hover_unselected="Toolbar_Middle_Over" -         image_overlay="AddItem_Off" -         image_selected="Toolbar_Middle_Selected" -         image_unselected="Toolbar_Middle_Off" -         layout="topleft" -         left_pad="1" -         name="add_btn" -         tool_tip="Add new item" -         width="31" /> -       <icon -        follows="bottom|left" -        height="25" -        image_name="Toolbar_Middle_Off" -        layout="topleft" -        left_pad="1" -        name="dummy_icon" -        width="209" -       /> -        <dnd_button -         follows="bottom|left" -         height="25" -         image_hover_unselected="Toolbar_Right_Over" -         image_overlay="TrashItem_Off" -         image_selected="Toolbar_Right_Selected" -         image_unselected="Toolbar_Right_Off" -         left_pad="1"  -         layout="topleft" -         name="trash_btn" -         tool_tip="Remove selected item" -         width="31"/> -    </panel> +     left_delta="0" +     name="Recent Items" +     width="290" /> +  </tab_container> -     +  <panel +     background_visible="true" +   bevel_style="none" +   follows="left|right|bottom" +   height="27" +   layout="topleft" +   top_pad="-1" +   left="10" +   name="bottom_panel" +   width="310"> +    <button +     follows="bottom|left" +     tool_tip="Show additional options" +     height="25" +     image_hover_unselected="Toolbar_Left_Over" +     image_overlay="OptionsMenu_Off" +     image_selected="Toolbar_Left_Selected" +     image_unselected="Toolbar_Left_Off" +     layout="topleft" +     left="0" +     name="options_gear_btn" +     top="1" +     width="31" /> +    <button +     follows="bottom|left" +     height="25" +     image_hover_unselected="Toolbar_Middle_Over" +     image_overlay="AddItem_Off" +     image_selected="Toolbar_Middle_Selected" +     image_unselected="Toolbar_Middle_Off" +     layout="topleft" +     left_pad="1" +     name="add_btn" +     tool_tip="Add new item" +     width="31" /> +    <icon +     follows="bottom|left" +     height="25" +     image_name="Toolbar_Middle_Off" +     layout="topleft" +     left_pad="1" +     name="dummy_icon" +     width="209" +       /> +    <dnd_button +     follows="bottom|left" +     height="25" +     image_hover_unselected="Toolbar_Right_Over" +     image_overlay="TrashItem_Off" +     image_selected="Toolbar_Right_Selected" +     image_unselected="Toolbar_Right_Off" +     left_pad="1" +     layout="topleft" +     name="trash_btn" +     tool_tip="Remove selected item" +     width="31"/> +  </panel>  </panel> diff --git a/indra/newview/skins/default/xui/en/panel_place_profile.xml b/indra/newview/skins/default/xui/en/panel_place_profile.xml index c1c1e54b47..a43b244fa0 100644 --- a/indra/newview/skins/default/xui/en/panel_place_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_place_profile.xml @@ -743,7 +743,6 @@                           bg_focus_color="DkGray2"                           bg_readonly_color="DkGray2"                           follows="left|top|right" -                         handle_edit_keys_directly="true"                           height="90"                           layout="topleft"                           left="10" diff --git a/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml b/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml index e604e401f6..69e8e6fdcc 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml @@ -186,6 +186,32 @@ Automatic position for:  		     function="Pref.applyUIColor"  		     parameter="BackgroundChatColor" />      </color_swatch> +  <text +   type="string" +   length="1" +   follows="left|top" +   height="12" +   layout="topleft" +   left="30" +   name="UI Size:" +   top_pad="5" +   width="300"> +    UI size +  </text> +  <slider +   control_name="UIScaleFactor" +   decimal_digits="2" +   follows="left|top" +   height="17" +   increment="0.025" +   initial_value="1" +   layout="topleft" +   left_delta="52" +   max_val="1.4" +   min_val="0.75" +   name="ui_scale_slider" +   top_pad="-14" +   width="180" />      <check_box       control_name="ShowScriptErrors"       follows="left|top" @@ -193,6 +219,7 @@ Automatic position for:       label="Show script errors in:"       layout="topleft"       left="30" +     top_pad="10"        name="show_script_errors"       width="256" />      <radio_group @@ -247,6 +274,7 @@ Automatic position for:       top_pad="5"       width="200" />      <button +     layout="topleft"        follows="top|left"       enabled_control="EnableVoiceChat"       height="23" @@ -257,8 +285,8 @@ Automatic position for:            <button.commit_callback            function="Pref.VoiceSetKey" />      </button> -    <button -     enabled_control="EnableVoiceChat" +  <button +   enabled_control="EnableVoiceChat"       follows="top|left"       halign="center"       height="23" @@ -271,4 +299,15 @@ Automatic position for:            <button.commit_callback            function="Pref.VoiceSetMiddleMouse" />      </button> +  <button + height="23" + label="Other Devices" + left="30" + name="joystick_setup_button" + top_pad="12" + width="155"> +    <button.commit_callback +     function="Floater.Show" +     parameter="pref_joystick" /> +  </button>  </panel> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_general.xml b/indra/newview/skins/default/xui/en/panel_preferences_general.xml index e667fa9a2b..9eaabbe77b 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_general.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_general.xml @@ -177,7 +177,7 @@       layout="topleft"       left="30"       name="start_location_textbox" -     top_pad="10" +     top_pad="15"       width="394">          Start location:      </text> @@ -256,26 +256,15 @@       left="50"       name="show_my_name_checkbox1"       width="300" /> -    <check_box -	 enabled_control="AvatarNameTagMode" -     control_name="SmallAvatarNames" -     height="16" -     initial_value="true" -     label="Small name tags" -     layout="topleft" -     left_delta="175" -     name="small_avatar_names_checkbox" -     width="200" />     <check_box  	 enabled_control="AvatarNameTagMode"       control_name="RenderShowGroupTitleAll"       height="16"       label="Show group titles"       layout="topleft" -     left_delta="-175" +     left_delta="175"       name="show_all_title_checkbox1" -     top_pad="5" -     width="300" /> +     width="200" />      <text       type="string"       length="1" @@ -354,7 +343,7 @@       left="30"       mouse_opaque="false"       name="text_box3" -     top_pad="10" +     top_pad="15"       width="240">         Busy mode response:      </text> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml index c74de043e9..44c44f5f59 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml @@ -16,35 +16,8 @@       height="12"       layout="topleft"       left="30" -     name="UI Size:" -     top="10" -     width="300"> -        UI size: -    </text> -    <slider -     can_edit_text="true" -     control_name="UIScaleFactor" -     decimal_digits="2" -     follows="left|top" -     height="15" -     increment="0.025" -     initial_value="1" -     layout="topleft" -     left_delta="52" -     max_val="1.4" -     min_val="0.75" -     name="ui_scale_slider" -     top_pad="2" -     width="180" /> -    <text -     type="string" -     length="1" -     follows="left|top" -     height="12" -     layout="topleft" -     left="30"       name="QualitySpeed" -     top_pad="4" +     top="10"        width="400">          Quality and speed:      </text> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml index 2123e62daa..500e65b916 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml @@ -9,18 +9,6 @@   name="Input panel"   top="1"   width="517"> -  <button -   height="23" -   label="Other Devices" -   layout="topleft" -   left="30" -   name="joystick_setup_button" -   top="10" -   width="155"> -    <button.commit_callback -     function="Floater.Show" -     parameter="pref_joystick" /> -  </button>    <text     type="string"     length="1" @@ -29,7 +17,7 @@     layout="topleft"     left="30"     name="Mouselook:" -   top_pad="10" +   top="10"     width="300">      Mouselook:    </text> @@ -210,7 +198,6 @@     enabled="false"     follows="left|top"     font="SansSerif" -   handle_edit_keys_directly="true"     height="23"     layout="topleft"     left="80" diff --git a/indra/newview/skins/default/xui/en/panel_region_covenant.xml b/indra/newview/skins/default/xui/en/panel_region_covenant.xml index dc8f71c868..2b2ea78fac 100644 --- a/indra/newview/skins/default/xui/en/panel_region_covenant.xml +++ b/indra/newview/skins/default/xui/en/panel_region_covenant.xml @@ -113,7 +113,6 @@       max_length="65535"       name="covenant_editor"       top_delta="30" -     handle_edit_keys_directly="true"       width="340"       word_wrap="true">          There is no Covenant provided for this Estate. diff --git a/indra/newview/skins/default/xui/en/panel_script_ed.xml b/indra/newview/skins/default/xui/en/panel_script_ed.xml index d444420550..c5c66c04d5 100644 --- a/indra/newview/skins/default/xui/en/panel_script_ed.xml +++ b/indra/newview/skins/default/xui/en/panel_script_ed.xml @@ -143,7 +143,6 @@       name="Script Editor"       width="487"       show_line_numbers="true"  -     handle_edit_keys_directly="true"        word_wrap="true">          Loading...      </text_editor> diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 0c73b8d769..9ac4ef9b37 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -46,6 +46,15 @@  	<string name="LoginWaitingForRegionHandshake">Waiting for region handshake...</string>  	<string name="LoginConnectingToRegion">Connecting to region...</string>  	<string name="LoginDownloadingClothing">Downloading clothing...</string> +        <string name="InvalidCertificate">The server returned an invalid or corrupt certificate. Please contact the Grid administrator.</string> +        <string name="CertInvalidHostname">An invalid hostname was used to access the server, please check your SLURL or Grid hostname.</string> +        <string name="CertExpired">The certificate returned by the Grid appears to be expired.  Please check your system clock, or contact your Grid administr\ +ator.</string> +        <string name="CertKeyUsage">The certificate returned by the server could not be used for SSL.  Please contact your Grid administrator.</string> +        <string name="CertBasicConstraints">Too many certificates were in the servers Certificate chain.  Please contact your Grid administrator.</string> +        <string name="CertInvalidSignature">The certificate signature returned by the Grid server could not be verified.  Please contact your Grid administrat +or.</string> +  	<string name="LoginFailedNoNetwork">Network Error: Could not establish connection, please check your network connection.</string>  	<string name="LoginFailed">Login failed.</string>  	<string name="Quit">Quit</string> diff --git a/indra/newview/skins/default/xui/en/widgets/line_editor.xml b/indra/newview/skins/default/xui/en/widgets/line_editor.xml index a21e3f2645..a054960bf8 100644 --- a/indra/newview/skins/default/xui/en/widgets/line_editor.xml +++ b/indra/newview/skins/default/xui/en/widgets/line_editor.xml @@ -3,7 +3,6 @@               background_image_disabled="TextField_Disabled"               background_image_focused="TextField_Active"               select_on_focus="false" -             handle_edit_keys_directly="false"               commit_on_focus_lost="true"               ignore_tab="true"               cursor_color="TextCursorColor" diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp index ef93586c6e..67da9f2cdf 100644 --- a/indra/newview/tests/lllogininstance_test.cpp +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -10,7 +10,10 @@  // Precompiled header  #include "../llviewerprecompiledheaders.h"  // Own header +#include "../llsecapi.h" +#include "../llviewernetwork.h"  #include "../lllogininstance.h" +  // STL headers  // std headers  // external library headers @@ -33,7 +36,12 @@ const std::string APPVIEWER_SERIALNUMBER("appviewer_serialno");  //-----------------------------------------------------------------------------  static LLEventStream gTestPump("test_pump"); +#include "../llslurl.h" +#include "../llstartup.h" +LLSLURL LLStartUp::sStartSLURL; +  #include "lllogin.h" +  static std::string gLoginURI;  static LLSD gLoginCreds;  static bool gDisconnectCalled = false; @@ -54,17 +62,68 @@ void LLLogin::disconnect()  	gDisconnectCalled = true;  } +LLSD LLCredential::getLoginParams() +{ +	LLSD result = LLSD::emptyMap(); + +	// legacy credential +	result["passwd"] = "$1$testpasssd"; +	result["first"] = "myfirst"; +	result["last"] ="mylast"; +	return result; +} +  //-----------------------------------------------------------------------------  #include "../llviewernetwork.h" -unsigned char gMACAddress[MAC_ADDRESS_BYTES] = {'1','2','3','4','5','6'}; +LLGridManager::~LLGridManager() +{ +} + +void LLGridManager::addGrid(LLSD& grid_data) +{ +} +LLGridManager::LLGridManager() +{	 +} -LLViewerLogin::LLViewerLogin() : mGridChoice(GRID_INFO_NONE) {} -LLViewerLogin::~LLViewerLogin() {} -void LLViewerLogin::getLoginURIs(std::vector<std::string>& uris) const  +void LLGridManager::getLoginURIs(std::vector<std::string>& uris)  {  	uris.push_back(VIEWERLOGIN_URI);  } -std::string LLViewerLogin::getGridLabel() const { return VIEWERLOGIN_GRIDLABEL; } + +void LLGridManager::addSystemGrid(const std::string& label,  +								  const std::string& name,  +								  const std::string& login,  +								  const std::string& helper, +								  const std::string& login_page, +								  const std::string& login_id) +{ +} +std::map<std::string, std::string> LLGridManager::getKnownGrids(bool favorite_only) +{ +	std::map<std::string, std::string> result; +	return result; +} + +void LLGridManager::setGridChoice(const std::string& grid_name) +{ +} + +bool LLGridManager::isInProductionGrid() +{ +	return false; +} + +void LLGridManager::saveFavorites() +{} +std::string LLGridManager::getSLURLBase(const std::string& grid_name) +{ +	return "myslurl"; +} +std::string LLGridManager::getAppSLURLBase(const std::string& grid_name) +{ +	return "myappslurl"; +}  //-----------------------------------------------------------------------------  #include "../llviewercontrol.h" @@ -86,10 +145,6 @@ BOOL LLControlGroup::declareString(const std::string& name, const std::string &i  #include "lluicolortable.h"  void LLUIColorTable::saveUserSettings(void)const {} -//----------------------------------------------------------------------------- -#include "../llurlsimstring.h" -LLURLSimString LLURLSimString::sInstance; -bool LLURLSimString::parse() { return true; }  //-----------------------------------------------------------------------------  #include "llnotifications.h" @@ -197,15 +252,29 @@ namespace tut  			gSavedSettings.declareString("NextLoginLocation", "", "", FALSE);  			gSavedSettings.declareBOOL("LoginLastLocation", FALSE, "", FALSE); -			credentials["first"] = "testfirst"; -			credentials["last"] = "testlast"; -			credentials["passwd"] = "testpass"; +			LLSD authenticator = LLSD::emptyMap(); +			LLSD identifier = LLSD::emptyMap(); +			identifier["type"] = "agent"; +			identifier["first_name"] = "testfirst"; +			identifier["last_name"] = "testlast"; +			authenticator["passwd"] = "testpass"; +			agentCredential = new LLCredential(); +			agentCredential->setCredentialData(identifier, authenticator); +			 +			authenticator = LLSD::emptyMap(); +			identifier = LLSD::emptyMap(); +			identifier["type"] = "account"; +			identifier["username"] = "testuser"; +			authenticator["secret"] = "testsecret"; +			accountCredential = new LLCredential(); +			accountCredential->setCredentialData(identifier, authenticator);			  			logininstance->setNotificationsInterface(¬ifications);  		}  		LLLoginInstance* logininstance; -		LLSD credentials; +		LLPointer<LLCredential> agentCredential; +		LLPointer<LLCredential> accountCredential;  		MockNotifications notifications;      }; @@ -219,7 +288,7 @@ namespace tut  		set_test_name("Test Simple Success And Disconnect");  		// Test default connect. -		logininstance->connect(credentials); +		logininstance->connect(agentCredential);  		ensure_equals("Default connect uri", gLoginURI, VIEWERLOGIN_URI);  @@ -260,7 +329,7 @@ namespace tut  		const std::string test_uri = "testing-uri";  		// Test default connect. -		logininstance->connect(test_uri, credentials); +		logininstance->connect(test_uri, agentCredential);  		// connect should call LLLogin::connect to init gLoginURI and gLoginCreds.  		ensure_equals("Default connect uri", gLoginURI, "testing-uri");  @@ -282,7 +351,7 @@ namespace tut  		ensure("No TOS, failed auth", logininstance->authFailure());  		// Start again. -		logininstance->connect(test_uri, credentials); +		logininstance->connect(test_uri, agentCredential);  		gTestPump.post(response); // Fail for tos again.  		gTOSReplyPump->post(true); // Accept tos, should reconnect w/ agree_to_tos.  		ensure_equals("Accepted agree to tos", gLoginCreds["params"]["agree_to_tos"].asBoolean(), true); @@ -294,11 +363,11 @@ namespace tut  		gTestPump.post(response);  		ensure("TOS auth failure", logininstance->authFailure()); -		logininstance->connect(test_uri, credentials); +		logininstance->connect(test_uri, agentCredential);  		ensure_equals("Reset to default for agree to tos", gLoginCreds["params"]["agree_to_tos"].asBoolean(), false);  		// Critical Message failure response. -		logininstance->connect(test_uri, credentials); +		logininstance->connect(test_uri, agentCredential);  		response["data"]["reason"] = "critical"; // Change response to "critical message"  		gTestPump.post(response); @@ -312,7 +381,7 @@ namespace tut  		response["data"]["reason"] = "key"; // bad creds.  		gTestPump.post(response);  		ensure("TOS auth failure", logininstance->authFailure()); -		logininstance->connect(test_uri, credentials); +		logininstance->connect(test_uri, agentCredential);  		ensure_equals("Default for agree to tos", gLoginCreds["params"]["read_critical"].asBoolean(), false);  	} @@ -323,7 +392,7 @@ namespace tut  		// Part 1 - Mandatory Update, with User accepts response.  		// Test connect with update needed. -		logininstance->connect(credentials); +		logininstance->connect(agentCredential);  		ensure_equals("Default connect uri", gLoginURI, VIEWERLOGIN_URI);  @@ -349,7 +418,7 @@ namespace tut  		set_test_name("Test Mandatory Update User Decline");  		// Test connect with update needed. -		logininstance->connect(credentials); +		logininstance->connect(agentCredential);  		ensure_equals("Default connect uri", gLoginURI, VIEWERLOGIN_URI);  @@ -375,7 +444,7 @@ namespace tut  		// Part 3 - Mandatory Update, with bogus response.  		// Test connect with update needed. -		logininstance->connect(credentials); +		logininstance->connect(agentCredential);  		ensure_equals("Default connect uri", gLoginURI, VIEWERLOGIN_URI);  @@ -401,7 +470,7 @@ namespace tut  		// Part 3 - Mandatory Update, with bogus response.  		// Test connect with update needed. -		logininstance->connect(credentials); +		logininstance->connect(agentCredential);  		ensure_equals("Default connect uri", gLoginURI, VIEWERLOGIN_URI);  diff --git a/indra/newview/tests/llsecapi_test.cpp b/indra/newview/tests/llsecapi_test.cpp new file mode 100644 index 0000000000..caa1461987 --- /dev/null +++ b/indra/newview/tests/llsecapi_test.cpp @@ -0,0 +1,188 @@ +/**  + * @file llsecapi_test.cpp + * @author Roxie + * @date 2009-02-10 + * @brief Test the sec api functionality + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + *  + * Copyright (c) 2009, Linden Research, Inc. + *  + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden LregisterSecAPIab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab.  Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + *  + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + *  + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + *  + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ +#include "../llviewerprecompiledheaders.h" +#include "../llviewernetwork.h" +#include "../test/lltut.h" +#include "../llsecapi.h" +#include "../../llxml/llcontrol.h" + + +//----------------------------------------------------------------------------                +// Mock objects for the dependencies of the code we're testing                                + +LLControlGroup::LLControlGroup(const std::string& name) +: LLInstanceTracker<LLControlGroup, std::string>(name) {} +LLControlGroup::~LLControlGroup() {} +BOOL LLControlGroup::declareString(const std::string& name, +                                   const std::string& initial_val, +                                   const std::string& comment, +                                   BOOL persist) {return TRUE;} +void LLControlGroup::setString(const std::string& name, const std::string& val){} +std::string LLControlGroup::getString(const std::string& name) +{ +	return ""; +} + + +LLControlGroup gSavedSettings("test"); +class LLSecAPIBasicHandler : public LLSecAPIHandler +{ +protected: +	LLPointer<LLCertificateChain> mCertChain; +	LLPointer<LLCertificate> mCert; +	LLPointer<LLCertificateStore> mCertStore; +	LLSD mLLSD; +	 +public: +	LLSecAPIBasicHandler() {} +	 +	virtual ~LLSecAPIBasicHandler() {} +	 +	// instantiate a certificate from a pem string +	virtual LLPointer<LLCertificate> getCertificate(const std::string& pem_cert)  +	{  +		return mCert;  +	} +	 +	 +	// instiate a certificate from an openssl X509 structure +	virtual LLPointer<LLCertificate> getCertificate(X509* openssl_cert)  +	{	 +		return mCert;  +	} +	 +	 +	// instantiate a chain from an X509_STORE_CTX +	virtual LLPointer<LLCertificateChain> getCertificateChain(const X509_STORE_CTX* chain)  +	{  +		return mCertChain;  +	} +	 +	// instantiate a cert store given it's id.  if a persisted version +	// exists, it'll be loaded.  If not, one will be created (but not +	// persisted) +	virtual LLPointer<LLCertificateStore> getCertificateStore(const std::string& store_id)  +	{  +		return mCertStore;  +	} +	 +	// persist data in a protected store +	virtual void setProtectedData(const std::string& data_type, +								  const std::string& data_id, +								  const LLSD& data) {} +	 +	// retrieve protected data +	virtual LLSD getProtectedData(const std::string& data_type, +								  const std::string& data_id)  +	{  +		return mLLSD; +	} +	 +	virtual void deleteProtectedData(const std::string& data_type, +									 const std::string& data_id) +	{ +	} +	 +	virtual LLPointer<LLCredential> createCredential(const std::string& grid, +													 const LLSD& identifier,  +													 const LLSD& authenticator) +	{ +		LLPointer<LLCredential> cred = NULL; +		return cred; +	} +	 +	virtual LLPointer<LLCredential> loadCredential(const std::string& grid) +	{ +		LLPointer<LLCredential> cred = NULL; +		return cred; +	} +	 +	virtual void saveCredential(LLPointer<LLCredential> cred, bool save_authenticator) {} +	 +	virtual void deleteCredential(LLPointer<LLCredential> cred) {} +}; + +// ------------------------------------------------------------------------------------------- +// TUT +// ------------------------------------------------------------------------------------------- +namespace tut +{ +	// Test wrapper declaration : wrapping nothing for the moment +	struct secapiTest +	{ +		 +		secapiTest() +		{ +		} +		~secapiTest() +		{ +		} +	}; +	 +	// Tut templating thingamagic: test group, object and test instance +	typedef test_group<secapiTest> secapiTestFactory; +	typedef secapiTestFactory::object secapiTestObject; +	tut::secapiTestFactory tut_test("llsecapi"); +	 +	// --------------------------------------------------------------------------------------- +	// Test functions  +	// --------------------------------------------------------------------------------------- +	// registration +	template<> template<> +	void secapiTestObject::test<1>() +	{ +		// retrieve an unknown handler + +		ensure("'Unknown' handler should be NULL", !(BOOL)getSecHandler("unknown")); +		LLPointer<LLSecAPIHandler> test1_handler =  new LLSecAPIBasicHandler(); +		registerSecHandler("sectest1", test1_handler); +		ensure("'Unknown' handler should be NULL", !(BOOL)getSecHandler("unknown")); +		LLPointer<LLSecAPIHandler> retrieved_test1_handler = getSecHandler("sectest1"); +		ensure("Retrieved sectest1 handler should be the same",  +			   retrieved_test1_handler == test1_handler); +		 +		// insert a second handler +		LLPointer<LLSecAPIHandler> test2_handler =  new LLSecAPIBasicHandler(); +		registerSecHandler("sectest2", test2_handler); +		ensure("'Unknown' handler should be NULL", !(BOOL)getSecHandler("unknown")); +		retrieved_test1_handler = getSecHandler("sectest1"); +		ensure("Retrieved sectest1 handler should be the same",  +			   retrieved_test1_handler == test1_handler); + +		LLPointer<LLSecAPIHandler> retrieved_test2_handler = getSecHandler("sectest2"); +		ensure("Retrieved sectest1 handler should be the same",  +			   retrieved_test2_handler == test2_handler); +		 +	} +} diff --git a/indra/newview/tests/llsechandler_basic_test.cpp b/indra/newview/tests/llsechandler_basic_test.cpp new file mode 100644 index 0000000000..236d17c591 --- /dev/null +++ b/indra/newview/tests/llsechandler_basic_test.cpp @@ -0,0 +1,964 @@ +/**  + * @file llsechandler_basic_test.cpp + * @author Roxie + * @date 2009-02-10 + * @brief Test the 'basic' sec handler functions + * + * $LicenseInfo:firstyear=2005&license=viewergpl$ + *  + * Copyright (c) 2005-2009, Linden Research, Inc. + *  + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab.  Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + *  + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + *  + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + *  + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ +#include "../llviewerprecompiledheaders.h" +#include "../test/lltut.h" +#include "../llsecapi.h" +#include "../llsechandler_basic.h" +#include "../../llxml/llcontrol.h" +#include "../llviewernetwork.h" +#include "lluuid.h" +#include "llxorcipher.h" +#include "apr_base64.h" +#include <vector> +#include <ios> +#include <llsdserialize.h> +#include <openssl/pem.h> +#include <openssl/err.h> +#include <openssl/evp.h> +#include "llxorcipher.h" + +#define ensure_throws(str, exc_type, cert, func, ...) \ +try \ +{ \ +func(__VA_ARGS__); \ +fail("throws, " str); \ +} \ +catch(exc_type& except) \ +{ \ +ensure("Exception cert is incorrect for " str, except.getCert() == cert); \ +} + +extern bool _cert_hostname_wildcard_match(const std::string& hostname, const std::string& wildcard_string); + +//----------------------------------------------------------------------------                +// Mock objects for the dependencies of the code we're testing                                + +std::string gFirstName; +std::string gLastName; +LLControlGroup::LLControlGroup(const std::string& name) +: LLInstanceTracker<LLControlGroup, std::string>(name) {} +LLControlGroup::~LLControlGroup() {} +BOOL LLControlGroup::declareString(const std::string& name, +                                   const std::string& initial_val, +                                   const std::string& comment, +                                   BOOL persist) {return TRUE;} +void LLControlGroup::setString(const std::string& name, const std::string& val){} +std::string LLControlGroup::getString(const std::string& name) +{ + +	if (name == "FirstName") +		return gFirstName; +	else if (name == "LastName") +		return gLastName; +	return ""; +} + +LLSD LLCredential::getLoginParams() +{ +	LLSD result = LLSD::emptyMap(); +	 +	// legacy credential +	result["passwd"] = "$1$testpasssd"; +	result["first"] = "myfirst"; +	result["last"] ="mylast"; +	return result; +} + + + +LLControlGroup gSavedSettings("test"); +unsigned char gMACAddress[MAC_ADDRESS_BYTES] = {77,21,46,31,89,2}; + +// ------------------------------------------------------------------------------------------- +// TUT +// ------------------------------------------------------------------------------------------- +namespace tut +{ +	// Test wrapper declaration : wrapping nothing for the moment +	struct sechandler_basic_test +	{ +		std::string mPemTestCert, mPemRootCert, mPemIntermediateCert, mPemChildCert; +		std::string mDerFormat; +		X509 *mX509TestCert, *mX509RootCert, *mX509IntermediateCert, *mX509ChildCert; + +		sechandler_basic_test() +		{ +			OpenSSL_add_all_algorithms(); +			OpenSSL_add_all_ciphers(); +			OpenSSL_add_all_digests();	 +			ERR_load_crypto_strings(); +			gFirstName = ""; +			gLastName = ""; +			LLFile::remove("test_password.dat"); +			LLFile::remove("sechandler_settings.tmp");			 +			mPemTestCert = "-----BEGIN CERTIFICATE-----\n" +				"MIIEuDCCA6CgAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBtDELMAkGA1UEBhMCQlIx\n" +				"EzARBgNVBAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25h\n" +				"bCBkZSBUZWNub2xvZ2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxETAPBgNVBAcTCEJy\n" +				"YXNpbGlhMQswCQYDVQQIEwJERjExMC8GA1UEAxMoQXV0b3JpZGFkZSBDZXJ0aWZp\n" +				"Y2Fkb3JhIFJhaXogQnJhc2lsZWlyYTAeFw0wMTExMzAxMjU4MDBaFw0xMTExMzAy\n" +				"MzU5MDBaMIG0MQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDE9MDsG\n" +				"A1UECxM0SW5zdGl0dXRvIE5hY2lvbmFsIGRlIFRlY25vbG9naWEgZGEgSW5mb3Jt\n" +				"YWNhbyAtIElUSTERMA8GA1UEBxMIQnJhc2lsaWExCzAJBgNVBAgTAkRGMTEwLwYD\n" +				"VQQDEyhBdXRvcmlkYWRlIENlcnRpZmljYWRvcmEgUmFpeiBCcmFzaWxlaXJhMIIB\n" +				"IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwPMudwX/hvm+Uh2b/lQAcHVA\n" +				"isamaLkWdkwP9/S/tOKIgRrL6Oy+ZIGlOUdd6uYtk9Ma/3pUpgcfNAj0vYm5gsyj\n" +				"Qo9emsc+x6m4VWwk9iqMZSCK5EQkAq/Ut4n7KuLE1+gdftwdIgxfUsPt4CyNrY50\n" +				"QV57KM2UT8x5rrmzEjr7TICGpSUAl2gVqe6xaii+bmYR1QrmWaBSAG59LrkrjrYt\n" +				"bRhFboUDe1DK+6T8s5L6k8c8okpbHpa9veMztDVC9sPJ60MWXh6anVKo1UcLcbUR\n" +				"yEeNvZneVRKAAU6ouwdjDvwlsaKydFKwed0ToQ47bmUKgcm+wV3eTRk36UOnTwID\n" +				"AQABo4HSMIHPME4GA1UdIARHMEUwQwYFYEwBAQAwOjA4BggrBgEFBQcCARYsaHR0\n" +				"cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0RQQ2FjcmFpei5wZGYwPQYDVR0f\n" +				"BDYwNDAyoDCgLoYsaHR0cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0xDUmFj\n" +				"cmFpei5jcmwwHQYDVR0OBBYEFIr68VeEERM1kEL6V0lUaQ2kxPA3MA8GA1UdEwEB\n" +				"/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAZA5c1\n" +				"U/hgIh6OcgLAfiJgFWpvmDZWqlV30/bHFpj8iBobJSm5uDpt7TirYh1Uxe3fQaGl\n" +				"YjJe+9zd+izPRbBqXPVQA34EXcwk4qpWuf1hHriWfdrx8AcqSqr6CuQFwSr75Fos\n" +				"SzlwDADa70mT7wZjAmQhnZx2xJ6wfWlT9VQfS//JYeIc7Fue2JNLd00UOSMMaiK/\n" +				"t79enKNHEA2fupH3vEigf5Eh4bVAN5VohrTm6MY53x7XQZZr1ME7a55lFEnSeT0u\n" +				"mlOAjR2mAbvSM5X5oSZNrmetdzyTj2flCM8CC7MLab0kkdngRIlUBGHF1/S5nmPb\n" +				"K+9A46sd33oqK8n8\n" +				"-----END CERTIFICATE-----\n"; + +			mPemRootCert = "-----BEGIN CERTIFICATE-----\n" +			"MIIB0TCCATqgAwIBAgIJANaTqrzEvHaRMA0GCSqGSIb3DQEBBAUAMBsxGTAXBgNV\n" +			"BAMTEFJveGllcyB0ZXN0IHJvb3QwHhcNMDkwNDE1MjEwNzQ3WhcNMTAwNDE1MjEw\n" +			"NzQ3WjAbMRkwFwYDVQQDExBSb3hpZXMgdGVzdCByb290MIGfMA0GCSqGSIb3DQEB\n" +			"AQUAA4GNADCBiQKBgQCpo5nDW6RNz9IHUVZd7Tw2XAQiBniDF4xH0N1w7sUYTiFq\n" +			"21mABsnOPJD3ra+MtOsXPHcaljm661JjTD8L40v5sfEbqDUPcOw76ClrPqnuAeyT\n" +			"38qk8DHku/mT8YdprevGZdVcUXQg3vosVzOL93HOOHK+u61mEEoM9W5xoNVEdQID\n" +			"AQABox0wGzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQQF\n" +			"AAOBgQAzn0aW/+zWPmcTbvxonyiYYUr9b4SOB/quhAkT8KT4ir1dcZAXRR59+kEn\n" +			"HSTu1FAodV0gvESqyobftF5hZ1XMxdJqGu//xP+YCwlv244G/0pp7KLI8ihNO2+N\n" +			"lPBUJgbo++ZkhiE1jotZi9Ay0Oedh3s/AfbMZPyfpJ23ll6+BA==\n" +			"-----END CERTIFICATE-----\n"; +			 +			 +			 +			mPemIntermediateCert = "-----BEGIN CERTIFICATE-----\n" +			"MIIBzzCCATigAwIBAgIBATANBgkqhkiG9w0BAQQFADAbMRkwFwYDVQQDExBSb3hp\n" +			"ZXMgdGVzdCByb290MB4XDTA5MDQxNTIxMzE1NloXDTEwMDQxNTIxMzE1NlowITEf\n" +			"MB0GA1UEAxMWUm94aWVzIGludGVybWVkaWF0ZSBDQTCBnzANBgkqhkiG9w0BAQEF\n" +			"AAOBjQAwgYkCgYEA15MM0W1R37rx/24Q2Qkb5bSiQZxTUcQAhJ2pA8mwUucXuCVt\n" +			"6ayI2TuN32nkjmsCgUkiT/bdXWp0OJo7/MXRIFeUNMCRxrpeFnxuigYEqbIXAdN6\n" +			"qu/vdG2X4PRv/v9Ijrju4cBEiKIldIgOurWEIfXEsVSFP2XmFQHesF04qDcCAwEA\n" +			"AaMdMBswDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQEEBQAD\n" +			"gYEAYljikYgak3W1jSo0vYthNHUy3lBVAKzDhpM96lY5OuXFslpCRX42zNL8X3kN\n" +			"U/4IaJUVtZqx8WsUXl1eXHzBCaXCftapV4Ir6cENLIsXCdXs8paFYzN5nPJA5GYU\n" +			"zWgkSEl1MEhNIc+bJW34vwi29EjrAShAhsIZ84Mt/lvD3Pc=\n" +			"-----END CERTIFICATE-----\n"; +			 +			mPemChildCert = "-----BEGIN CERTIFICATE-----\n" +			"MIIB5DCCAU0CBEnm9eUwDQYJKoZIhvcNAQEEBQAwITEfMB0GA1UEAxMWUm94aWVz\n" +			"IGludGVybWVkaWF0ZSBDQTAeFw0wOTA0MTYwMDAzNDlaFw0xMDA0MTYwMDAzNDla\n" +			"MCAxHjAcBgNVBAMTFWVuaWFjNjMubGluZGVubGFiLmNvbTCBnzANBgkqhkiG9w0B\n" +			"AQEFAAOBjQAwgYkCgYEAp9I5rofEzbjNht+9QejfnsIlEPqSxskoWKCG255TesWR\n" +			"RTmw9wafHQQkJk/VIsaU4RMBYHkknGbHX2dGvMHmKZoWUPSQ/8FZz09o0Qx3TNUZ\n" +			"l7KlGOD2d1c7ZxXDPqlLC6QW8DrE1/8zfwJ5cbYBXc8e7OKdSZeRrnwHyw4Q8r8C\n" +			"AwEAAaMvMC0wEwYDVR0lBAwwCgYIKwYBBQUHAwEwCQYDVR0TBAIwADALBgNVHQ8E\n" +			"BAMCBaAwDQYJKoZIhvcNAQEEBQADgYEAIG0M5tqYlXyMiGKPZfXy/R3M3ZZOapDk\n" +			"W0dsXJYXAc35ftwtn0VYu9CNnZCcli17/d+AKhkK8a/oGPazqudjFF6WLJLTXaY9\n" +			"NmhkJcOPADXkbyQPUPXzLe4YRrkEQeGhzMb4rKDQ1TKAcXfs0Y068pTpsixNSxja\n" +			"NhAUUcve5Is=\n" +			"-----END CERTIFICATE-----\n"; +			 +			mDerFormat = "MIIEuDCCA6CgAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBtDELMAkGA1UEBhMCQlIxEzARBgNVBAoT" +"CklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25hbCBkZSBUZWNub2xvZ2lhIGRh" +"IEluZm9ybWFjYW8gLSBJVEkxETAPBgNVBAcTCEJyYXNpbGlhMQswCQYDVQQIEwJERjExMC8GA1UE" +"AxMoQXV0b3JpZGFkZSBDZXJ0aWZpY2Fkb3JhIFJhaXogQnJhc2lsZWlyYTAeFw0wMTExMzAxMjU4" +"MDBaFw0xMTExMzAyMzU5MDBaMIG0MQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDE9" +"MDsGA1UECxM0SW5zdGl0dXRvIE5hY2lvbmFsIGRlIFRlY25vbG9naWEgZGEgSW5mb3JtYWNhbyAt" +"IElUSTERMA8GA1UEBxMIQnJhc2lsaWExCzAJBgNVBAgTAkRGMTEwLwYDVQQDEyhBdXRvcmlkYWRl" +"IENlcnRpZmljYWRvcmEgUmFpeiBCcmFzaWxlaXJhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB" +"CgKCAQEAwPMudwX/hvm+Uh2b/lQAcHVAisamaLkWdkwP9/S/tOKIgRrL6Oy+ZIGlOUdd6uYtk9Ma" +"/3pUpgcfNAj0vYm5gsyjQo9emsc+x6m4VWwk9iqMZSCK5EQkAq/Ut4n7KuLE1+gdftwdIgxfUsPt" +"4CyNrY50QV57KM2UT8x5rrmzEjr7TICGpSUAl2gVqe6xaii+bmYR1QrmWaBSAG59LrkrjrYtbRhF" +"boUDe1DK+6T8s5L6k8c8okpbHpa9veMztDVC9sPJ60MWXh6anVKo1UcLcbURyEeNvZneVRKAAU6o" +"uwdjDvwlsaKydFKwed0ToQ47bmUKgcm+wV3eTRk36UOnTwIDAQABo4HSMIHPME4GA1UdIARHMEUw" +"QwYFYEwBAQAwOjA4BggrBgEFBQcCARYsaHR0cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0RQ" +"Q2FjcmFpei5wZGYwPQYDVR0fBDYwNDAyoDCgLoYsaHR0cDovL2FjcmFpei5pY3BicmFzaWwuZ292" +"LmJyL0xDUmFjcmFpei5jcmwwHQYDVR0OBBYEFIr68VeEERM1kEL6V0lUaQ2kxPA3MA8GA1UdEwEB" +"/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAZA5c1U/hgIh6OcgLA" +"fiJgFWpvmDZWqlV30/bHFpj8iBobJSm5uDpt7TirYh1Uxe3fQaGlYjJe+9zd+izPRbBqXPVQA34E" +"Xcwk4qpWuf1hHriWfdrx8AcqSqr6CuQFwSr75FosSzlwDADa70mT7wZjAmQhnZx2xJ6wfWlT9VQf" +"S//JYeIc7Fue2JNLd00UOSMMaiK/t79enKNHEA2fupH3vEigf5Eh4bVAN5VohrTm6MY53x7XQZZr" +"1ME7a55lFEnSeT0umlOAjR2mAbvSM5X5oSZNrmetdzyTj2flCM8CC7MLab0kkdngRIlUBGHF1/S5" +"nmPbK+9A46sd33oqK8n8"; +			 +			mX509TestCert = NULL; +			mX509RootCert = NULL; +			mX509IntermediateCert = NULL; +			mX509ChildCert = NULL; +			 +			BIO * validation_bio = BIO_new_mem_buf((void*)mPemTestCert.c_str(), mPemTestCert.length());			 +			PEM_read_bio_X509(validation_bio, &mX509TestCert, 0, NULL); +			BIO_free(validation_bio); +			validation_bio = BIO_new_mem_buf((void*)mPemRootCert.c_str(), mPemRootCert.length()); +			PEM_read_bio_X509(validation_bio, &mX509RootCert, 0, NULL); +			BIO_free(validation_bio); +			validation_bio = BIO_new_mem_buf((void*)mPemIntermediateCert.c_str(), mPemIntermediateCert.length()); +			PEM_read_bio_X509(validation_bio, &mX509IntermediateCert, 0, NULL); +			BIO_free(validation_bio);	 +			validation_bio = BIO_new_mem_buf((void*)mPemChildCert.c_str(), mPemChildCert.length()); +			PEM_read_bio_X509(validation_bio, &mX509ChildCert, 0, NULL); +			BIO_free(validation_bio);				 +		} +		~sechandler_basic_test() +		{ +			LLFile::remove("test_password.dat"); +			LLFile::remove("sechandler_settings.tmp"); +			LLFile::remove("mycertstore.pem"); +			X509_free(mX509TestCert); +			X509_free(mX509RootCert); +			X509_free(mX509IntermediateCert); +			X509_free(mX509ChildCert); +		} +	}; +	 +	// Tut templating thingamagic: test group, object and test instance +	typedef test_group<sechandler_basic_test> sechandler_basic_test_factory; +	typedef sechandler_basic_test_factory::object sechandler_basic_test_object; +	tut::sechandler_basic_test_factory tut_test("llsechandler_basic"); +	 +	// --------------------------------------------------------------------------------------- +	// Test functions  +	// --------------------------------------------------------------------------------------- +	// test cert data retrieval +	template<> template<> +	void sechandler_basic_test_object::test<1>() +	 +	{ +		char buffer[4096]; +		LLPointer<LLCertificate> test_cert = new LLBasicCertificate(mPemTestCert); +		 +		ensure_equals("Resultant pem is correct", +			   mPemTestCert, test_cert->getPem()); +		std::vector<U8> binary_cert = test_cert->getBinary(); + +		apr_base64_encode(buffer, (const char *)&binary_cert[0], binary_cert.size()); +		 +		ensure_equals("Der Format is correct", memcmp(buffer, mDerFormat.c_str(), mDerFormat.length()), 0); +		 +		LLSD llsd_cert = test_cert->getLLSD(); +		std::ostringstream llsd_value; +		llsd_value << LLSDOStreamer<LLSDNotationFormatter>(llsd_cert) << std::endl; +		std::string llsd_cert_str = llsd_value.str(); +		ensure_equals("Issuer Name/commonName",  +			   (std::string)llsd_cert["issuer_name"]["commonName"], "Autoridade Certificadora Raiz Brasileira"); +		ensure_equals("Issure Name/countryName", (std::string)llsd_cert["issuer_name"]["countryName"], "BR"); +		ensure_equals("Issuer Name/localityName", (std::string)llsd_cert["issuer_name"]["localityName"], "Brasilia"); +		ensure_equals("Issuer Name/org name", (std::string)llsd_cert["issuer_name"]["organizationName"], "ICP-Brasil"); +		ensure_equals("IssuerName/org unit",  +			   (std::string)llsd_cert["issuer_name"]["organizationalUnitName"], "Instituto Nacional de Tecnologia da Informacao - ITI"); +		ensure_equals("IssuerName/state", (std::string)llsd_cert["issuer_name"]["stateOrProvinceName"], "DF"); +		ensure_equals("Issuer name string",  +			   (std::string)llsd_cert["issuer_name_string"], "CN=Autoridade Certificadora Raiz Brasileira,ST=DF," +															   "L=Brasilia,OU=Instituto Nacional de Tecnologia da Informacao - ITI,O=ICP-Brasil,C=BR"); +		ensure_equals("subject Name/commonName",  +			   (std::string)llsd_cert["subject_name"]["commonName"], "Autoridade Certificadora Raiz Brasileira"); +		ensure_equals("subject Name/countryName", (std::string)llsd_cert["subject_name"]["countryName"], "BR"); +		ensure_equals("subject Name/localityName", (std::string)llsd_cert["subject_name"]["localityName"], "Brasilia"); +		ensure_equals("subject Name/org name", (std::string)llsd_cert["subject_name"]["organizationName"], "ICP-Brasil"); +		ensure_equals("subjectName/org unit",  +			   (std::string)llsd_cert["subject_name"]["organizationalUnitName"], "Instituto Nacional de Tecnologia da Informacao - ITI"); +		ensure_equals("subjectName/state", (std::string)llsd_cert["subject_name"]["stateOrProvinceName"], "DF"); +		ensure_equals("subject name string",  +			   (std::string)llsd_cert["subject_name_string"], "CN=Autoridade Certificadora Raiz Brasileira,ST=DF," +			                                                    "L=Brasilia,OU=Instituto Nacional de Tecnologia da Informacao - ITI,O=ICP-Brasil,C=BR"); +		 +		ensure_equals("md5 digest", (std::string)llsd_cert["md5_digest"], "96:89:7d:61:d1:55:2b:27:e2:5a:39:b4:2a:6c:44:6f"); +		ensure_equals("serial number", (std::string)llsd_cert["serial_number"], "04"); +		// sha1 digest is giving a weird value, and I've no idea why...feh +		//ensure_equals("sha1 digest", (std::string)llsd_cert["sha1_digest"], "8e:fd:ca:bc:93:e6:1e:92:5d:4d:1d:ed:18:1a:43:20:a4:67:a1:39"); +		ensure_equals("valid from", (std::string)llsd_cert["valid_from"], "2001-11-30T12:58:00Z"); +		ensure_equals("valid to", (std::string)llsd_cert["valid_to"], "2011-11-30T23:59:00Z"); +		LLSD expectedKeyUsage = LLSD::emptyArray(); +		expectedKeyUsage.append(LLSD((std::string)"certSigning")); +		expectedKeyUsage.append(LLSD((std::string)"crlSigning")); +		ensure("key usage", valueCompareLLSD(llsd_cert["keyUsage"], expectedKeyUsage)); +		ensure("basic constraints", (bool)llsd_cert["basicConstraints"]["CA"]); +		 +		ensure("x509 is equal", !X509_cmp(mX509TestCert, test_cert->getOpenSSLX509())); +	} + +	 +	// test protected data +	template<> template<> +	void sechandler_basic_test_object::test<2>() + +	{ +		unsigned char MACAddress[MAC_ADDRESS_BYTES]; +		LLUUID::getNodeID(MACAddress); +		 +		std::string protected_data = "sUSh3wj77NG9oAMyt3XIhaej3KLZhLZWFZvI6rIGmwUUOmmelrRg0NI9rkOj8ZDpTPxpwToaBT5u" +		"GQhakdaGLJznr9bHr4/6HIC1bouKj4n2rs4TL6j2WSjto114QdlNfLsE8cbbE+ghww58g8SeyLQO" +		"nyzXoz+/PBz0HD5SMFDuObccoPW24gmqYySz8YoEWhSwO0pUtEEqOjVRsAJgF5wLAtJZDeuilGsq" +		"4ZT9Y4wZ9Rh8nnF3fDUL6IGamHe1ClXM1jgBu10F6UMhZbnH4C3aJ2E9+LiOntU+l3iCb2MpkEpr" +		"82r2ZAMwIrpnirL/xoYoyz7MJQYwUuMvBPToZJrxNSsjI+S2Z+I3iEJAELMAAA=="; +		 +		std::vector<U8> binary_data(apr_base64_decode_len(protected_data.c_str())); +		apr_base64_decode_binary(&binary_data[0], protected_data.c_str()); + +		LLXORCipher cipher(gMACAddress, MAC_ADDRESS_BYTES); +		cipher.decrypt(&binary_data[0], 16); +		LLXORCipher cipher2(MACAddress, MAC_ADDRESS_BYTES); +		cipher2.encrypt(&binary_data[0], 16); +		std::ofstream temp_file("sechandler_settings.tmp", std::ofstream::binary); +		temp_file.write((const char *)&binary_data[0], binary_data.size()); +		temp_file.close(); + +		LLPointer<LLSecAPIBasicHandler> handler = new LLSecAPIBasicHandler("sechandler_settings.tmp", +																		   "test_password.dat"); +		handler->init();																		 +		// data retrieval for existing data +		LLSD data = handler->getProtectedData("test_data_type", "test_data_id"); + + +		ensure_equals("retrieve existing data1", (std::string)data["data1"], "test_data_1"); +		ensure_equals("retrieve existing data2", (std::string)data["data2"], "test_data_2"); +		ensure_equals("retrieve existing data3", (std::string)data["data3"]["elem1"], "test element1"); +		 +		// data storage +		LLSD store_data = LLSD::emptyMap(); +		store_data["store_data1"] = "test_store_data1"; +		store_data["store_data2"] = 27; +		store_data["store_data3"] = LLSD::emptyMap(); +		store_data["store_data3"]["subelem1"] = "test_subelem1"; +		 +		handler->setProtectedData("test_data_type", "test_data_id1", store_data); +		data = handler->getProtectedData("test_data_type", "test_data_id"); +		 +		data = handler->getProtectedData("test_data_type", "test_data_id"); +		// verify no overwrite of existing data +		ensure_equals("verify no overwrite 1", (std::string)data["data1"], "test_data_1"); +		ensure_equals("verify no overwrite 2", (std::string)data["data2"], "test_data_2"); +		ensure_equals("verify no overwrite 3", (std::string)data["data3"]["elem1"], "test element1"); +		 +		// verify written data is good +		data = handler->getProtectedData("test_data_type", "test_data_id1"); +		ensure_equals("verify stored data1", (std::string)data["store_data1"], "test_store_data1"); +		ensure_equals("verify stored data2", (int)data["store_data2"], 27); +		ensure_equals("verify stored data3", (std::string)data["store_data3"]["subelem1"], "test_subelem1"); +		 +		// verify overwrite works +		handler->setProtectedData("test_data_type", "test_data_id", store_data);	 +		data = handler->getProtectedData("test_data_type", "test_data_id"); +		ensure_equals("verify overwrite stored data1", (std::string)data["store_data1"], "test_store_data1"); +		ensure_equals("verify overwrite stored data2", (int)data["store_data2"], 27); +		ensure_equals("verify overwrite stored data3", (std::string)data["store_data3"]["subelem1"], "test_subelem1"); +		 +		// verify other datatype doesn't conflict +		store_data["store_data3"] = "test_store_data3"; +		store_data["store_data4"] = 28; +		store_data["store_data5"] = LLSD::emptyMap(); +		store_data["store_data5"]["subelem2"] = "test_subelem2"; +		 +		handler->setProtectedData("test_data_type1", "test_data_id", store_data);	 +		data = handler->getProtectedData("test_data_type1", "test_data_id"); +		ensure_equals("verify datatype stored data3", (std::string)data["store_data3"], "test_store_data3"); +		ensure_equals("verify datatype stored data4", (int)data["store_data4"], 28); +		ensure_equals("verify datatype stored data5", (std::string)data["store_data5"]["subelem2"], "test_subelem2");	 +		 +		// test data not found + +		data = handler->getProtectedData("test_data_type1", "test_data_not_found"); +		ensure("not found", data.isUndefined()); + +		// cause a 'write' by using 'LLPointer' to delete then instantiate a handler +		handler = NULL; +		handler = new LLSecAPIBasicHandler("sechandler_settings.tmp", "test_password.dat"); +		handler->init(); + +		data = handler->getProtectedData("test_data_type1", "test_data_id"); +		ensure_equals("verify datatype stored data3a", (std::string)data["store_data3"], "test_store_data3"); +		ensure_equals("verify datatype stored data4a", (int)data["store_data4"], 28); +		ensure_equals("verify datatype stored data5a", (std::string)data["store_data5"]["subelem2"], "test_subelem2");	 +		 +		// rewrite the initial file to verify reloads +		handler = NULL; +		std::ofstream temp_file2("sechandler_settings.tmp", std::ofstream::binary); +		temp_file2.write((const char *)&binary_data[0], binary_data.size()); +		temp_file2.close(); +		 +		// cause a 'write' +		handler = new LLSecAPIBasicHandler("sechandler_settings.tmp", "test_password.dat"); +		handler->init();		 +		data = handler->getProtectedData("test_data_type1", "test_data_id"); +		ensure("not found", data.isUndefined()); +		 +		handler->deleteProtectedData("test_data_type", "test_data_id"); +		ensure("Deleted data not found", handler->getProtectedData("test_data_type", "test_data_id").isUndefined()); +		 +		LLFile::remove("sechandler_settings.tmp"); +		handler = new LLSecAPIBasicHandler("sechandler_settings.tmp", "test_password.dat"); +		handler->init();		 +		data = handler->getProtectedData("test_data_type1", "test_data_id"); +		ensure("not found", data.isUndefined()); +		handler = NULL; +		 +		ensure(LLFile::isfile("sechandler_settings.tmp")); +	} +	 +	// test credenitals +	template<> template<> +	void sechandler_basic_test_object::test<3>() +	{ +		LLPointer<LLSecAPIBasicHandler> handler = new LLSecAPIBasicHandler("sechandler_settings.tmp", "test_password.dat"); +		handler->init(); + +		LLSD my_id = LLSD::emptyMap(); +		LLSD my_authenticator = LLSD::emptyMap(); +		my_id["type"] = "test_type"; +		my_id["username"] = "testuser@lindenlab.com"; +		my_authenticator["type"] = "test_auth"; +		my_authenticator["creds"] = "12345"; + +		// test creation of credentials		 +		LLPointer<LLCredential> my_cred = handler->createCredential("my_grid", my_id, my_authenticator); + +		// test retrieval of credential components +		ensure_equals("basic credential creation: identifier", my_id, my_cred->getIdentifier()); +		ensure_equals("basic credential creation: authenticator", my_authenticator, my_cred->getAuthenticator()); +		ensure_equals("basic credential creation: grid", "my_grid", my_cred->getGrid()); +		 +		// test setting/overwriting of credential components +		my_id["first_name"] = "firstname"; +		my_id.erase("username"); +		my_authenticator.erase("creds"); +		my_authenticator["hash"] = "6563245"; +		 +		my_cred->setCredentialData(my_id, my_authenticator); +		ensure_equals("set credential data: identifier", my_id, my_cred->getIdentifier()); +		ensure_equals("set credential data: authenticator", my_authenticator, my_cred->getAuthenticator()); +		ensure_equals("set credential data: grid", "my_grid", my_cred->getGrid());		 +			 +		// test loading of a credential, that hasn't been saved, without +		// any legacy saved credential data +		LLPointer<LLCredential> my_new_cred = handler->loadCredential("my_grid2"); +		ensure("unknown credential load test", my_new_cred->getIdentifier().isMap()); +		ensure("unknown credential load test", !my_new_cred->getIdentifier().has("type"));		 +		ensure("unknown credential load test", my_new_cred->getAuthenticator().isMap()); +		ensure("unknown credential load test", !my_new_cred->getAuthenticator().has("type"));	 +		// test saving of a credential +		handler->saveCredential(my_cred, true); + +		// test loading of a known credential +		my_new_cred = handler->loadCredential("my_grid"); +		ensure_equals("load a known credential: identifier", my_id, my_new_cred->getIdentifier()); +		ensure_equals("load a known credential: authenticator",my_authenticator, my_new_cred->getAuthenticator()); +		ensure_equals("load a known credential: grid", "my_grid", my_cred->getGrid()); +	 +		// test deletion of a credential +		handler->deleteCredential(my_new_cred); + +		ensure("delete credential: identifier", my_new_cred->getIdentifier().isUndefined()); +		ensure("delete credentialt: authenticator", my_new_cred->getIdentifier().isUndefined()); +		ensure_equals("delete credential: grid", "my_grid", my_cred->getGrid());		 +		// load unknown cred +		 +		my_new_cred = handler->loadCredential("my_grid"); +		ensure("deleted credential load test", my_new_cred->getIdentifier().isMap()); +		ensure("deleted credential load test", !my_new_cred->getIdentifier().has("type"));		 +		ensure("deleted credential load test", my_new_cred->getAuthenticator().isMap()); +		ensure("deleted credential load test", !my_new_cred->getAuthenticator().has("type")); +		 +		// test loading of an unknown credential with legacy saved username, but without +		// saved password +		gFirstName = "myfirstname"; +		gLastName = "mylastname"; +		my_new_cred = handler->loadCredential("my_legacy_grid"); +		ensure_equals("legacy credential with no password: type",  +					  (const std::string)my_new_cred->getIdentifier()["type"], "agent"); +		ensure_equals("legacy credential with no password: first_name",  +					  (const std::string)my_new_cred->getIdentifier()["first_name"], "myfirstname"); +		ensure_equals("legacy credential with no password: last_name", +					  (const std::string)my_new_cred->getIdentifier()["last_name"], "mylastname"); +		 +		ensure("legacy credential with no password: no authenticator", my_new_cred->getAuthenticator().isUndefined()); +		 +		// test loading of an unknown credential with legacy saved password and username + +		std::string hashed_password = "fSQcLG03eyIWJmkzfyYaKm81dSweLmsxeSAYKGE7fSQ=";		 +		int length = apr_base64_decode_len(hashed_password.c_str()); +		std::vector<char> decoded_password(length); +		apr_base64_decode(&decoded_password[0], hashed_password.c_str()); +		unsigned char MACAddress[MAC_ADDRESS_BYTES]; +		LLUUID::getNodeID(MACAddress); +		LLXORCipher cipher(gMACAddress, MAC_ADDRESS_BYTES); +		cipher.decrypt((U8*)&decoded_password[0], length); +		LLXORCipher cipher2(MACAddress, MAC_ADDRESS_BYTES); +		cipher2.encrypt((U8*)&decoded_password[0], length); +		llofstream password_file("test_password.dat", std::ofstream::binary); +		password_file.write(&decoded_password[0], length);  +		password_file.close(); +		 +		my_new_cred = handler->loadCredential("my_legacy_grid2");		 +		ensure_equals("legacy credential with password: type",  +					  (const std::string)my_new_cred->getIdentifier()["type"], "agent"); +		ensure_equals("legacy credential with password: first_name",  +					  (const std::string)my_new_cred->getIdentifier()["first_name"], "myfirstname"); +		ensure_equals("legacy credential with password: last_name", +					  (const std::string)my_new_cred->getIdentifier()["last_name"], "mylastname"); +		 +		LLSD legacy_authenticator = my_new_cred->getAuthenticator(); +		ensure_equals("legacy credential with password: type",  +					  (std::string)legacy_authenticator["type"],  +					  "hash"); +		ensure_equals("legacy credential with password: algorithm",  +					  (std::string)legacy_authenticator["algorithm"],  +					  "md5");	 +		ensure_equals("legacy credential with password: algorithm",  +					  (std::string)legacy_authenticator["secret"],  +					  "01234567890123456789012345678901"); +		 +		// test creation of credentials		 +		my_cred = handler->createCredential("mysavedgrid", my_id, my_authenticator); +		// test save without saving authenticator. 		 +		handler->saveCredential(my_cred, FALSE); +		my_new_cred = handler->loadCredential("mysavedgrid");	 +		ensure_equals("saved credential without auth",  +					  (const std::string)my_new_cred->getIdentifier()["type"], "test_type"); +		ensure("no authenticator values were saved", my_new_cred->getAuthenticator().isUndefined()); +	} + +	// test cert vector +	template<> template<> +	void sechandler_basic_test_object::test<4>() +	{ +		 +		// validate create from empty vector +		LLPointer<LLBasicCertificateVector> test_vector = new LLBasicCertificateVector(); +		ensure_equals("when loading with nothing, we should result in no certs in vector", test_vector->size(), 0); +		 +		test_vector->add(new LLBasicCertificate(mPemTestCert)); +		ensure_equals("one element in vector", test_vector->size(), 1); +		test_vector->add(new LLBasicCertificate(mPemChildCert)); +		ensure_equals("two elements in vector after add", test_vector->size(), 2); +		 +		test_vector->add(new LLBasicCertificate(mPemChildCert)); +		ensure_equals("two elements in vector after re-add", test_vector->size(), 2); +		// validate order +		X509* test_cert = (*test_vector)[0]->getOpenSSLX509();		 +		ensure("first cert added remains first cert", !X509_cmp(test_cert, mX509TestCert)); +		X509_free(test_cert); +		 +		test_cert = (*test_vector)[1]->getOpenSSLX509();	 +		ensure("adding a duplicate cert", !X509_cmp(test_cert, mX509ChildCert)); +		X509_free(test_cert);		 +		 +		// +		// validate iterator +		// +		LLBasicCertificateVector::iterator current_cert = test_vector->begin(); +		LLBasicCertificateVector::iterator copy_current_cert = current_cert; +		// operator++(int) +		ensure("validate iterator++ element in vector is expected cert", *current_cert++ == (*test_vector)[0]); +		ensure("validate 2nd iterator++ element in vector is expected cert", *current_cert++ == (*test_vector)[1]); +		ensure("validate end iterator++", current_cert == test_vector->end()); +		 +		// copy  +		ensure("validate copy iterator element in vector is expected cert", *copy_current_cert == (*test_vector)[0]);		 +		 +		// operator--(int) +		current_cert--; +		ensure("validate iterator-- element in vector is expected cert", *current_cert-- == (*test_vector)[1]);		 +		ensure("validate iterator-- element in vector is expected cert", *current_cert == (*test_vector)[0]); +		 +		ensure("begin iterator is equal", current_cert == test_vector->begin()); +		 +		// operator++ +		ensure("validate ++iterator element in vector is expected cert", *++current_cert == (*test_vector)[1]);				 +		ensure("end of cert vector after ++iterator", ++current_cert == test_vector->end()); +		// operator-- +		ensure("validate --iterator element in vector is expected cert", *--current_cert == (*test_vector)[1]);		 +		ensure("validate 2nd --iterator element in vector is expected cert", *--current_cert == (*test_vector)[0]);		 +		 +		// validate remove +		// validate create from empty vector +		test_vector = new LLBasicCertificateVector(); +		test_vector->add(new LLBasicCertificate(mPemTestCert)); +		test_vector->add(new LLBasicCertificate(mPemChildCert)); +		test_vector->erase(test_vector->begin()); +		ensure_equals("one element in store after remove", test_vector->size(), 1); +		test_cert = (*test_vector)[0]->getOpenSSLX509(); +		ensure("validate cert was removed", !X509_cmp(test_cert, mX509ChildCert)); +		X509_free(test_cert); +		 +		// validate insert +		test_vector->insert(test_vector->begin(), new LLBasicCertificate(mPemChildCert)); +		test_cert = (*test_vector)[0]->getOpenSSLX509(); +		 +		ensure("validate cert was inserted", !X509_cmp(test_cert, mX509ChildCert)); +		X509_free(test_cert);	 + +		//validate find +		LLSD find_info = LLSD::emptyMap(); +		test_vector->insert(test_vector->begin(), new LLBasicCertificate(mPemRootCert)); +		find_info["issuer_name"] = LLSD::emptyMap(); +		find_info["issuer_name"]["commonName"] = "Roxies intermediate CA"; +		find_info["md5_digest"] = "97:24:c7:4c:d4:ba:2d:0e:9c:a1:18:8e:3a:c6:1f:c3"; +		current_cert = test_vector->find(find_info); +		ensure("found", current_cert != test_vector->end()); +		ensure("found cert", (*current_cert).get() == (*test_vector)[1].get()); +		find_info["sha1_digest"] = "bad value"; +		current_cert =test_vector->find(find_info); +		ensure("didn't find cert", current_cert == test_vector->end());		 +	}	 +	 +	// test cert store +	template<> template<> +	void sechandler_basic_test_object::test<5>() +	{ +		// validate load with nothing +		LLFile::remove("mycertstore.pem"); +		LLPointer<LLBasicCertificateStore> test_store = new LLBasicCertificateStore("mycertstore.pem"); +		ensure_equals("when loading with nothing, we should result in no certs in store", test_store->size(), 0); +		 +		// validate load with empty file +		test_store->save(); +		test_store = NULL; +		test_store = new LLBasicCertificateStore("mycertstore.pem"); +		ensure_equals("when loading with nothing, we should result in no certs in store", test_store->size(), 0); +		test_store=NULL; +		 +		// instantiate a cert store from a file +		llofstream certstorefile("mycertstore.pem", std::ios::out); +		certstorefile << mPemChildCert << std::endl << mPemTestCert << std::endl; +		certstorefile.close(); +		// validate loaded certs +		test_store = new LLBasicCertificateStore("mycertstore.pem"); +		ensure_equals("two elements in store", test_store->size(), 2); +		 +		// operator[] +		X509* test_cert = (*test_store)[0]->getOpenSSLX509(); + +		ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); +		X509_free(test_cert); +		test_cert = (*test_store)[1]->getOpenSSLX509(); +		ensure("validate second element in store is expected cert", !X509_cmp(test_cert, mX509TestCert));	 +		X509_free(test_cert); + + +		// validate save +		LLFile::remove("mycertstore.pem"); +		test_store->save(); +		test_store = NULL; +		test_store = new LLBasicCertificateStore("mycertstore.pem"); +		ensure_equals("two elements in store after save", test_store->size(), 2);				 +		LLCertificateStore::iterator current_cert = test_store->begin();		 +		test_cert = (*current_cert)->getOpenSSLX509(); +		ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); +		current_cert++; +		X509_free(test_cert); +		test_cert = (*current_cert)->getOpenSSLX509(); +		ensure("validate second element in store is expected cert", !X509_cmp(test_cert, mX509TestCert));	 +		X509_free(test_cert); +		current_cert++; +		ensure("end of cert store", current_cert == test_store->end()); +		 +	} +	 +	// cert name wildcard matching +	template<> template<> +	void sechandler_basic_test_object::test<6>() +	{ +		ensure("simple name match",  +			   _cert_hostname_wildcard_match("foo", "foo")); +		 +		ensure("simple name match, with end period",  +			   _cert_hostname_wildcard_match("foo.", "foo.")); +		 +		ensure("simple name match, with begin period",  +			   _cert_hostname_wildcard_match(".foo", ".foo"));		 +		 +		ensure("simple name match, with subdomain",  +			   _cert_hostname_wildcard_match("foo.bar", "foo.bar"));	 +		 +		ensure("stutter name match",  +			   _cert_hostname_wildcard_match("foobbbbfoo", "foo*bbbfoo"));			 +		 +		ensure("simple name match, with beginning wildcard",  +			   _cert_hostname_wildcard_match("foobar", "*bar"));	 +		 +		ensure("simple name match, with ending wildcard",  +			   _cert_hostname_wildcard_match("foobar", "foo*")); +		 +		ensure("simple name match, with beginning null wildcard",  +			   _cert_hostname_wildcard_match("foobar", "*foobar"));			 + +		ensure("simple name match, with ending null wildcard",  +			   _cert_hostname_wildcard_match("foobar", "foobar*")); +		 +		ensure("simple name match, with embedded wildcard",  +			   _cert_hostname_wildcard_match("foobar", "f*r"));		 +		 +		ensure("simple name match, with embedded null wildcard",  +			   _cert_hostname_wildcard_match("foobar", "foo*bar")); + +		ensure("simple name match, with dual embedded wildcard",  +			   _cert_hostname_wildcard_match("foobar", "f*o*ar"));		 + +		ensure("simple name mismatch",  +			   !_cert_hostname_wildcard_match("bar", "foo")); +		 +		ensure("simple name mismatch, with end period",  +			   !_cert_hostname_wildcard_match("foobar.", "foo.")); +		 +		ensure("simple name mismatch, with begin period",  +			   !_cert_hostname_wildcard_match(".foobar", ".foo"));		 +		 +		ensure("simple name mismatch, with subdomain",  +			   !_cert_hostname_wildcard_match("foobar.bar", "foo.bar"));	 +		 +		ensure("simple name mismatch, with beginning wildcard",  +			   !_cert_hostname_wildcard_match("foobara", "*bar"));	 +		 +		ensure("simple name mismatch, with ending wildcard",  +			   !_cert_hostname_wildcard_match("oobar", "foo*")); +		 +		ensure("simple name mismatch, with embedded wildcard",  +			   !_cert_hostname_wildcard_match("oobar", "f*r"));		 +		 +		ensure("simple name mismatch, with dual embedded wildcard",  +			   !_cert_hostname_wildcard_match("foobar", "f*d*ar")); +		 +		ensure("simple wildcard",  +			   _cert_hostname_wildcard_match("foobar", "*")); +		 +		ensure("long domain",  +			   _cert_hostname_wildcard_match("foo.bar.com", "foo.bar.com")); +		 +		ensure("long domain with multiple wildcards",  +			   _cert_hostname_wildcard_match("foo.bar.com", "*.b*r.com"));	 + +		ensure("end periods",  +			   _cert_hostname_wildcard_match("foo.bar.com.", "*.b*r.com."));	 +		 +		ensure("mismatch end period",  +			   !_cert_hostname_wildcard_match("foo.bar.com.", "*.b*r.com")); +		 +		ensure("mismatch end period2",  +			   !_cert_hostname_wildcard_match("foo.bar.com", "*.b*r.com."));				 +	} +	 +	// test cert chain +	template<> template<> +	void sechandler_basic_test_object::test<7>() +	{ +		// validate create from empty chain +		LLPointer<LLBasicCertificateChain> test_chain = new LLBasicCertificateChain(NULL); +		ensure_equals("when loading with nothing, we should result in no certs in chain", test_chain->size(), 0); + +		// Single cert in the chain. +		X509_STORE_CTX *test_store = X509_STORE_CTX_new(); +		test_store->cert = mX509ChildCert;		 +		test_store->untrusted = NULL; +		test_chain = new LLBasicCertificateChain(test_store); +		X509_STORE_CTX_free(test_store); +		ensure_equals("two elements in store", test_chain->size(), 1);		 +		X509* test_cert = (*test_chain)[0]->getOpenSSLX509(); +		ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); +		X509_free(test_cert);		 +		 +		// cert + CA +		 +		test_store = X509_STORE_CTX_new(); +		test_store->cert = mX509ChildCert; +		test_store->untrusted = sk_X509_new_null(); +		sk_X509_push(test_store->untrusted, mX509IntermediateCert); +		test_chain = new LLBasicCertificateChain(test_store); +		X509_STORE_CTX_free(test_store); +		ensure_equals("two elements in store", test_chain->size(), 2);	 +		test_cert = (*test_chain)[0]->getOpenSSLX509(); +		ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); +		X509_free(test_cert); +		test_cert = (*test_chain)[1]->getOpenSSLX509(); +		ensure("validate second element in store is expected cert", !X509_cmp(test_cert, mX509IntermediateCert));	 +		X509_free(test_cert); + +		// cert + nonrelated +		 +		test_store = X509_STORE_CTX_new(); +		test_store->cert = mX509ChildCert; +		test_store->untrusted = sk_X509_new_null(); +		sk_X509_push(test_store->untrusted, mX509TestCert); +		test_chain = new LLBasicCertificateChain(test_store); +		X509_STORE_CTX_free(test_store); +		ensure_equals("two elements in store", test_chain->size(), 1);	 +		test_cert = (*test_chain)[0]->getOpenSSLX509(); +		ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); +		X509_free(test_cert); +		 +		// cert + CA + nonrelated +		test_store = X509_STORE_CTX_new(); +		test_store->cert = mX509ChildCert; +		test_store->untrusted = sk_X509_new_null(); +		sk_X509_push(test_store->untrusted, mX509IntermediateCert); +		sk_X509_push(test_store->untrusted, mX509TestCert); +		test_chain = new LLBasicCertificateChain(test_store); +		X509_STORE_CTX_free(test_store); +		ensure_equals("two elements in store", test_chain->size(), 2);	 +		test_cert = (*test_chain)[0]->getOpenSSLX509(); +		ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); +		X509_free(test_cert); +		test_cert = (*test_chain)[1]->getOpenSSLX509(); +		ensure("validate second element in store is expected cert", !X509_cmp(test_cert, mX509IntermediateCert));	 +		X509_free(test_cert); + +		// cert + intermediate + CA  +		test_store = X509_STORE_CTX_new(); +		test_store->cert = mX509ChildCert; +		test_store->untrusted = sk_X509_new_null(); +		sk_X509_push(test_store->untrusted, mX509IntermediateCert); +		sk_X509_push(test_store->untrusted, mX509RootCert); +		test_chain = new LLBasicCertificateChain(test_store); +		X509_STORE_CTX_free(test_store); +		ensure_equals("three elements in store", test_chain->size(), 3);	 +		test_cert = (*test_chain)[0]->getOpenSSLX509(); +		ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); +		X509_free(test_cert); +		test_cert = (*test_chain)[1]->getOpenSSLX509(); +		ensure("validate second element in store is expected cert", !X509_cmp(test_cert, mX509IntermediateCert));	 +		X509_free(test_cert); + +		test_cert = (*test_chain)[2]->getOpenSSLX509(); +		ensure("validate second element in store is expected cert", !X509_cmp(test_cert, mX509RootCert));	 +		X509_free(test_cert);		 +	} +	// test cert validation +	template<> template<> +	void sechandler_basic_test_object::test<8>() +	{ +		// start with a trusted store with our known root cert +		LLFile::remove("mycertstore.pem"); +		LLPointer<LLBasicCertificateStore> test_store = new LLBasicCertificateStore("mycertstore.pem"); +		test_store->add(new LLBasicCertificate(mX509RootCert)); +		LLSD validation_params; +		 +		// validate basic trust for a chain containing only the intermediate cert.  (1 deep) +		LLPointer<LLBasicCertificateChain> test_chain = new LLBasicCertificateChain(NULL); + +		test_chain->add(new LLBasicCertificate(mX509IntermediateCert)); + +		test_chain->validate(0, test_store, validation_params); + +		// add the root certificate to the chain and revalidate +		test_chain->add(new LLBasicCertificate(mX509RootCert));	 +		test_chain->validate(0, test_store, validation_params); + +		// add the child cert at the head of the chain, and revalidate (3 deep chain) +		test_chain->insert(test_chain->begin(), new LLBasicCertificate(mX509ChildCert)); +		test_chain->validate(0, test_store, validation_params); + +		// basic failure cases +		test_chain = new LLBasicCertificateChain(NULL); +		//validate with only the child cert +		test_chain->add(new LLBasicCertificate(mX509ChildCert)); +		ensure_throws("no CA, with only a child cert",  +					  LLCertValidationTrustException,  +					  (*test_chain)[0], +					  test_chain->validate,  +					  VALIDATION_POLICY_TRUSTED,  +					  test_store,  +					  validation_params); + + +		// validate without the trust flag. +		test_chain->validate(0, test_store, validation_params);		 + +		// clear out the store +		test_store = new LLBasicCertificateStore("mycertstore.pem"); +		// append the intermediate cert +		test_chain->add(new LLBasicCertificate(mX509IntermediateCert));		 +		ensure_throws("no CA, with child and intermediate certs",  +					  LLCertValidationTrustException,  +					  (*test_chain)[1], +					  test_chain->validate,  +					  VALIDATION_POLICY_TRUSTED,  +					  test_store,  +					  validation_params); +		// validate without the trust flag +		test_chain->validate(0, test_store, validation_params); + +		// Test time validity +		LLSD child_info = (*test_chain)[0]->getLLSD(); +		validation_params = LLSD::emptyMap(); +		validation_params[CERT_VALIDATION_DATE] = LLDate(child_info[CERT_VALID_FROM].asDate().secondsSinceEpoch() + 1.0);   +		test_chain->validate(VALIDATION_POLICY_TIME, test_store, validation_params); + +		validation_params = LLSD::emptyMap();		 +		validation_params[CERT_VALIDATION_DATE] = child_info[CERT_VALID_FROM].asDate(); +		 +		validation_params[CERT_VALIDATION_DATE] = LLDate(child_info[CERT_VALID_FROM].asDate().secondsSinceEpoch() - 1.0); + 		 +		// test not yet valid +		ensure_throws("Child cert not yet valid",  +					  LLCertValidationExpirationException,  +					  (*test_chain)[0], +					  test_chain->validate,  +					  VALIDATION_POLICY_TIME,  +					  test_store,  +					  validation_params);	 +		validation_params = LLSD::emptyMap();		 +		validation_params[CERT_VALIDATION_DATE] = LLDate(child_info[CERT_VALID_TO].asDate().secondsSinceEpoch() + 1.0); + 		 +		// test cert expired +		ensure_throws("Child cert expired",  +					  LLCertValidationExpirationException,  +					  (*test_chain)[0], +					  test_chain->validate,  +					  VALIDATION_POLICY_TIME,  +					  test_store,  +					  validation_params); + +		// test SSL KU +		// validate basic trust for a chain containing child and intermediate. +		test_chain = new LLBasicCertificateChain(NULL); +		test_chain->add(new LLBasicCertificate(mX509ChildCert)); +		test_chain->add(new LLBasicCertificate(mX509IntermediateCert)); +		test_chain->validate(VALIDATION_POLICY_SSL_KU, test_store, validation_params);	 + +		test_chain = new LLBasicCertificateChain(NULL); +		test_chain->add(new LLBasicCertificate(mX509TestCert)); + +		ensure_throws("Cert doesn't have ku",  +					  LLCertKeyUsageValidationException,  +					  (*test_chain)[0], +					  test_chain->validate,  +					  VALIDATION_POLICY_SSL_KU,  +					  test_store,  +					  validation_params); +	} +}; + diff --git a/indra/newview/tests/llslurl_test.cpp b/indra/newview/tests/llslurl_test.cpp new file mode 100644 index 0000000000..803020dc7a --- /dev/null +++ b/indra/newview/tests/llslurl_test.cpp @@ -0,0 +1,258 @@ +/**  + * @file llsecapi_test.cpp + * @author Roxie + * @date 2009-02-10 + * @brief Test the sec api functionality + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + *  + * Copyright (c) 2009, Linden Research, Inc. + *  + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version maps.secondlife.com2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab.  Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + *  + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + *  + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + *  + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ +#include "../llviewerprecompiledheaders.h" +#include "../llviewernetwork.h" +#include "../test/lltut.h" +#include "../llslurl.h" +#include "../../llxml/llcontrol.h" +#include "llsdserialize.h" +//----------------------------------------------------------------------------                +// Mock objects for the dependencies of the code we're testing                                + +LLControlGroup::LLControlGroup(const std::string& name) +: LLInstanceTracker<LLControlGroup, std::string>(name) {} +LLControlGroup::~LLControlGroup() {} +BOOL LLControlGroup::declareString(const std::string& name, +                                   const std::string& initial_val, +                                   const std::string& comment, +                                   BOOL persist) {return TRUE;} +void LLControlGroup::setString(const std::string& name, const std::string& val){} + +std::string gCmdLineLoginURI; +std::string gCmdLineGridChoice; +std::string gCmdLineHelperURI; +std::string gLoginPage; +std::string gCurrentGrid; +std::string LLControlGroup::getString(const std::string& name) +{ +	if (name == "CmdLineGridChoice") +		return gCmdLineGridChoice; +	else if (name == "CmdLineHelperURI") +		return gCmdLineHelperURI; +	else if (name == "LoginPage") +		return gLoginPage; +	else if (name == "CurrentGrid") +		return gCurrentGrid; +	return ""; +} + +LLSD LLControlGroup::getLLSD(const std::string& name) +{ +	if (name == "CmdLineLoginURI") +	{ +		if(!gCmdLineLoginURI.empty()) +		{ +			return LLSD(gCmdLineLoginURI); +		} +	} +	return LLSD(); +} + + +LLControlGroup gSavedSettings("test"); + +// ------------------------------------------------------------------------------------------- +// TUT +// ------------------------------------------------------------------------------------------- +namespace tut +{ +	// Test wrapper declaration : wrapping nothing for the moment +	struct slurlTest +	{ +		slurlTest() +		{	 +			LLGridManager::getInstance()->initialize(std::string("")); +		} +		~slurlTest() +		{ +		} +	}; +	 +	// Tut templating thingamagic: test group, object and test instance +	typedef test_group<slurlTest> slurlTestFactory; +	typedef slurlTestFactory::object slurlTestObject; +	tut::slurlTestFactory tut_test("llslurl"); +	 +	// --------------------------------------------------------------------------------------- +	// Test functions  +	// --------------------------------------------------------------------------------------- +	// construction from slurl string +	template<> template<> +	void slurlTestObject::test<1>() +	{ +		LLGridManager::getInstance()->setGridChoice("util.agni.lindenlab.com"); +		 +		LLSLURL slurl = LLSLURL(""); +		ensure_equals("null slurl", (int)slurl.getType(), LLSLURL::LAST_LOCATION); +		 +		slurl = LLSLURL("http://slurl.com/secondlife/myregion"); +		ensure_equals("slurl.com slurl, region only - type", slurl.getType(), LLSLURL::LOCATION); +		ensure_equals("slurl.com slurl, region only", slurl.getSLURLString(),  +					  "http://maps.secondlife.com/secondlife/myregion/128/128/0"); +		 +		slurl = LLSLURL("http://maps.secondlife.com/secondlife/myregion/1/2/3"); +		ensure_equals("maps.secondlife.com slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); +		ensure_equals("maps.secondlife.com slurl, region + coords", slurl.getSLURLString(),  +					  "http://maps.secondlife.com/secondlife/myregion/1/2/3"); + +		slurl = LLSLURL("secondlife://myregion"); +		ensure_equals("secondlife: slurl, region only - type", slurl.getType(), LLSLURL::LOCATION); +		ensure_equals("secondlife: slurl, region only", slurl.getSLURLString(),  +					  "http://maps.secondlife.com/secondlife/myregion/128/128/0"); +		 +		slurl = LLSLURL("secondlife://myregion/1/2/3"); +		ensure_equals("secondlife: slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); +		ensure_equals("secondlife slurl, region + coords", slurl.getSLURLString(),  +					  "http://maps.secondlife.com/secondlife/myregion/1/2/3"); +		 +		slurl = LLSLURL("/myregion"); +		ensure_equals("/region slurl, region- type", slurl.getType(), LLSLURL::LOCATION); +		ensure_equals("/region slurl, region ", slurl.getSLURLString(),  +					  "http://maps.secondlife.com/secondlife/myregion/128/128/0"); +		 +		slurl = LLSLURL("/myregion/1/2/3"); +		ensure_equals("/: slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); +		ensure_equals("/ slurl, region + coords", slurl.getSLURLString(),  +					  "http://maps.secondlife.com/secondlife/myregion/1/2/3");	 +		 +		slurl = LLSLURL("my region/1/2/3"); +		ensure_equals(" slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); +		ensure_equals(" slurl, region + coords", slurl.getSLURLString(),  +					  "http://maps.secondlife.com/secondlife/my%20region/1/2/3");	 +		 +		slurl = LLSLURL("https://my.grid.com/region/my%20region/1/2/3"); +		ensure_equals("grid slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); +		ensure_equals("grid slurl, region + coords", slurl.getSLURLString(),  +					  "https://my.grid.com/region/my%20region/1/2/3");	 +		 +		slurl = LLSLURL("https://my.grid.com/region/my region"); +		ensure_equals("grid slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); +		ensure_equals("grid slurl, region + coords", slurl.getSLURLString(),  +					  "https://my.grid.com/region/my%20region/128/128/0"); +		 +		LLGridManager::getInstance()->setGridChoice("foo.bar.com");		 +		slurl = LLSLURL("/myregion/1/2/3"); +		ensure_equals("/: slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); +		ensure_equals("/ slurl, region + coords", slurl.getSLURLString(),  +					  "https://foo.bar.com/region/myregion/1/2/3");		 +		 +		slurl = LLSLURL("myregion/1/2/3"); +		ensure_equals(": slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); +		ensure_equals(" slurl, region + coords", slurl.getSLURLString(),  +					  "https://foo.bar.com/region/myregion/1/2/3");		 +		 +		slurl = LLSLURL(LLSLURL::SIM_LOCATION_HOME); +		ensure_equals("home", slurl.getType(), LLSLURL::HOME_LOCATION); + +		slurl = LLSLURL(LLSLURL::SIM_LOCATION_LAST); +		ensure_equals("last", slurl.getType(), LLSLURL::LAST_LOCATION); +		 +		slurl = LLSLURL("secondlife:///app/foo/bar?12345"); +		ensure_equals("app", slurl.getType(), LLSLURL::APP);		 +		ensure_equals("appcmd", slurl.getAppCmd(), "foo"); +		ensure_equals("apppath", slurl.getAppPath().size(), 1); +		ensure_equals("apppath2", slurl.getAppPath()[0].asString(), "bar"); +		ensure_equals("appquery", slurl.getAppQuery(), "12345"); +		ensure_equals("grid1", "foo.bar.com", slurl.getGrid()); +	 +		slurl = LLSLURL("secondlife://Aditi/app/foo/bar?12345"); +		ensure_equals("app", slurl.getType(), LLSLURL::APP);		 +		ensure_equals("appcmd", slurl.getAppCmd(), "foo"); +		ensure_equals("apppath", slurl.getAppPath().size(), 1); +		ensure_equals("apppath2", slurl.getAppPath()[0].asString(), "bar"); +		ensure_equals("appquery", slurl.getAppQuery(), "12345"); +		ensure_equals("grid2", "util.aditi.lindenlab.com", slurl.getGrid());		 + +		LLGridManager::getInstance()->setGridChoice("foo.bar.com");			 +		slurl = LLSLURL("secondlife:///secondlife/myregion/1/2/3"); +		ensure_equals("/: slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); +		ensure_equals("location", slurl.getType(), LLSLURL::LOCATION); +		ensure_equals("region" , "myregion", slurl.getRegion()); +		ensure_equals("grid3", "util.agni.lindenlab.com", slurl.getGrid()); +				 +		slurl = LLSLURL("secondlife://Aditi/secondlife/myregion/1/2/3"); +		ensure_equals("/: slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); +		ensure_equals("location", slurl.getType(), LLSLURL::LOCATION); +		ensure_equals("region" , "myregion", slurl.getRegion()); +		ensure_equals("grid4", "util.aditi.lindenlab.com", slurl.getGrid());		 +		 +		slurl = LLSLURL("https://my.grid.com/app/foo/bar?12345"); +		ensure_equals("app", slurl.getType(), LLSLURL::APP);		 +		ensure_equals("appcmd", slurl.getAppCmd(), "foo"); +		ensure_equals("apppath", slurl.getAppPath().size(), 1); +		ensure_equals("apppath2", slurl.getAppPath()[0].asString(), "bar"); +		ensure_equals("appquery", slurl.getAppQuery(), "12345");	 +		 +	} +	 +	// construction from grid/region/vector combos +	template<> template<> +	void slurlTestObject::test<2>() +	{ +		LLSLURL slurl = LLSLURL("mygrid.com", "my region"); +		ensure_equals("grid/region - type", slurl.getType(), LLSLURL::LOCATION); +		ensure_equals("grid/region", slurl.getSLURLString(),  +					  "https://mygrid.com/region/my%20region/128/128/0");	 +		 +		slurl = LLSLURL("mygrid.com", "my region", LLVector3(1,2,3)); +		ensure_equals("grid/region/vector - type", slurl.getType(), LLSLURL::LOCATION); +		ensure_equals(" grid/region/vector", slurl.getSLURLString(),  +					  "https://mygrid.com/region/my%20region/1/2/3");			 + +		LLGridManager::getInstance()->setGridChoice("foo.bar.com.bar");			 +		slurl = LLSLURL("my region", LLVector3(1,2,3)); +		ensure_equals("grid/region/vector - type", slurl.getType(), LLSLURL::LOCATION); +		ensure_equals(" grid/region/vector", slurl.getSLURLString(),  +					  "https://foo.bar.com.bar/region/my%20region/1/2/3");	 +		 +		LLGridManager::getInstance()->setGridChoice("util.agni.lindenlab.com");	 +		slurl = LLSLURL("my region", LLVector3(1,2,3)); +		ensure_equals("default grid/region/vector - type", slurl.getType(), LLSLURL::LOCATION); +		ensure_equals(" default grid/region/vector", slurl.getSLURLString(),  +					  "http://maps.secondlife.com/secondlife/my%20region/1/2/3");	 +		 +	} +	// Accessors +	template<> template<> +	void slurlTestObject::test<3>() +	{ +		LLSLURL slurl = LLSLURL("https://my.grid.com/region/my%20region/1/2/3"); +		ensure_equals("login string", slurl.getLoginString(), "uri:my region&1&2&3"); +		ensure_equals("location string", slurl.getLocationString(), "my region/1/2/3"); +		ensure_equals("grid", slurl.getGrid(), "my.grid.com"); +		ensure_equals("region", slurl.getRegion(), "my region"); +		ensure_equals("position", slurl.getPosition(), LLVector3(1, 2, 3)); +		 +	} +} diff --git a/indra/newview/tests/llviewernetwork_test.cpp b/indra/newview/tests/llviewernetwork_test.cpp new file mode 100644 index 0000000000..f9f42cfc86 --- /dev/null +++ b/indra/newview/tests/llviewernetwork_test.cpp @@ -0,0 +1,486 @@ +/**  + * @file llviewernetwork_test.cpp + * @author Roxie + * @date 2009-03-9 + * @brief Test the viewernetwork functionality + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + *  + * Copyright (c) 2009, Linden Research, Inc. + *  + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden LregisterSecAPIab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab.  Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + *  + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + *  + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + *  + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ +#include "../llviewerprecompiledheaders.h" +#include "../llviewernetwork.h" +#include "../test/lltut.h" +#include "../../llxml/llcontrol.h" +#include "llfile.h" + +//----------------------------------------------------------------------------                +// Mock objects for the dependencies of the code we're testing                                + +LLControlGroup::LLControlGroup(const std::string& name) +: LLInstanceTracker<LLControlGroup, std::string>(name) {} +LLControlGroup::~LLControlGroup() {} +BOOL LLControlGroup::declareString(const std::string& name, +                                   const std::string& initial_val, +                                   const std::string& comment, +                                   BOOL persist) {return TRUE;} +void LLControlGroup::setString(const std::string& name, const std::string& val){} + +std::string gCmdLineLoginURI; +std::string gCmdLineGridChoice; +std::string gCmdLineHelperURI; +std::string gLoginPage; +std::string gCurrentGrid; +std::string LLControlGroup::getString(const std::string& name) +{ +	if (name == "CmdLineGridChoice") +		return gCmdLineGridChoice; +	else if (name == "CmdLineHelperURI") +		return gCmdLineHelperURI; +	else if (name == "LoginPage") +		return gLoginPage; +	else if (name == "CurrentGrid") +		return gCurrentGrid; +	return ""; +} + +LLSD LLControlGroup::getLLSD(const std::string& name) +{ +	if (name == "CmdLineLoginURI") +	{ +		if(!gCmdLineLoginURI.empty()) +		{ +			return LLSD(gCmdLineLoginURI); +		} +	} +	return LLSD(); +} + + +LLControlGroup gSavedSettings("test"); + +const char *gSampleGridFile = "<llsd><map>" +"<key>grid1</key><map>" +"  <key>favorite</key><integer>1</integer>" +"  <key>helper_uri</key><string>https://helper1/helpers/</string>" +"  <key>label</key><string>mylabel</string>" +"  <key>login_page</key><string>loginpage</string>" +"  <key>login_uri</key><array><string>myloginuri</string></array>" +"  <key>name</key><string>grid1</string>" +"  <key>visible</key><integer>1</integer>" +"  <key>credential_type</key><string>agent</string>" +"  <key>grid_login_id</key><string>MyGrid</string>" +"</map>" +"<key>util.agni.lindenlab.com</key><map>" +"  <key>favorite</key><integer>1</integer>" +"  <key>helper_uri</key><string>https://helper1/helpers/</string>" +"  <key>label</key><string>mylabel</string>" +"  <key>login_page</key><string>loginpage</string>" +"  <key>login_uri</key><array><string>myloginuri</string></array>" +"  <key>name</key><string>util.agni.lindenlab.com</string>" +"</map></map></llsd>"; +// ------------------------------------------------------------------------------------------- +// TUT +// ------------------------------------------------------------------------------------------- +namespace tut +{ +  // Test wrapper declaration : wrapping nothing for the moment +  struct viewerNetworkTest +	{ +		viewerNetworkTest() +		{ +			LLFile::remove("grid_test.xml"); +			gCmdLineLoginURI.clear(); +			gCmdLineGridChoice.clear(); +			gCmdLineHelperURI.clear(); +			gLoginPage.clear(); +			gCurrentGrid.clear();			 +		} +		~viewerNetworkTest() +		{ +			LLFile::remove("grid_test.xml"); +		} +	}; +	 +	// Tut templating thingamagic: test group, object and test instance +	typedef test_group<viewerNetworkTest> viewerNetworkTestFactory; +	typedef viewerNetworkTestFactory::object viewerNetworkTestObject; +	tut::viewerNetworkTestFactory tut_test("llviewernetwork"); +	 +	// --------------------------------------------------------------------------------------- +	// Test functions  +	// --------------------------------------------------------------------------------------- +	// initialization without a grid file +	template<> template<> +	void viewerNetworkTestObject::test<1>() +	{ + +		LLGridManager *manager = LLGridManager::getInstance(); +		// grid file doesn't exist +		manager->initialize("grid_test.xml"); +		// validate that some of the defaults are available. +		std::map<std::string, std::string> known_grids = manager->getKnownGrids(); +#ifndef LL_RELEASE_FOR_DOWNLOAD +		ensure_equals("Known grids is a string-string map of size 18", known_grids.size(), 18); +		ensure_equals("Agni has the right name and label",  +					  known_grids[std::string("util.agni.lindenlab.com")], std::string("Agni")); +#else // LL_RELEASE_FOR_DOWNLOAD +		ensure_equals("Known grids is a string-string map of size 2", known_grids.size(), 2); +		ensure_equals("Agni has the right name and label",  +					  known_grids[std::string("util.agni.lindenlab.com")], std::string("Secondlife.com"));		 +#endif // LL_RELEASE_FOR_DOWNLOAD + + +		ensure_equals("None exists", known_grids[""], "None"); +		 +		LLSD grid = LLGridManager::getInstance()->getGridInfo("util.agni.lindenlab.com"); +		ensure("Grid info for agni is a map", grid.isMap()); +		ensure_equals("name is correct for agni",  +					  grid[GRID_VALUE].asString(), std::string("util.agni.lindenlab.com")); +#ifndef LL_RELEASE_FOR_DOWNLOAD		 +		ensure_equals("label is correct for agni",  +					  grid[GRID_LABEL_VALUE].asString(), std::string("Agni")); +#else // LL_RELEASE_FOR_DOWNLOAD +		ensure_equals("label is correct for agni",  +					  grid[GRID_LABEL_VALUE].asString(), std::string("Secondlife.com"));		 +#endif // LL_RELEASE_FOR_DOWNLOAD +		ensure("Login URI is an array",  +			   grid[GRID_LOGIN_URI_VALUE].isArray()); +		ensure_equals("Agni login uri is correct",  +					  grid[GRID_LOGIN_URI_VALUE][0].asString(),  +					  std::string("https://login.agni.lindenlab.com/cgi-bin/login.cgi")); +		ensure_equals("Agni helper uri is correct", +					  grid[GRID_HELPER_URI_VALUE].asString(),  +					  std::string("https://secondlife.com/helpers/")); +		ensure_equals("Agni login page is correct", +					  grid[GRID_LOGIN_PAGE_VALUE].asString(),  +					  std::string("http://secondlife.com/app/login/")); +		ensure("Agni is a favorite", +			   grid.has(GRID_IS_FAVORITE_VALUE)); +		ensure("Agni is a system grid",  +			   grid.has(GRID_IS_SYSTEM_GRID_VALUE)); +		ensure("Grid file wasn't greated as it wasn't saved",  +			   !LLFile::isfile("grid_test.xml")); +	} +	 +	// initialization with a grid file +	template<> template<> +	void viewerNetworkTestObject::test<2>() +	{ +		llofstream gridfile("grid_test.xml"); +		gridfile << gSampleGridFile; +		gridfile.close(); +		 +		LLGridManager::getInstance()->initialize("grid_test.xml"); +		std::map<std::string, std::string> known_grids = LLGridManager::getInstance()->getKnownGrids(); +#ifndef LL_RELEASE_FOR_DOWNLOAD +		ensure_equals("adding a grid via a grid file increases known grid size",  +					  known_grids.size(), 19); +		ensure_equals("Agni is still there after we've added a grid via a grid file",  +					  known_grids["util.agni.lindenlab.com"], std::string("Agni")); +	 +#else +		ensure_equals("adding a grid via a grid file increases known grid size",  +					  known_grids.size(), 3); +		ensure_equals("Agni is still there after we've added a grid via a grid file",  +					  known_grids["util.agni.lindenlab.com"], std::string("Secondlife.com")); + +#endif +	 +		 +		// assure Agni doesn't get overwritten +		LLSD grid = LLGridManager::getInstance()->getGridInfo("util.agni.lindenlab.com"); +#ifndef LL_RELEASE_FOR_DOWNLOAD +		ensure_equals("Agni grid label was not modified by grid file",  +					  grid[GRID_LABEL_VALUE].asString(), std::string("Agni")); +#else \\ LL_RELEASE_FOR_DOWNLOAD +		ensure_equals("Agni grid label was not modified by grid file",  +					  grid[GRID_LABEL_VALUE].asString(), std::string("Secondlife.com")); +#endif \\ LL_RELEASE_FOR_DOWNLOAD +		 +		ensure_equals("Agni name wasn't modified by grid file", +					  grid[GRID_VALUE].asString(), std::string("util.agni.lindenlab.com")); +		ensure("Agni grid URI is still an array after grid file",  +			   grid[GRID_LOGIN_URI_VALUE].isArray()); +		ensure_equals("Agni login uri still the same after grid file",  +					  grid[GRID_LOGIN_URI_VALUE][0].asString(),   +					  std::string("https://login.agni.lindenlab.com/cgi-bin/login.cgi")); +		ensure_equals("Agni helper uri still the same after grid file",  +					  grid[GRID_HELPER_URI_VALUE].asString(),  +					  std::string("https://secondlife.com/helpers/")); +		ensure_equals("Agni login page the same after grid file",  +					  grid[GRID_LOGIN_PAGE_VALUE].asString(),  +					  std::string("http://secondlife.com/app/login/")); +		ensure("Agni still a favorite after grid file",  +			   grid.has(GRID_IS_FAVORITE_VALUE)); +		ensure("Agni system grid still set after grid file",  +			   grid.has(GRID_IS_SYSTEM_GRID_VALUE)); +		 +		ensure_equals("Grid file adds to name<->label map",  +					  known_grids["grid1"], std::string("mylabel")); +		grid = LLGridManager::getInstance()->getGridInfo("grid1"); +		ensure_equals("grid file grid name is set", +					  grid[GRID_VALUE].asString(), std::string("grid1")); +		ensure_equals("grid file label is set",  +					  grid[GRID_LABEL_VALUE].asString(), std::string("mylabel")); +		ensure("grid file login uri is an array", +			   grid[GRID_LOGIN_URI_VALUE].isArray()); +		ensure_equals("grid file login uri is set", +					  grid[GRID_LOGIN_URI_VALUE][0].asString(),  +					  std::string("myloginuri")); +		ensure_equals("grid file helper uri is set", +					  grid[GRID_HELPER_URI_VALUE].asString(),  +					  std::string("https://helper1/helpers/")); +		ensure_equals("grid file login page is set", +					  grid[GRID_LOGIN_PAGE_VALUE].asString(),  +					  std::string("loginpage")); +		ensure("grid file favorite is set", +			   grid.has(GRID_IS_FAVORITE_VALUE)); +		ensure("grid file isn't a system grid", +			   !grid.has(GRID_IS_SYSTEM_GRID_VALUE));		 +		ensure("Grid file still exists after loading",  +			   LLFile::isfile("grid_test.xml")); +	} +	 +	// Initialize via command line +	 +	template<> template<> +	void viewerNetworkTestObject::test<3>() +	{	 +		gCmdLineLoginURI = "https://my.login.uri/cgi-bin/login.cgi"; +		 +		LLGridManager::getInstance()->initialize("grid_test.xml"); +		// with single login uri specified. +		std::map<std::string, std::string> known_grids = LLGridManager::getInstance()->getKnownGrids(); +		ensure_equals("adding a command line grid increases known grid size",  +					  known_grids.size(), 19); +		ensure_equals("Command line grid is added to the list of grids",  +					  known_grids["my.login.uri"], std::string("my.login.uri")); +		LLSD grid = LLGridManager::getInstance()->getGridInfo("my.login.uri"); +		ensure_equals("Command line grid name is set", +					  grid[GRID_VALUE].asString(), std::string("my.login.uri")); +		ensure_equals("Command line grid label is set",  +					  grid[GRID_LABEL_VALUE].asString(), std::string("my.login.uri")); +		ensure("Command line grid login uri is an array", +			   grid[GRID_LOGIN_URI_VALUE].isArray()); +		ensure_equals("Command line grid login uri is set", +					  grid[GRID_LOGIN_URI_VALUE][0].asString(),  +					  std::string("https://my.login.uri/cgi-bin/login.cgi")); +		ensure_equals("Command line grid helper uri is set", +					  grid[GRID_HELPER_URI_VALUE].asString(),  +					  std::string("https://my.login.uri/helpers/")); +		ensure_equals("Command line grid login page is set", +					  grid[GRID_LOGIN_PAGE_VALUE].asString(),  +					  std::string("http://my.login.uri/app/login/")); +		ensure("Command line grid favorite is set", +			   !grid.has(GRID_IS_FAVORITE_VALUE)); +		ensure("Command line grid isn't a system grid", +			   !grid.has(GRID_IS_SYSTEM_GRID_VALUE));		 +		 +		// now try a command line with a custom grid identifier +		gCmdLineGridChoice = "mycustomgridchoice";		 +		LLGridManager::getInstance()->initialize("grid_test.xml"); +		known_grids = LLGridManager::getInstance()->getKnownGrids(); +		ensure_equals("adding a command line grid with custom name increases known grid size",  +					  known_grids.size(), 19); +		ensure_equals("Custom Command line grid is added to the list of grids",  +					  known_grids["mycustomgridchoice"], std::string("mycustomgridchoice")); +		grid = LLGridManager::getInstance()->getGridInfo("mycustomgridchoice"); +		ensure_equals("Custom Command line grid name is set", +					  grid[GRID_VALUE].asString(), std::string("mycustomgridchoice")); +		ensure_equals("Custom Command line grid label is set",  +					  grid[GRID_LABEL_VALUE].asString(), std::string("mycustomgridchoice"));		 +		ensure("Custom Command line grid login uri is an array", +			   grid[GRID_LOGIN_URI_VALUE].isArray()); +		ensure_equals("Custom Command line grid login uri is set", +					  grid[GRID_LOGIN_URI_VALUE][0].asString(),  +					  std::string("https://my.login.uri/cgi-bin/login.cgi")); +		 +		// add a helperuri +		gCmdLineHelperURI = "myhelperuri"; +		LLGridManager::getInstance()->initialize("grid_test.xml"); +		grid = LLGridManager::getInstance()->getGridInfo("mycustomgridchoice");		 +		ensure_equals("Validate command line helper uri",  +					  grid[GRID_HELPER_URI_VALUE].asString(), std::string("myhelperuri"));		 +		 +		// add a login page +		gLoginPage = "myloginpage"; +		LLGridManager::getInstance()->initialize("grid_test.xml"); +		grid = LLGridManager::getInstance()->getGridInfo("mycustomgridchoice");		 +		ensure_equals("Validate command line helper uri",  +					  grid[GRID_LOGIN_PAGE_VALUE].asString(), std::string("myloginpage"));			 +	} +	 +	// validate grid selection +	template<> template<> +	void viewerNetworkTestObject::test<4>() +	{	 +		LLSD loginURI = LLSD::emptyArray(); +		LLSD grid = LLSD::emptyMap(); +		// adding a grid with simply a name will populate the values. +		grid[GRID_VALUE] = "myaddedgrid"; + +		LLGridManager::getInstance()->initialize("grid_test.xml"); +		LLGridManager::getInstance()->addGrid(grid); +		LLGridManager::getInstance()->setGridChoice("util.agni.lindenlab.com"); +#ifndef LL_RELEASE_FOR_DOWNLOAD		 +		ensure_equals("getGridLabel", LLGridManager::getInstance()->getGridLabel(), std::string("Agni")); +#else // LL_RELEASE_FOR_DOWNLOAD +		ensure_equals("getGridLabel", LLGridManager::getInstance()->getGridLabel(), std::string("Secondlife.com"));		 +#endif // LL_RELEASE_FOR_DOWNLOAD +		ensure_equals("getGrid", LLGridManager::getInstance()->getGrid(),  +					  std::string("util.agni.lindenlab.com")); +		ensure_equals("getHelperURI", LLGridManager::getInstance()->getHelperURI(),  +					  std::string("https://secondlife.com/helpers/")); +		ensure_equals("getLoginPage", LLGridManager::getInstance()->getLoginPage(),  +					  std::string("http://secondlife.com/app/login/")); +		ensure_equals("getLoginPage2", LLGridManager::getInstance()->getLoginPage("util.agni.lindenlab.com"),  +					  std::string("http://secondlife.com/app/login/")); +		ensure("Is Agni a production grid", LLGridManager::getInstance()->isInProductionGrid());		 +		std::vector<std::string> uris; +		LLGridManager::getInstance()->getLoginURIs(uris); +		ensure_equals("getLoginURIs size", uris.size(), 1); +		ensure_equals("getLoginURIs", uris[0],  +					  std::string("https://login.agni.lindenlab.com/cgi-bin/login.cgi")); +		LLGridManager::getInstance()->setGridChoice("myaddedgrid"); +		ensure_equals("getGridLabel", LLGridManager::getInstance()->getGridLabel(), std::string("myaddedgrid"));		 +		ensure("Is myaddedgrid a production grid", !LLGridManager::getInstance()->isInProductionGrid()); +		 +		LLGridManager::getInstance()->setFavorite(); +		grid = LLGridManager::getInstance()->getGridInfo("myaddedgrid"); +		ensure("setting favorite", grid.has(GRID_IS_FAVORITE_VALUE)); +	} +	 +	// name based grid population +	template<> template<> +	void viewerNetworkTestObject::test<5>() +	{ +		LLGridManager::getInstance()->initialize("grid_test.xml"); +		LLSD grid = LLSD::emptyMap(); +		// adding a grid with simply a name will populate the values. +		grid[GRID_VALUE] = "myaddedgrid"; +		LLGridManager::getInstance()->addGrid(grid); +		grid = LLGridManager::getInstance()->getGridInfo("myaddedgrid"); +		 +		ensure_equals("name based grid has name value",  +					  grid[GRID_VALUE].asString(), +					  std::string("myaddedgrid")); +		ensure_equals("name based grid has label value",  +					  grid[GRID_LABEL_VALUE].asString(), +					  std::string("myaddedgrid")); +		ensure_equals("name based grid has name value",  +					  grid[GRID_HELPER_URI_VALUE].asString(), +					  std::string("https://myaddedgrid/helpers/")); +		ensure_equals("name based grid has name value",  +					  grid[GRID_LOGIN_PAGE_VALUE].asString(), +					  std::string("http://myaddedgrid/app/login/")); +		ensure("name based grid has array loginuri",  +			   grid[GRID_LOGIN_URI_VALUE].isArray()); +		ensure_equals("name based grid has single login uri value", +			   grid[GRID_LOGIN_URI_VALUE].size(), 1); +		ensure_equals("Name based grid login uri is correct", +					  grid[GRID_LOGIN_URI_VALUE][0].asString(), +					  std::string("https://myaddedgrid/cgi-bin/login.cgi")); +		ensure("name based grid is not a favorite yet",  +			   !grid.has(GRID_IS_FAVORITE_VALUE)); +		ensure("name based grid does not have system setting", +			   !grid.has(GRID_IS_SYSTEM_GRID_VALUE)); +		 +		llofstream gridfile("grid_test.xml"); +		gridfile << gSampleGridFile; +		gridfile.close(); +	} +	 +	// persistence of the grid list with an empty gridfile. +	template<> template<> +	void viewerNetworkTestObject::test<6>() +	{ +		// try with initial grid list without a grid file, +		// without setting the grid to a saveable favorite. +		LLGridManager::getInstance()->initialize("grid_test.xml"); +		LLSD grid = LLSD::emptyMap(); +		grid[GRID_VALUE] = std::string("mynewgridname"); +		LLGridManager::getInstance()->addGrid(grid); +		LLGridManager::getInstance()->saveFavorites(); +		ensure("Grid file exists after saving",  +			   LLFile::isfile("grid_test.xml")); +		LLGridManager::getInstance()->initialize("grid_test.xml"); +		// should not be there +		std::map<std::string, std::string> known_grids = LLGridManager::getInstance()->getKnownGrids(); +		ensure("New grid wasn't added to persisted list without being marked a favorite", +					  known_grids.find(std::string("mynewgridname")) == known_grids.end()); +		 +		// mark a grid a favorite to make sure it's persisted +		LLGridManager::getInstance()->addGrid(grid); +		LLGridManager::getInstance()->setGridChoice("mynewgridname"); +		LLGridManager::getInstance()->setFavorite(); +		LLGridManager::getInstance()->saveFavorites(); +		ensure("Grid file exists after saving",  +			   LLFile::isfile("grid_test.xml")); +		LLGridManager::getInstance()->initialize("grid_test.xml"); +		// should not be there +		known_grids = LLGridManager::getInstance()->getKnownGrids(); +		ensure("New grid wasn't added to persisted list after being marked a favorite", +					  known_grids.find(std::string("mynewgridname")) != +					  known_grids.end()); +	} +	 +	// persistence of the grid file with existing gridfile +	template<> template<> +	void viewerNetworkTestObject::test<7>() +	{ +		 +		llofstream gridfile("grid_test.xml"); +		gridfile << gSampleGridFile; +		gridfile.close(); +		 +		LLGridManager::getInstance()->initialize("grid_test.xml"); +		LLSD grid = LLSD::emptyMap(); +		grid[GRID_VALUE] = std::string("mynewgridname"); +		LLGridManager::getInstance()->addGrid(grid); +		LLGridManager::getInstance()->saveFavorites(); +		// validate we didn't lose existing favorites +		LLGridManager::getInstance()->initialize("grid_test.xml"); +		std::map<std::string, std::string> known_grids = LLGridManager::getInstance()->getKnownGrids(); +		ensure("New grid wasn't added to persisted list after being marked a favorite", +			   known_grids.find(std::string("grid1")) != +			   known_grids.end()); +		 +		// add a grid +		LLGridManager::getInstance()->addGrid(grid); +		LLGridManager::getInstance()->setGridChoice("mynewgridname"); +		LLGridManager::getInstance()->setFavorite(); +		LLGridManager::getInstance()->saveFavorites(); +		known_grids = LLGridManager::getInstance()->getKnownGrids(); +		ensure("New grid wasn't added to persisted list after being marked a favorite", +			   known_grids.find(std::string("grid1")) != +			   known_grids.end()); +		known_grids = LLGridManager::getInstance()->getKnownGrids(); +		ensure("New grid wasn't added to persisted list after being marked a favorite", +			   known_grids.find(std::string("mynewgridname")) != +			   known_grids.end()); +	} +} diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 18ac10fe38..659da31007 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -397,6 +397,15 @@ class WindowsManifest(ViewerManifest):          self.disable_manifest_check() +        # Diamondware Runtimes +        if self.prefix(src="diamondware-runtime/i686-win32", dst=""): +            self.path("SLVoice_dwTVC.exe") +            self.path("libcurl.dll") +            self.path("libeay32.dll") +            self.path("ssleay32.dll") +            self.path("zlib1.dll") +            self.end_prefix() +          # pull in the crash logger and updater from other projects          # tag:"crash-logger" here as a cue to the exporter          self.path(src='../win_crash_logger/%s/windows-crash-logger.exe' % self.args['configuration'], @@ -606,6 +615,9 @@ class DarwinManifest(ViewerManifest):                  self.path("vivox-runtime/universal-darwin/libvivoxsdk.dylib", "libvivoxsdk.dylib")                  self.path("vivox-runtime/universal-darwin/libvivoxplatform.dylib", "libvivoxplatform.dylib")                  self.path("vivox-runtime/universal-darwin/SLVoice", "SLVoice") +                # DiamondWare runtime                                            +                self.path("diamondware-runtime/universal-darwin/SLVoice_dwTVC","SLVoice_dwTVC") +                self.path("diamondware-runtime/universal-darwin/libfmodex.dylib", "libfmodex.dylib")                  libdir = "../../libraries/universal-darwin/lib_release"                  dylibs = {} @@ -901,6 +913,11 @@ class Linux_i686Manifest(LinuxManifest):                      pass              self.end_prefix("lib") +            # Diamondware runtimes +            if self.prefix(src="diamondware-runtime/i686-linux", dst="bin"): +                    self.path("SLVoice_dwTVC") +                    self.end_prefix() +              # Vivox runtimes              if self.prefix(src="vivox-runtime/i686-linux", dst="bin"):                      self.path("SLVoice") diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp index b9f61ca7e1..e1922367bf 100644 --- a/indra/viewer_components/login/lllogin.cpp +++ b/indra/viewer_components/login/lllogin.cpp @@ -122,29 +122,35 @@ private:  	LLSD mAuthResponse, mValidAuthResponse;  }; -void LLLogin::Impl::connect(const std::string& uri, const LLSD& credentials) +void LLLogin::Impl::connect(const std::string& uri, const LLSD& login_params)  { +    LL_DEBUGS("LLLogin") << " connect with  uri '" << uri << "', login_params " << login_params << LL_ENDL; +	      // Launch a coroutine with our login_() method. Run the coroutine until      // its first wait; at that point, return here.      std::string coroname =           LLCoros::instance().launch("LLLogin::Impl::login_", -                                   boost::bind(&Impl::login_, this, _1, uri, credentials)); +                                   boost::bind(&Impl::login_, this, _1, uri, login_params)); +    LL_DEBUGS("LLLogin") << " connected with  uri '" << uri << "', login_params " << login_params << LL_ENDL;	  } -void LLLogin::Impl::login_(LLCoros::self& self, std::string uri, LLSD credentials) +void LLLogin::Impl::login_(LLCoros::self& self, std::string uri, LLSD login_params)  { -	LLSD printable_credentials = credentials; -	if(printable_credentials.has("params")  -		&& printable_credentials["params"].has("passwd"))  +	try  	{ -		printable_credentials["params"]["passwd"] = "*******"; -	} +	LLSD printable_params = login_params; +	//if(printable_params.has("params")  +	//	&& printable_params["params"].has("passwd"))  +	//{ +	//	printable_params["params"]["passwd"] = "*******"; +	//}      LL_DEBUGS("LLLogin") << "Entering coroutine " << LLCoros::instance().getName(self) -                        << " with uri '" << uri << "', credentials " << printable_credentials << LL_ENDL; +                        << " with uri '" << uri << "', parameters " << printable_params << LL_ENDL;  	// Arriving in SRVRequest state      LLEventStream replyPump("SRVreply", true);      // Should be an array of one or more uri strings. +      LLSD rewrittenURIs;      {          LLEventTimeout filter(replyPump); @@ -155,9 +161,9 @@ void LLLogin::Impl::login_(LLCoros::self& self, std::string uri, LLSD credential          // *NOTE:Mani - Completely arbitrary default timeout value for SRV request.  		F32 seconds_to_timeout = 5.0f; -		if(credentials.has("cfg_srv_timeout")) +		if(login_params.has("cfg_srv_timeout"))  		{ -			seconds_to_timeout = credentials["cfg_srv_timeout"].asReal(); +			seconds_to_timeout = login_params["cfg_srv_timeout"].asReal();  		}          // If the SRV request times out (e.g. EXT-3934), simulate response: an @@ -167,9 +173,9 @@ void LLLogin::Impl::login_(LLCoros::self& self, std::string uri, LLSD credential  		filter.eventAfter(seconds_to_timeout, fakeResponse);  		std::string srv_pump_name = "LLAres"; -		if(credentials.has("cfg_srv_pump")) +		if(login_params.has("cfg_srv_pump"))  		{ -			srv_pump_name = credentials["cfg_srv_pump"].asString(); +			srv_pump_name = login_params["cfg_srv_pump"].asString();  		}  		// Make request @@ -194,7 +200,7 @@ void LLLogin::Impl::login_(LLCoros::self& self, std::string uri, LLSD credential               urend(rewrittenURIs.endArray());           urit != urend; ++urit)      { -        LLSD request(credentials); +        LLSD request(login_params);          request["reply"] = loginReplyPump.getName();          request["uri"] = *urit;          std::string status; @@ -291,8 +297,17 @@ void LLLogin::Impl::login_(LLCoros::self& self, std::string uri, LLSD credential  	// to success, add a data/message and data/reason fields.  	LLSD error_response;  	error_response["reason"] = mAuthResponse["status"]; +	error_response["errorcode"] = mAuthResponse["errorcode"];  	error_response["message"] = mAuthResponse["error"]; +	if(mAuthResponse.has("certificate")) +	{ +		error_response["certificate"] = mAuthResponse["certificate"]; +	}  	sendProgressEvent("offline", "fail.login", error_response); +	} +	catch (...) { +		llerrs << "login exception caught" << llendl;  +	}  }  void LLLogin::Impl::disconnect()  | 
