diff options
121 files changed, 16220 insertions, 9590 deletions
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/llurlentry.cpp b/indra/llui/llurlentry.cpp index e8e3459673..c38e38c900 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";  } @@ -496,10 +502,11 @@ std::string LLUrlEntryInventory::getLabel(const std::string &url, const LLUrlLab  ///  /// 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"); @@ -515,7 +522,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"); @@ -560,10 +567,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"); @@ -581,7 +589,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 @@ -680,7 +693,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/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 30049b73ea..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> @@ -3442,7 +3487,7 @@        <key>Type</key>        <string>Boolean</string>        <key>Value</key> -      <integer>0</integer> +      <integer>1</integer>      </map>      <key>ForceMandatoryUpdate</key>      <map> @@ -7696,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> @@ -8500,7 +8556,7 @@        <key>Type</key>        <string>Boolean</string>        <key>Value</key> -      <integer>0</integer> +      <integer>1</integer>      </map>      <key>ShowTangentBasis</key>      <map> @@ -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/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/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..215f1b95aa 100644 --- a/indra/newview/llcallfloater.cpp +++ b/indra/newview/llcallfloater.cpp @@ -133,9 +133,9 @@ LLCallFloater::~LLCallFloater()  	// Don't use LLVoiceClient::getInstance() here   	// singleton MAY have already been destroyed. -	if(gVoiceClient) +	if(LLVoiceClient::getInstance())  	{ -		gVoiceClient->removeObserver(this); +		LLVoiceClient::getInstance()->removeObserver(this);  	}  	LLTransientFloaterMgr::getInstance()->removeControlView(this);  } @@ -191,7 +191,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 +209,6 @@ void LLCallFloater::draw()  void LLCallFloater::onChange()  {  	if (NULL == mParticipants) return; -  	updateParticipantsVoiceState();  	// Add newly joined participants. @@ -239,11 +238,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 +252,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 +291,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 +470,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 +554,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 +724,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 71e7ae7061..1e404d611c 100644 --- a/indra/newview/llchathistory.cpp +++ b/indra/newview/llchathistory.cpp @@ -640,20 +640,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/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 764a0dc954..3a73037ae8 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -595,7 +595,7 @@ void LLFloaterPreference::onBtnOK()  		llinfos << "Can't close preferences!" << llendl;  	} -	LLPanelLogin::refreshLocation( false ); +	LLPanelLogin::updateLocationCombo( false );  }  // static  @@ -612,7 +612,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/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 ad2e594b49..3b4a4a1344 100644 --- a/indra/newview/lllocationinputctrl.cpp +++ b/indra/newview/lllocationinputctrl.cpp @@ -668,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( @@ -1011,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/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/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 1d2687a8c2..8488527185 100644 --- a/indra/newview/llurllineeditorctrl.cpp +++ b/indra/newview/llurllineeditorctrl.cpp @@ -89,7 +89,7 @@ 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); 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 b2b7e653e4..8627f08891 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -413,9 +413,9 @@ bool handleHighResSnapshotChanged(const LLSD& newvalue)  bool handleVoiceClientPrefsChanged(const LLSD& newvalue)  { -	if(gVoiceClient) +	if(LLVoiceClient::getInstance())  	{ -		gVoiceClient->updateSettings(); +		LLVoiceClient::getInstance()->updateSettings();  	}  	return true;  } @@ -446,7 +446,7 @@ bool handleVelocityInterpolate(const LLSD& newvalue)  bool handleForceShowGrid(const LLSD& newvalue)  { -	LLPanelLogin::refreshLocation( false ); +	LLPanelLogin::updateServer( );  	return true;  } 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/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 7d87f06794..a8b1257cf6 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -429,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" );  	} @@ -445,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 @@ -3467,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; @@ -3507,7 +3507,7 @@ BOOL check_toggle_hacked_godmode(void*)  bool enable_toggle_hacked_godmode(void*)  { -  return !LLViewerLogin::getInstance()->isInProductionGrid(); +  return !LLGridManager::getInstance()->isInProductionGrid();  }  #endif @@ -4378,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; @@ -4991,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(); @@ -6627,7 +6627,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  		{ @@ -6689,7 +6689,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/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 ae3f680cbf..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" );      } @@ -2200,7 +2194,6 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask)  		}  		return TRUE;  	} -  	// hidden edit menu for cut/copy/paste  	if (gEditMenu && gEditMenu->handleAcceleratorKey(key, mask))  	{ diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 0ce8894872..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; 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..a7efc49c67 --- /dev/null +++ b/indra/newview/llvoicevivox.cpp @@ -0,0 +1,6967 @@ + /**  + * @file LLVivoxVoiceClient.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/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..da61840761 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 vefifySSLCert = !gSavedSettings.getBOOL("NoVerifySSLCert"); +	mCertStore = gSavedSettings.getString("CertStore"); +	mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, vefifySSLCert); +	mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, vefifySSLCert ? 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/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/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index e8ba8c683d..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] @@ -2417,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 01adc00e1a..539c5f785c 100644 --- a/indra/newview/skins/default/xui/en/panel_login.xml +++ b/indra/newview/skins/default/xui/en/panel_login.xml @@ -48,50 +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"  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" -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" @@ -99,7 +80,7 @@ height="15"  left_pad="8"  name="password_text"  top="20" -    width="150"> +    width="135">         Password:  </text>  <line_editor @@ -119,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 @@ -149,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" @@ -162,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/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/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()  | 
