From 1a9d19d95527d717b89d4ebf45af81824fcbdf44 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Fri, 3 Jul 2009 01:05:27 +0000 Subject: Initial secapi merge svn merge -c112450 svn+ssh://svn.lindenlab.com/svn/linden/branches/giab-viewer/giab-viewer-1 giab-viewer-1-23 svn merge -c112913 svn+ssh://svn.lindenlab.com/svn/linden/branches/giab-viewer/giab-viewer-1 giab-viewer-1-23 --- indra/newview/CMakeLists.txt | 10 + indra/newview/llsecapi.cpp | 69 +++++ indra/newview/llsecapi.h | 232 +++++++++++++++++ indra/newview/llsechandler_basic.cpp | 490 +++++++++++++++++++++++++++++++++++ indra/newview/llsechandler_basic.h | 116 +++++++++ 5 files changed, 917 insertions(+) create mode 100644 indra/newview/llsecapi.cpp create mode 100644 indra/newview/llsecapi.h create mode 100644 indra/newview/llsechandler_basic.cpp create mode 100644 indra/newview/llsechandler_basic.h diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 62cb8380c0..9430bb1d56 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(ELFIO) include(FMOD) include(OPENAL) @@ -372,6 +373,8 @@ set(viewer_SOURCE_FILES llscrollingpanelparam.cpp llsearchcombobox.cpp llsearchhistory.cpp + llsecapi.cpp + llsechandler_basic.cpp llselectmgr.cpp llsidepanelappearance.cpp llsidepanelinventory.cpp @@ -875,6 +878,8 @@ set(viewer_HEADER_FILES llscrollingpanelparam.h llsearchcombobox.h llsearchhistory.h + llsecapi.h + llsechandler_basic.h llselectmgr.h llsidepanelappearance.h llsidepanelinventory.h @@ -1618,6 +1623,8 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${WINDOWS_LIBRARIES} ${XMLRPCEPI_LIBRARIES} ${ELFIO_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIBRARIES} ${LLLOGIN_LIBRARIES} ${GOOGLE_PERFTOOLS_LIBRARIES} ) @@ -1803,6 +1810,9 @@ if (LL_TESTS) #ADD_VIEWER_BUILD_TEST(lltextureinfo viewer) #ADD_VIEWER_BUILD_TEST(lltextureinfodetails viewer) #ADD_VIEWER_BUILD_TEST(lltexturestatsuploader viewer) + #ADD_COMM_BUILD_TEST(llsecapi viewer "" llviewerprecompiledheaders.cpp llsechandler_basic.cpp) + #ADD_VIEWER_COMM_BUILD_TEST(llsechandler_basic viewer "") + endif (LL_TESTS) diff --git a/indra/newview/llsecapi.cpp b/indra/newview/llsecapi.cpp new file mode 100644 index 0000000000..c2cfde0dc7 --- /dev/null +++ b/indra/newview/llsecapi.cpp @@ -0,0 +1,69 @@ +/** + * @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 + + +std::map > gHandlerMap; + + +void initializeSecHandler() +{ + gHandlerMap[BASIC_SECHANDLER] = new LLSecAPIBasicHandler(); +} +// start using a given security api handler. If the string is empty +// the default is used +LLPointer getSecHandler(const std::string& handler_type) +{ + if (gHandlerMap.find(handler_type) != gHandlerMap.end()) + { + return gHandlerMap[handler_type]; + } + else + { + return LLPointer(NULL); + } +} +// register a handler +void registerSecHandler(const std::string& handler_type, + LLPointer& handler) +{ + gHandlerMap[handler_type] = handler; +} + + + + diff --git a/indra/newview/llsecapi.h b/indra/newview/llsecapi.h new file mode 100644 index 0000000000..743d3d6770 --- /dev/null +++ b/indra/newview/llsecapi.h @@ -0,0 +1,232 @@ +/** + * @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 +#include + +// All error handling is via exceptions. + + +#define CERT_SUBJECT_NAME "subject_name" +#define CERT_ISSUER_NAME "issuer_name" + +#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 BASIC_SECHANDLER "BASIC_SECHANDLER" + + +// All error handling is via exceptions. + +class LLCertException +{ +public: + LLCertException(const char* msg) + { + llerrs << "Certificate Error: " << msg << llendl; + mMsg = std::string(msg); + } +protected: + std::string mMsg; +}; + +class LLProtectedDataException +{ +public: + LLProtectedDataException(const char *msg) + { + llerrs << "Certificate Error: " << msg << llendl; + mMsg = std::string(msg); + } +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()=0; + + // return a DER encoded certificate + virtual std::vector getBinary()=0; + + // return an LLSD object containing information about the certificate + // such as its name, signature, expiry time, serial number + virtual LLSD getLLSD()=0; + + // return an openSSL X509 struct for the certificate + virtual X509* getOpenSSLX509()=0; + +}; + + +// class LLCertificateChain +// Class representing a chain of certificates in order, with the +// 0th element being the CA +class LLCertificateChain : public LLRefCount +{ + LOG_CLASS(LLCertificateChain); + static const int VT_SSL = 0; + static const int VT_AGENT_DOMAIN = 1; + static const int VT_GRID_DOMAIN = 1; + +public: + LLCertificateChain() {} + + virtual ~LLCertificateChain() {} + + virtual X509_STORE getOpenSSLX509Store()=0; // return an openssl X509_STORE + // for this store + + virtual void appendCert(const LLCertificate& cert)=0; // append a cert to the end + //of the chain + + virtual LLPointer& operator [](int index)=0; // retrieve a certificate + // from the chain by index + // -1 == end of chain + + virtual int len() const =0; // return number of certificates in the chain + + // validate a certificate chain given the params. + // validation type indicates whether it's simply an SSL cert, or + // something more specific + virtual bool validate(int validation_type, + const LLSD& validation_params) const =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 : public LLRefCount +{ +public: + LLCertificateStore() {} + virtual ~LLCertificateStore() {} + + virtual X509_STORE getOpenSSLX509Store()=0; // return an openssl X509_STORE + // for this store + + // add a copy of a cert to the store + virtual void append(const LLCertificate& cert)=0; + + // add a copy of a cert to the store + virtual void insert(const int index, const LLCertificate& cert)=0; + + // remove a certificate from the store + virtual void remove(int index)=0; + + // return a certificate at the index + virtual LLPointer& operator[](int index)=0; + // return the number of certs in the store + virtual int len() const =0; + + // load the store from a persisted location + virtual void load(const std::string& store_id)=0; + + // persist the store + virtual void save()=0; + + // return the store id + virtual std::string storeId()=0; + + // validate a cert chain + virtual bool validate(const LLCertificateChain& cert_chain) const=0; +}; + + +// LLSecAPIHandler Class +// Interface handler class for the various security storage handlers. +class LLSecAPIHandler : public LLRefCount +{ +public: + + LLSecAPIHandler() {} + virtual ~LLSecAPIHandler() {} + + // instantiate a certificate from a pem string + virtual LLPointer getCertificate(const std::string& pem_cert)=0; + + + // instiate a certificate from an openssl X509 structure + virtual LLPointer getCertificate(X509* openssl_cert)=0; + + // instantiate a chain from an X509_STORE_CTX + virtual LLPointer 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 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; +}; + +void secHandlerInitialize(); + +// retrieve a security api depending on the api type +LLPointer getSecHandler(const std::string& handler_type); + +void registerSecHandler(const std::string& handler_type, + LLPointer& handler); + +#endif // LL_SECAPI_H diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp new file mode 100644 index 0000000000..ab6bf034f1 --- /dev/null +++ b/indra/newview/llsechandler_basic.cpp @@ -0,0 +1,490 @@ +/** + * @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 + * + * 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 "llfile.h" +#include "lldir.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// 128 bits of salt data... +#define STORE_SALT_SIZE 16 +#define BUFFER_READ_SIZE 256 + + +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()); + + mCert = NULL; + PEM_read_bio_X509(pem_bio, &mCert, 0, NULL); + BIO_free(pem_bio); + if (!mCert) + { + throw LLCertException("Error parsing certificate"); + } +} + + +LLBasicCertificate::LLBasicCertificate(X509* pCert) +{ + if (!pCert || !pCert->cert_info) + { + throw LLCertException("Invalid certificate"); + } + mCert = X509_dup(pCert); +} + +LLBasicCertificate::~LLBasicCertificate() +{ + + X509_free(mCert); +} + +// +// retrieve the pem using the openssl functionality +std::string LLBasicCertificate::getPem() +{ + 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) + { + throw LLCertException("couldn't allocate memory buffer"); + } + 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 LLBasicCertificate::getBinary() +{ + U8 * der_bio_data = NULL; + // get a memory bio + BIO *der_bio = BIO_new(BIO_s_mem()); + if (!der_bio) + { + throw LLCertException("couldn't allocate memory buffer"); + } + i2d_X509_bio(der_bio, mCert); + int length = BIO_get_mem_data(der_bio, &der_bio_data); + std::vector 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() +{ + LLSD result; + + // call the various helpers to build the LLSD + result[CERT_SUBJECT_NAME] = cert_name_from_X509_NAME(X509_get_subject_name(mCert)); + result[CERT_ISSUER_NAME] = cert_name_from_X509_NAME(X509_get_issuer_name(mCert)); + result[CERT_SUBJECT_NAME_STRING] = cert_string_name_from_X509_NAME(X509_get_subject_name(mCert)); + result[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) + { + result[CERT_SERIAL_NUMBER] = cert_string_from_asn1_integer(sn); + } + + result[CERT_VALID_TO] = cert_date_from_asn1_time(X509_get_notAfter(mCert)); + result[CERT_VALID_FROM] = cert_date_from_asn1_time(X509_get_notBefore(mCert)); + result[CERT_SHA1_DIGEST] = cert_get_digest("sha1", mCert); + result[CERT_MD5_DIGEST] = cert_get_digest("md5", mCert); + + + + return result; +} + +X509* LLBasicCertificate::getOpenSSLX509() +{ + 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) +{ + BIGNUM *bn = ASN1_INTEGER_to_BN(value, NULL); + char * ascii_bn = BN_bn2hex(bn); + + BN_free(bn); + + std::string result(ascii_bn); + OPENSSL_free(ascii_bn); + 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) + throw LLCertException("invalid certificate time value"); + + // 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'); + + return LLDate((F64)mktime(×truct)); +} + +// 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[1024]; + unsigned int len = 1024; + std::stringstream result; + const EVP_MD* digest = NULL; + OpenSSL_add_all_digests(); + // 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 + { + throw LLCertException("Invalid digest"); + } + + 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(); +} + + +// 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) +{ + mProtectedDataFilename = protected_data_file; + mProtectedDataMap = LLSD::emptyMap(); + _readProtectedData(); +} + +LLSecAPIBasicHandler::LLSecAPIBasicHandler() +{ + std::string mProtectedDataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, + "bin_conf.dat"); + + mProtectedDataMap = LLSD::emptyMap(); + _readProtectedData(); +} + +LLSecAPIBasicHandler::~LLSecAPIBasicHandler() +{ + _writeProtectedData(); +} + +void LLSecAPIBasicHandler::_readProtectedData() +{ + // attempt to load the file into our map + LLPointer 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; + + + // 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("Corrupt Protected Data Store"); + } + + // 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. + // 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("Corrupt Protected Data Store"); + } + } +} + +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); + 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::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 LLSecAPIBasicHandler::getCertificate(const std::string& pem_cert) +{ + LLPointer result = new LLBasicCertificate(pem_cert); + return result; +} + + + +// instiate a certificate from an openssl X509 structure +LLPointer LLSecAPIBasicHandler::getCertificate(X509* openssl_cert) +{ + LLPointer result = new LLBasicCertificate(openssl_cert); + return result; +} + +// instantiate a chain from an X509_STORE_CTX +LLPointer LLSecAPIBasicHandler::getCertificateChain(const X509_STORE_CTX* chain) +{ + LLPointer result = NULL; + 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 LLSecAPIBasicHandler::getCertificateStore(const std::string& store_id) +{ + LLPointer result; + return result; +} + +// 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(); +} + +// 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; +} \ No newline at end of file diff --git a/indra/newview/llsechandler_basic.h b/indra/newview/llsechandler_basic.h new file mode 100644 index 0000000000..0ec6938583 --- /dev/null +++ b/indra/newview/llsechandler_basic.h @@ -0,0 +1,116 @@ +/** + * @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 +#include + +// 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(); + virtual std::vector getBinary(); + virtual LLSD getLLSD(); + + virtual X509* getOpenSSLX509(); +protected: + // certificates are stored as X509 objects, as validation and + // other functionality is via openssl + X509* mCert; +}; + +// LLSecAPIBasicHandler Class +// Interface handler class for the various security storage handlers. +class LLSecAPIBasicHandler : public LLSecAPIHandler +{ +public: + + LLSecAPIBasicHandler(const std::string& protected_data_filename); + LLSecAPIBasicHandler(); + + virtual ~LLSecAPIBasicHandler(); + + // instantiate a certificate from a pem string + virtual LLPointer getCertificate(const std::string& pem_cert); + + + // instiate a certificate from an openssl X509 structure + virtual LLPointer getCertificate(X509* openssl_cert); + + // instantiate a chain from an X509_STORE_CTX + virtual LLPointer 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 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); +protected: + void _readProtectedData(); + void _writeProtectedData(); + + std::string mProtectedDataFilename; + LLSD mProtectedDataMap; +}; + +#endif // LLSECHANDLER_BASIC + + + -- cgit v1.2.3 From fe71dd340ab396b93bde45df438041af5d85fd47 Mon Sep 17 00:00:00 2001 From: Roxie Linden Date: Mon, 1 Feb 2010 15:10:19 -0800 Subject: Merge giab-viewer-trunk 2497, general merge of more secapi stuff as well as certificate handling stuff. Grid manager as well --- indra/llmessage/llares.cpp | 1 + indra/llvfs/lldir.cpp | 39 +- indra/llvfs/lldir.h | 4 +- indra/newview/CMakeLists.txt | 18 +- indra/newview/app_settings/logcontrol.xml | 8 +- indra/newview/app_settings/settings.xml | 24 +- indra/newview/llappviewer.cpp | 54 +- indra/newview/llappviewer.h | 1 - indra/newview/llcurrencyuimanager.cpp | 2 +- indra/newview/llfloaterbuyland.cpp | 2 +- indra/newview/llfloaterreporter.cpp | 2 +- indra/newview/llloginhandler.cpp | 196 +++--- indra/newview/llloginhandler.h | 11 +- indra/newview/lllogininstance.cpp | 27 +- indra/newview/lllogininstance.h | 7 +- indra/newview/llpanellogin.cpp | 314 +++++----- indra/newview/llpanellogin.h | 14 +- indra/newview/llsecapi.cpp | 32 +- indra/newview/llsecapi.h | 68 +- indra/newview/llsechandler_basic.cpp | 284 ++++++++- indra/newview/llsechandler_basic.h | 80 ++- indra/newview/llstartup.cpp | 351 +++-------- indra/newview/llstartup.h | 8 - indra/newview/llviewermenu.cpp | 16 +- indra/newview/llviewernetwork.cpp | 683 ++++++++++++++------- indra/newview/llviewernetwork.h | 181 ++++-- indra/newview/llviewerobject.cpp | 12 +- indra/newview/llviewerstats.cpp | 6 +- indra/newview/llviewerwindow.cpp | 8 +- indra/newview/llvoiceclient.cpp | 13 +- indra/newview/llvoiceclient.h | 6 +- indra/newview/llweb.cpp | 2 +- indra/newview/skins/default/xui/en/panel_login.xml | 55 +- indra/newview/tests/lllogininstance_test.cpp | 107 +++- indra/newview/tests/llsechandler_basic_test.cpp | 456 ++++++++++++++ indra/newview/tests/llviewernetwork_test.cpp | 446 ++++++++++++++ indra/viewer_components/login/lllogin.cpp | 37 +- 37 files changed, 2479 insertions(+), 1096 deletions(-) create mode 100644 indra/newview/tests/llsechandler_basic_test.cpp create mode 100644 indra/newview/tests/llviewernetwork_test.cpp diff --git a/indra/llmessage/llares.cpp b/indra/llmessage/llares.cpp index 104629c157..db7ac2f154 100644 --- a/indra/llmessage/llares.cpp +++ b/indra/llmessage/llares.cpp @@ -171,6 +171,7 @@ void LLAres::rewriteURI(const std::string &uri, UriRewriteResponder *resp) resp->mUri = LLURI(uri); search("_" + resp->mUri.scheme() + "._tcp." + resp->mUri.hostName(), RES_SRV, resp); + llinfos << "Rewritten " << uri << llendl; } LLQueryResponder::LLQueryResponder() diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index b2b17fdd56..296d827a20 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -555,26 +555,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, 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, ' ', '_'); mLindenUserDir = getOSUserAppDir(); mLindenUserDir += mDirDelimiter; - mLindenUserDir += firstlower; - mLindenUserDir += "_"; - mLindenUserDir += lastlower; + mLindenUserDir += userlower; } else { - llerrs << "Invalid name for LLDir::setLindenUserDir(first='" << first << "', last='" << last << "')" << llendl; + llerrs << "NULL name for LLDir::setLindenUserDir" << llendl; } dumpCurrentDirectories(); @@ -592,27 +589,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); - mPerAccountChatLogsDir = getChatLogsDir(); - mPerAccountChatLogsDir += mDirDelimiter; - mPerAccountChatLogsDir += firstlower; - mPerAccountChatLogsDir += "_"; - mPerAccountChatLogsDir += lastlower; + std::string userlower(username); + LLStringUtil::toLower(userlower); + LLStringUtil::replaceChar(userlower, ' ', '_'); + mLindenUserDir = getChatLogsDir(); + mLindenUserDir += mDirDelimiter; + mLindenUserDir += 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 206e3223e3..9c1e992eca 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 9430bb1d56..4be6fb940e 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1407,11 +1407,11 @@ if (WINDOWS) # Note the need to specify multiple names explicitly. set(GOOGLE_PERF_TOOLS_SOURCE ${SHARED_LIB_STAGING_DIR}/Release/libtcmalloc_minimal.dll - ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libtcmalloc_minimal.dll - ${SHARED_LIB_STAGING_DIR}/Debug/libtcmalloc_minimal-debug.dll - ) - endif(USE_GOOGLE_PERFTOOLS) - + ${SHARED_LIB_STAGING_DIR}/RelWithDebInfo/libtcmalloc_minimal.dll + ${SHARED_LIB_STAGING_DIR}/Debug/libtcmalloc_minimal-debug.dll + ) + endif(USE_GOOGLE_PERFTOOLS) + set(COPY_INPUT_DEPENDECIES # The following commented dependencies are determined at variably at build time. Can't do this here. @@ -1504,11 +1504,11 @@ if (WINDOWS) --dest=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR} --grid=${GRID} --source=${CMAKE_CURRENT_SOURCE_DIR} - --touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/copy_touched.bat + --touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/copy_touched.bat DEPENDS - ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py - stage_third_party_libs - ${COPY_INPUT_DEPENDECIES} + ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py + stage_third_party_libs + ${COPY_INPUT_DEPENDECIES} COMMENT "Performing viewer_manifest copy" ) diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml index d7bb64ce8a..0eb98e3311 100644 --- a/indra/newview/app_settings/logcontrol.xml +++ b/indra/newview/app_settings/logcontrol.xml @@ -20,11 +20,7 @@ tags AppInit - SystemInfo - TextureCache - AppCache - Window - RenderInit + LLLogin @@ -40,6 +36,8 @@ tags + AppInit + LLLogin diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 7d98a4b6ce..75ee389750 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -1629,6 +1629,17 @@ Value 1 + CurrentGrid + + Comment + Currently Selected Grid + Persist + 1 + Type + String + Value + + CustomServer Comment @@ -3420,7 +3431,7 @@ Type Boolean Value - 0 + 1 ForceMandatoryUpdate @@ -7594,6 +7605,17 @@ Value 0 + SecondLifeEnterprise + + Comment + Enables Second Life Enterprise features + Persist + 1 + Type + Boolean + Value + 0 + SelectMovableOnly Comment diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index fb1bded795..cc32346441 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -191,6 +191,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 @@ -506,35 +509,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() { @@ -646,7 +620,7 @@ bool LLAppViewer::init() LLCurl::initClass(); initThreads(); - + initializeSecHandler(); writeSystemInfo(); // Build a string representing the current version number. @@ -776,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 @@ -1462,13 +1432,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()); @@ -2022,7 +1985,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")) @@ -2511,7 +2473,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 @@ -2678,10 +2640,10 @@ void LLAppViewer::handleViewerCrash() gMessageSystem->stopLogging(); } - LLWorld::getInstance()->getInfo(gDebugInfo); + //LLWorld::getInstance()->getInfo(gDebugInfo); // Close the debug file - pApp->writeDebugInfo(); + //pApp->writeDebugInfo(); LLError::logToFile(""); @@ -4271,7 +4233,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 diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index 40e74061b5..870d47aa79 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -186,7 +186,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/llcurrencyuimanager.cpp b/indra/newview/llcurrencyuimanager.cpp index 00c05445e1..207270c0ad 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/llfloaterbuyland.cpp b/indra/newview/llfloaterbuyland.cpp index 9b88923e7e..10a4908f3a 100644 --- a/indra/newview/llfloaterbuyland.cpp +++ b/indra/newview/llfloaterbuyland.cpp @@ -837,7 +837,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/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index e0f2fca580..a52fe131cd 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -545,7 +545,7 @@ LLSD LLFloaterReporter::gatherReport() mCopyrightWarningSeen = FALSE; std::ostringstream summary; - if (!LLViewerLogin::getInstance()->isInProductionGrid()) + if (!LLGridManager::getInstance()->isInProductionGrid()) { summary << "Preview "; } diff --git a/indra/newview/llloginhandler.cpp b/indra/newview/llloginhandler.cpp index 1be3430e07..7d43f6a8cc 100644 --- a/indra/newview/llloginhandler.cpp +++ b/indra/newview/llloginhandler.cpp @@ -35,12 +35,13 @@ #include "llloginhandler.h" // viewer includes +#include "llsecapi.h" #include "llpanellogin.h" // save_password_to_disk() #include "llstartup.h" // getStartupState() #include "llurlsimstring.h" #include "llviewercontrol.h" // gSavedSettings #include "llviewernetwork.h" // EGridInfo -#include "llviewerwindow.h" // getWindow() +#include "llviewerwindow.h" // getWindow() // library includes #include "llmd5.h" @@ -59,109 +60,28 @@ 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") + if (queryMap.has("grid")) { - grid_choice = GRID_INFO_DURGA; + LLGridManager::getInstance()->setGridChoice(queryMap["grid"].asString()); } - 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") - { - grid_choice = GRID_INFO_NANDI; - } - 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()); + LLURLSimString::setString(queryMap["region"].asString()); } - else if (!startLocation.empty()) // "last" or "home" or ??? (let LLURLSimString figure it out) + else if (!startLocation.empty()) { - LLURLSimString::setString(startLocation); + LLURLSimString::setString(startLocation); } } @@ -212,40 +132,68 @@ 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 +// 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 LLLoginHandler::initializeLoginInfo(const std::string& url) +{ + LLURI uri(url); + LLPointer result = NULL; + parse(uri.queryMap()); + // we weren't able to parse login info from the slurl, + // so try to load it from the UserLoginInfo + result = loadSavedUserLoginInfo(); + if (result.isNull()) + { + result = gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGridName()); + } + + return result; +} + + +LLPointer 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) { - 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; + + 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()->getGridName(), + identifier, authenticator); + } + return NULL; } diff --git a/indra/newview/llloginhandler.h b/indra/newview/llloginhandler.h index ac4648761b..ec2459c835 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 loadSavedUserLoginInfo(); + LLPointer initializeLoginInfo(const std::string& url); + 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..bb45cc93ea 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -55,6 +55,7 @@ #if LL_LINUX || LL_SOLARIS #include "lltrans.h" #endif +#include "llsecapi.h" static const char * const TOS_REPLY_PUMP = "lllogininstance_tos_callback"; static const char * const TOS_LISTENER_NAME = "lllogininstance_tos"; @@ -83,14 +84,14 @@ LLLoginInstance::~LLLoginInstance() { } -void LLLoginInstance::connect(const LLSD& credentials) +void LLLoginInstance::connect(LLPointer credentials) { std::vector 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 credentials) { mAttemptComplete = false; // Reset attempt complete at this point! constructAuthParams(credentials); @@ -102,7 +103,7 @@ void LLLoginInstance::reconnect() // Sort of like connect, only using the pre-existing // request params. std::vector uris; - LLViewerLogin::getInstance()->getLoginURIs(uris); + LLGridManager::getInstance()->getLoginURIs(uris); mLoginModule->connect(uris.front(), mRequestData); } @@ -118,7 +119,7 @@ LLSD LLLoginInstance::getResponse() return mResponseData; } -void LLLoginInstance::constructAuthParams(const LLSD& credentials) +void LLLoginInstance::constructAuthParams(LLPointer user_credential) { // Set up auth request options. //#define LL_MINIMIAL_REQUESTED_OPTIONS @@ -155,20 +156,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 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 #include +#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 credentials); // Connect to the current grid choice. + void connect(const std::string& uri, LLPointer 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 user_credentials); void updateApp(bool mandatory, const std::string& message); bool updateDialogCallback(const LLSD& notification, const LLSD& response); diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 87d101b00f..6978d05389 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -51,6 +51,7 @@ #include "llfocusmgr.h" #include "lllineeditor.h" #include "llnotificationsutil.h" +#include "llsecapi.h" #include "llstartup.h" #include "lltextbox.h" #include "llui.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; @@ -187,6 +189,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 +216,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, } #if !USE_VIEWER_AUTH - childSetPrevalidate("first_name_edit", LLLineEditor::prevalidateASCIIPrintableNoSpace); - childSetPrevalidate("last_name_edit", LLLineEditor::prevalidateASCIIPrintableNoSpace); - - childSetCommitCallback("password_edit", mungePassword, this); + childSetPrevalidate("username_edit", LLLineEditor::prevalidateASCIIPrintableNoPipe); getChild("password_edit")->setKeystrokeCallback(onPassKey, this); // change z sort of clickable text to be behind buttons @@ -248,6 +248,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, LLComboBox* server_choice_combo = sInstance->getChild("server_combo"); server_choice_combo->setCommitCallback(onSelectServer, NULL); server_choice_combo->setFocusLostCallback(boost::bind(onServerComboLostFocus, _1)); + updateServerCombo(); childSetAction("connect_btn", onClickConnect, this); @@ -303,12 +304,8 @@ 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 ); + + LLHTTPClient::head( LLGridManager::getInstance()->getLoginPage(), gResponsePtr ); #if !USE_VIEWER_AUTH // Initialize visibility (and don't force visibility - use prefs) @@ -377,21 +374,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() { @@ -498,14 +480,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. @@ -514,7 +496,7 @@ void LLPanelLogin::giveFocus() else { // User doesn't have a name, so start there. - edit = sInstance->getChild("first_name_edit"); + edit = sInstance->getChild("username_edit"); } if (edit) @@ -559,77 +541,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 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& 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()->getGridName()); - LLComboBox* combo = sInstance->getChild("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()->getGridName(), identifier, authenticator); + remember = sInstance->childGetValue("remember_check"); } // static @@ -682,6 +707,7 @@ void LLPanelLogin::refreshLocation( bool force_visible ) sInstance->childSetVisible("start_location_combo", show_start); sInstance->childSetVisible("start_location_text", show_start); + // should be true for enterprise viewer BOOL show_server = gSavedSettings.getBOOL("ForceShowGrid"); sInstance->childSetVisible("server_combo", show_server); @@ -721,15 +747,12 @@ 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) { @@ -761,11 +784,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()->getGridLabel().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()); @@ -790,30 +812,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); @@ -896,34 +908,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("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=="" || combo_text =="") + LLComboBox* combo = sInstance->getChild("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()) + { + LLNotificationsUtil::add("StartRegionEmpty"); + return; + } + try { - // *NOTE: Mani - Location field is not always committed by this point! - LLURLSimString::sInstance.setString(combo_text); - has_location = true; + 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 { @@ -986,6 +997,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"); @@ -993,6 +1006,34 @@ void LLPanelLogin::onPassKey(LLLineEditor* caller, void* user_data) } } +void LLPanelLogin::updateServerCombo() +{ + // We add all of the possible values, sorted, and then add a bar and the current value at the top + LLGridManager* viewer_login = LLGridManager::getInstance(); + LLComboBox* server_choice_combo = sInstance->getChild("server_combo"); + server_choice_combo->removeall(); + std::map known_grids = viewer_login->getKnownGrids(); + for (std::map::iterator grid_choice = known_grids.begin(); + grid_choice != known_grids.end(); + grid_choice++) + { + //if (!grid_choice->first.empty()) + { + LL_INFOS("Credentials") << "adding " << grid_choice->second << ":" << grid_choice->first << LL_ENDL; + server_choice_combo->add(grid_choice->second, grid_choice->first, ADD_SORTED); + } + } + + server_choice_combo->addSeparator(ADD_TOP); + + LL_INFOS("Credentials") << "adding top grid choice by " << viewer_login->getGridLabel() << LL_ENDL; + server_choice_combo->add(viewer_login->getGridLabel(), + viewer_login->getGridName(), + ADD_TOP); + + server_choice_combo->selectFirstItem(); +} + // static void LLPanelLogin::onSelectServer(LLUICtrl*, void*) { @@ -1002,45 +1043,34 @@ void LLPanelLogin::onSelectServer(LLUICtrl*, void*) // The user twiddled with the grid choice ui. // apply the selection to the grid setting. - std::string grid_label; - S32 grid_index; + LLPointer credential; + BOOL remember = FALSE; LLComboBox* combo = sInstance->getChild("server_combo"); - LLSD combo_val = combo->getValue(); - - if (LLSD::TypeInteger == combo_val.type()) - { - grid_index = combo->getValue().asInteger(); - - if ((S32)GRID_INFO_OTHER == grid_index) - { - // This happens if the user specifies a custom grid - // via command line. - grid_label = combo->getSimple(); - } - } - else + LLSD combo_val = combo->getSelectedValue(); + if (combo_val.isUndefined()) { - // no valid selection, return other - grid_index = (S32)GRID_INFO_OTHER; - grid_label = combo_val.asString(); + combo_val = combo->getValue(); } - // This new seelction will override preset uris + // This new selection will override preset uris // from the command line. - LLViewerLogin* vl = LLViewerLogin::getInstance(); - vl->resetURIs(); - if(grid_index != GRID_INFO_OTHER) - { - vl->setGridChoice((EGridInfo)grid_index); - } - else - { - vl->setGridChoice(grid_label); - } + + LLGridManager::getInstance()->setGridChoice(combo_val.asString()); + updateServerCombo(); // grid changed so show new splash screen (possibly) loadLoginPage(); + + // if they've selected another grid, we should load the credentials + // for that grid and set them to the UI. + credential = gSecAPIHandler->loadCredential(LLGridManager::getInstance()->getGridName()); + + + remember = sInstance->childGetValue("remember_check"); + sInstance->setFields(credential, remember); + + LL_INFOS("Credentials") << "Grid changed to:" << LLGridManager::getInstance()->getGridName() << LL_ENDL; } void LLPanelLogin::onServerComboLostFocus(LLFocusableElement* fe) diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h index 97350ce5c7..d33aa2d550 100644 --- a/indra/newview/llpanellogin.h +++ b/indra/newview/llpanellogin.h @@ -37,6 +37,7 @@ #include "llpointer.h" // LLPointer<> #include "llmediactrl.h" // LLMediaCtrlObserver #include +#include "llsecapi.h" class LLLineEditor; class LLUIImage; @@ -65,15 +66,11 @@ 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 credential, BOOL remember); - static void addServer(const std::string& server, S32 domain_name); static void refreshLocation( bool force_visible ); - static void getFields(std::string *firstname, std::string *lastname, - std::string *password); + static void getFields(LLPointer& credential, BOOL& remember); static BOOL isGridComboDirty(); static void getLocation(std::string &location); @@ -85,7 +82,6 @@ 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); @@ -102,6 +98,7 @@ private: static void onPassKey(LLLineEditor* caller, void* user_data); static void onSelectServer(LLUICtrl*, void*); static void onServerComboLostFocus(LLFocusableElement*); + static void updateServerCombo(); private: LLPointer mLogoImage; @@ -110,8 +107,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/llsecapi.cpp b/indra/newview/llsecapi.cpp index c2cfde0dc7..cdf4a3fe01 100644 --- a/indra/newview/llsecapi.cpp +++ b/indra/newview/llsecapi.cpp @@ -38,11 +38,17 @@ std::map > gHandlerMap; - +LLPointer gSecAPIHandler; void initializeSecHandler() { 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]; } // start using a given security api handler. If the string is empty // the default is used @@ -64,6 +70,28 @@ void registerSecHandler(const std::string& handler_type, gHandlerMap[handler_type] = handler; } +std::ostream& operator <<(std::ostream& s, const LLCredential& cred) +{ + return s << (std::string)cred; +} - +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["username"]; + result["passwd"] = mAuthenticator["secret"]; + + } + return result; +} diff --git a/indra/newview/llsecapi.h b/indra/newview/llsecapi.h index 743d3d6770..d456ca95b1 100644 --- a/indra/newview/llsecapi.h +++ b/indra/newview/llsecapi.h @@ -34,6 +34,7 @@ #define LLSECAPI_H #include #include +#include // All error handling is via exceptions. @@ -156,7 +157,7 @@ public: LLCertificateStore() {} virtual ~LLCertificateStore() {} - virtual X509_STORE getOpenSSLX509Store()=0; // return an openssl X509_STORE + virtual X509_STORE* getOpenSSLX509Store()=0; // return an openssl X509_STORE // for this store // add a copy of a cert to the store @@ -169,7 +170,8 @@ public: virtual void remove(int index)=0; // return a certificate at the index - virtual LLPointer& operator[](int index)=0; + virtual LLPointer operator[](int index)=0; + // return the number of certs in the store virtual int len() const =0; @@ -186,6 +188,49 @@ public: virtual bool validate(const LLCertificateChain& cert_chain) const=0; }; +// +// 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); + // LLSecAPIHandler Class // Interface handler class for the various security storage handlers. @@ -219,9 +264,24 @@ public: // 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 createCredential(const std::string& grid, + const LLSD& identifier, + const LLSD& authenticator)=0; + + virtual LLPointer loadCredential(const std::string& grid)=0; + + virtual void saveCredential(LLPointer cred, bool save_authenticator)=0; + + virtual void deleteCredential(LLPointer cred)=0; + }; -void secHandlerInitialize(); +void initializeSecHandler(); // retrieve a security api depending on the api type LLPointer getSecHandler(const std::string& handler_type); @@ -229,4 +289,6 @@ LLPointer getSecHandler(const std::string& handler_type); void registerSecHandler(const std::string& handler_type, LLPointer& handler); +extern LLPointer gSecAPIHandler; + #endif // LL_SECAPI_H diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index ab6bf034f1..4180f578b9 100644 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -35,8 +35,11 @@ #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 #include #include @@ -276,22 +279,94 @@ std::string cert_get_digest(const std::string& digest_type, X509 *cert) } +// +// LLBasicCertificateStore +// +LLBasicCertificateStore::LLBasicCertificateStore(const std::string& filename) +{ +} +LLBasicCertificateStore::LLBasicCertificateStore(const X509_STORE* store) +{ +} + +LLBasicCertificateStore::~LLBasicCertificateStore() +{ +} + + +X509_STORE* LLBasicCertificateStore::getOpenSSLX509Store() +{ + return NULL; +} + + // add a copy of a cert to the store +void LLBasicCertificateStore::append(const LLCertificate& cert) +{ +} + + // add a copy of a cert to the store +void LLBasicCertificateStore::insert(const int index, const LLCertificate& cert) +{ +} + + // remove a certificate from the store +void LLBasicCertificateStore::remove(int index) +{ +} + + // return a certificate at the index +LLPointer LLBasicCertificateStore::operator[](int index) +{ + LLPointer result = NULL; + return result; +} + // return the number of certs in the store +int LLBasicCertificateStore::len() const +{ + return 0; +} + + // load the store from a persisted location +void LLBasicCertificateStore::load(const std::string& store_id) +{ +} + + // persist the store +void LLBasicCertificateStore::save() +{ +} + + // return the store id +std::string LLBasicCertificateStore::storeId() +{ + return std::string(""); +} + + // validate a cert chain +bool LLBasicCertificateStore::validate(const LLCertificateChain& cert_chain) const +{ + return FALSE; +} + // 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) +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; _readProtectedData(); } LLSecAPIBasicHandler::LLSecAPIBasicHandler() { - std::string mProtectedDataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, - "bin_conf.dat"); + mProtectedDataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, + "bin_conf.dat"); + mLegacyPasswordPath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "password.dat"); mProtectedDataMap = LLSD::emptyMap(); _readProtectedData(); @@ -306,7 +381,6 @@ void LLSecAPIBasicHandler::_readProtectedData() { // attempt to load the file into our map LLPointer parser = new LLSDXMLParser(); - llifstream protected_data_stream(mProtectedDataFilename.c_str(), llifstream::binary); if (!protected_data_stream.fail()) { @@ -314,21 +388,27 @@ void LLSecAPIBasicHandler::_readProtectedData() U8 salt[STORE_SALT_SIZE]; U8 buffer[BUFFER_READ_SIZE]; U8 decrypted_buffer[BUFFER_READ_SIZE]; - int decrypted_length; - + 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("Corrupt Protected Data Store"); + throw LLProtectedDataException("Corrupt Protected Data Store1"); } - + + 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. - // It would be better to use the password, but as this store + // 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. @@ -369,6 +449,7 @@ void LLSecAPIBasicHandler::_writeProtectedData() U8 salt[STORE_SALT_SIZE]; U8 buffer[BUFFER_READ_SIZE]; U8 encrypted_buffer[BUFFER_READ_SIZE]; + if(mProtectedDataMap.isUndefined()) { @@ -377,10 +458,10 @@ void LLSecAPIBasicHandler::_writeProtectedData() } // 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. @@ -394,7 +475,12 @@ void LLSecAPIBasicHandler::_writeProtectedData() EVP_CIPHER_CTX ctx; EVP_CIPHER_CTX_init(&ctx); EVP_EncryptInit(&ctx, EVP_rc4(), salt, NULL); - protected_data_stream.write((const char *)salt, STORE_SALT_SIZE); + 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); @@ -423,7 +509,8 @@ void LLSecAPIBasicHandler::_writeProtectedData() } // move the temporary file to the specified file location. - if((LLFile::remove(mProtectedDataFilename) != 0) || + if((((LLFile::isfile(mProtectedDataFilename) != 0) && + (LLFile::remove(mProtectedDataFilename) != 0))) || (LLFile::rename(tmp_filename, mProtectedDataFilename))) { LLFile::remove(tmp_filename); @@ -477,14 +564,181 @@ LLSD LLSecAPIBasicHandler::getProtectedData(const std::string& data_type, 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) + 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; -} \ No newline at end of file +} + +// +// Create a credential object from an identifier and authenticator. credentials are +// per grid. +LLPointer LLSecAPIBasicHandler::createCredential(const std::string& grid, + const LLSD& identifier, + const LLSD& authenticator) +{ + LLPointer result = new LLSecAPIBasicCredential(grid); + result->setCredentialData(identifier, authenticator); + return result; +} + +// Load a credential from the credential store, given the grid +LLPointer LLSecAPIBasicHandler::loadCredential(const std::string& grid) +{ + LLSD credential = getProtectedData("credential", grid); + LLPointer 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 cred, bool save_authenticator) +{ + LLSD credential = LLSD::emptyMap(); + credential["identifier"] = cred->getIdentifier(); + if (save_authenticator) + { + credential["authenticator"] = cred->getAuthenticator(); + } + LL_INFOS("Credentials") << "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? +} + +// Remove a credential from the credential store. +void LLSecAPIBasicHandler::deleteCredential(LLPointer cred) +{ + LLSD undefVal; + deleteProtectedData("credential", cred->getGrid()); + cred->setCredentialData(undefVal, undefVal); +} + +// load the legacy hash for agni, and decrypt it given the +// mac address +std::string LLSecAPIBasicHandler::_legacyLoadPassword() +{ + const S32 HASHED_LENGTH = 32; + std::vector 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)"; +} + + diff --git a/indra/newview/llsechandler_basic.h b/indra/newview/llsechandler_basic.h index 0ec6938583..5d81b6e190 100644 --- a/indra/newview/llsechandler_basic.h +++ b/indra/newview/llsechandler_basic.h @@ -68,13 +68,69 @@ protected: X509* mCert; }; +// 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 : public LLCertificateStore +{ +public: + LLBasicCertificateStore(const std::string& filename); + LLBasicCertificateStore(const X509_STORE* store); + virtual ~LLBasicCertificateStore(); + + virtual X509_STORE* getOpenSSLX509Store(); // return an openssl X509_STORE + // for this store + + // add a copy of a cert to the store + virtual void append(const LLCertificate& cert); + + // add a copy of a cert to the store + virtual void insert(const int index, const LLCertificate& cert); + + // remove a certificate from the store + virtual void remove(int index); + + // return a certificate at the index + virtual LLPointer operator[](int index); + // return the number of certs in the store + virtual int len() const; + + // load the store from a persisted location + virtual void load(const std::string& store_id); + + // persist the store + virtual void save(); + + // return the store id + virtual std::string storeId(); + + // validate a cert chain + virtual bool validate(const LLCertificateChain& cert_chain) const; +}; + +// 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); + LLSecAPIBasicHandler(const std::string& protected_data_filename, + const std::string& legacy_password_path); LLSecAPIBasicHandler(); virtual ~LLSecAPIBasicHandler(); @@ -102,12 +158,32 @@ public: // 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 createCredential(const std::string& grid, + const LLSD& identifier, + const LLSD& authenticator); + + virtual LLPointer loadCredential(const std::string& grid); + + virtual void saveCredential(LLPointer cred, bool save_authenticator); + + virtual void deleteCredential(LLPointer cred); + protected: void _readProtectedData(); void _writeProtectedData(); - + std::string _legacyLoadPassword(); + std::string mProtectedDataFilename; LLSD mProtectedDataMap; + + std::string mLegacyPasswordPath; }; #endif // LLSECHANDLER_BASIC diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 99fa271b78..dd991c8eff 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -230,10 +230,9 @@ static bool gUseCircuitCallbackCalled = false; EStartupState LLStartUp::gStartupState = STATE_FIRST; -// *NOTE:Mani - to reconcile with giab changes... -static std::string gFirstname; -static std::string gLastname; -static std::string gPassword; +static LLPointer gUserCredential; +static std::string gDisplayName; +static BOOL gRememberPassword = TRUE; static U64 gFirstSimHandle = 0; static LLHost gFirstSim; @@ -250,7 +249,6 @@ boost::scoped_ptr 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); @@ -716,69 +714,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(LLStartUp::sSLURLCommand); } - 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; @@ -810,8 +764,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(LLStartUp::sSLURLCommand); + } if (gNoRender) { LL_ERRS("AppInit") << "Need to autologin or use command line with norender!" << LL_ENDL; @@ -822,8 +778,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); @@ -890,36 +848,32 @@ 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->setPerAccountChatLogsDir(userid); LLFile::mkdir(gDirUtilp->getLindenUserDir()); // Set PerAccountSettingsFile to the default value. @@ -952,8 +906,6 @@ bool idle_startup() { gDirUtilp->setChatLogsDir(gSavedPerAccountSettings.getString("InstantMessageLogPath")); } - - gDirUtilp->setPerAccountChatLogsDir(gFirstname, gLastname); LLFile::mkdir(gDirUtilp->getChatLogsDir()); LLFile::mkdir(gDirUtilp->getPerAccountChatLogsDir()); @@ -1052,7 +1004,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"); @@ -1076,11 +1028,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; @@ -1128,8 +1076,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" @@ -1166,7 +1114,8 @@ bool idle_startup() if(process_login_success_response()) { // Pass the user information to the voice chat server interface. - gVoiceClient->userAuthorized(gFirstname, gLastname, gAgentID); + gVoiceClient->userAuthorized(gUserCredential->userID(), gAgentID); + LLGridManager::getInstance()->setFavorite(); LLStartUp::setStartupState( STATE_WORLD_INIT); } else @@ -2115,20 +2064,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. @@ -2144,9 +2082,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 @@ -2169,142 +2104,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); @@ -2881,33 +2680,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 @@ -3108,7 +2919,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..28bc7fcd2a 100644 --- a/indra/newview/llstartup.h +++ b/indra/newview/llstartup.h @@ -101,14 +101,6 @@ 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 diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 1bff04352c..00f4454fab 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -424,7 +424,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" ); } @@ -439,7 +439,7 @@ void init_menus() gMenuHolder->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 @@ -3415,7 +3415,7 @@ void set_god_level(U8 god_level) if(gViewerWindow) { gViewerWindow->setMenuBackgroundColor(god_level > GOD_NOT, - LLViewerLogin::getInstance()->isInProductionGrid()); + LLGridManager::getInstance()->isInProductionGrid()); } LLSD args; @@ -3455,7 +3455,7 @@ BOOL check_toggle_hacked_godmode(void*) bool enable_toggle_hacked_godmode(void*) { - return !LLViewerLogin::getInstance()->isInProductionGrid(); + return !LLGridManager::getInstance()->isInProductionGrid(); } #endif @@ -4320,7 +4320,7 @@ BOOL enable_take() return TRUE; #else # ifdef TOGGLE_HACKED_GODLIKE_VIEWER - if (!LLViewerLogin::getInstance()->isInProductionGrid() + if (!LLGridManager::getInstance()->isInProductionGrid() && gAgent.isGodlike()) { return TRUE; @@ -4947,7 +4947,7 @@ bool enable_object_delete() TRUE; #else # ifdef TOGGLE_HACKED_GODLIKE_VIEWER - (!LLViewerLogin::getInstance()->isInProductionGrid() + (!LLGridManager::getInstance()->isInProductionGrid() && gAgent.isGodlike()) || # endif LLSelectMgr::getInstance()->canDoDelete(); @@ -6556,7 +6556,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 { @@ -6618,7 +6618,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/llviewernetwork.cpp b/indra/newview/llviewernetwork.cpp index d7b55d7e97..fcfaf1eef2 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-2007, 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,302 +33,462 @@ #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 = "http://slurl.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 +// + + +// +// 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, + mGridName.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()) + { + llsd_xml.open( grid_file.c_str(), std::ios::in | std::ios::binary ); -void LLViewerLogin::setGridChoice(EGridInfo grid) -{ - if(grid < 0 || grid >= GRID_INFO_COUNT) + // 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. + LLSD cmd_line_grid = gSavedSettings.getString("CmdLineGridChoice"); + if (gSavedSettings.controlExists("CmdLineGridChoice")) { - llerrs << "Invalid grid index specified." << llendl; + mGridName = gSavedSettings.getString("CmdLineGridChoice"); + LL_INFOS("GridManager") << "Grid Name: " << mGridName << 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 (mGridName.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 + mGridName = uri.hostName(); } - else if(GRID_INFO_OTHER == mGridChoice) + + grid[GRID_NAME_VALUE] = mGridName; + + if (mGridList.has(mGridName) && mGridList[mGridName].has(GRID_LABEL_VALUE)) { - // *FIX:Mani - could this possibly be valid? - mGridName = "other"; + grid[GRID_LABEL_VALUE] = mGridList[mGridName][GRID_LABEL_VALUE]; } else { - mGridName = gGridInfo[mGridChoice].mLabel; + grid[GRID_LABEL_VALUE] = mGridName; + } + if(!cmd_line_helper_uri.empty()) + { + grid[GRID_HELPER_URI_VALUE] = cmd_line_helper_uri; + } + + 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(mGridName) && mGridList[mGridName].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 (mGridName.empty()) + { - gSavedSettings.setS32("ServerChoice", mGridChoice); - gSavedSettings.setString("CustomServer", ""); + mGridName = gSavedSettings.getString("CurrentGrid"); } -} -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); - } - } + if (mGridName.empty() || !mGridList.has(mGridName)) + { + // 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 " << mGridName << " is not in the list" << LL_ENDL; +#if LL_RELEASE_FOR_DOWNLOAD + mGridName = MAINGRID; +#else + mGridName = ""; +#endif + } + LL_INFOS("GridManager") << "Selected grid is " << mGridName << LL_ENDL; + gSavedSettings.setString("CurrentGrid", 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_NAME_VALUE)) + { + std::string grid_name = utf8str_tolower(grid_data[GRID_NAME_VALUE]); + + // grid_name should be in the form of a dns address + if (!grid_name.empty() && + grid_name.find_first_not_of("abcdefghijklmnopqrstuvwxyz1234567890-_. ") != std::string::npos) + { + printf("grid name: %s", grid_name.c_str()); + throw LLInvalidGridName(grid_name); + } + + // populate the other values if they don't exist + if (!grid_data.has(GRID_LABEL_VALUE)) + { + grid_data[GRID_LABEL_VALUE] = grid_name; + } + if (!grid_data.has(GRID_ID_VALUE)) + { + grid_data[GRID_ID_VALUE] = grid_name; + } + + // if the grid data doesn't include any of the URIs, then + // generate them from the grid_name, 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_name + "/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_name + "/app/login/"; + } + if (!grid_data.has(GRID_HELPER_URI_VALUE)) + { + grid_data[GRID_HELPER_URI_VALUE] = std::string("https://") + grid_name + "/helpers/"; + } + LL_INFOS("GridManager") << "ADDING: " << grid_name << LL_ENDL; + mGridList[grid_name] = 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_NAME_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_SLURL_BASE] = SYSTEM_GRID_SLURL_BASE; + 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)) { - return gGridInfo[grid_index].mLabel; + grid[GRID_IS_FAVORITE_VALUE] = TRUE; } - return gGridInfo[GRID_INFO_NONE].mLabel; + addGrid(grid); } -void LLViewerLogin::getLoginURIs(std::vector& uris) const +// return a list of grid name -> grid label mappings for UI purposes +std::map LLGridManager::getKnownGrids(bool favorite_only) { - // return the login uri set on the command line. - LLControlVariable* c = gSavedSettings.getControl("CmdLineLoginURI"); - if(c) + std::map 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_name) +{ + // 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_name == grid_iter->first) || + (grid_name == grid_iter->second[GRID_LABEL_VALUE].asString())) { - uris.push_back(gGridInfo[mGridChoice].mLoginURI); - } - else - { - uris.push_back(mGridName); + mGridName = grid_iter->second[GRID_NAME_VALUE].asString(); + gSavedSettings.setString("CurrentGrid", grid_iter->second[GRID_NAME_VALUE]); + return; + } } + LLSD grid = LLSD::emptyMap(); + grid[GRID_NAME_VALUE] = grid_name; + addGrid(grid); + mGridName = grid_name; + gSavedSettings.setString("CurrentGrid", grid_name); } -std::string LLViewerLogin::getHelperURI() const +void LLGridManager::getLoginURIs(std::vector& uris) { - std::string helper_uri = gSavedSettings.getString("CmdLineHelperURI"); - if (helper_uri.empty()) + uris.clear(); + for (LLSD::array_iterator llsd_uri = mGridList[mGridName][GRID_LOGIN_URI_VALUE].beginArray(); + llsd_uri != mGridList[mGridName][GRID_LOGIN_URI_VALUE].endArray(); + llsd_uri++) { - // grab URI from selected grid - if(mGridChoice > GRID_INFO_NONE && mGridChoice < GRID_INFO_OTHER) - { - helper_uri = gGridInfo[mGridChoice].mHelperURI; - } - - 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 - } + 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 uris; getLoginURIs(uris); + if (uris.size() < 1) + { + return 1; + } LLStringUtil::toLower(uris[0]); if((uris[0].find("agni") != std::string::npos)) { @@ -338,3 +497,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_name) +{ + std::string grid_base; + if(mGridList.has(grid_name) && mGridList[grid_name].has(GRID_SLURL_BASE)) + { + return mGridList[grid_name][GRID_SLURL_BASE].asString(); + } + else + { + return llformat(DEFAULT_SLURL_BASE, grid_name.c_str()); + } +} + +// build a slurl for the given region within the selected grid +std::string LLGridManager::getAppSLURLBase(const std::string& grid_name) +{ + std::string grid_base; + if(mGridList.has(grid_name) && mGridList[grid_name].has(GRID_APP_SLURL_BASE)) + { + return mGridList[grid_name][GRID_APP_SLURL_BASE].asString(); + } + else + { + return llformat(DEFAULT_APP_SLURL_BASE, grid_name.c_str()); + } +} diff --git a/indra/newview/llviewernetwork.h b/indra/newview/llviewernetwork.h index edae6dc47b..7b3ce9c499 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-2007, 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,137 @@ #ifndef LL_LLVIEWERNETWORK_H #define LL_LLVIEWERNETWORK_H + +extern const char* DEFAULT_LOGIN_PAGE; + +#define GRID_NAME_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 +// 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_name) : mGridName(grid_name) + { + } +protected: + std::string mGridName; }; + /** - * @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 +class LLGridManager : public LLSingleton { 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& uris) const; - std::string getHelperURI() const; + // retrieve a map of grid-name <-> label + // by default only return the user visible grids + std::map getKnownGrids(bool favorites_only=FALSE); + + LLSD getGridInfo(const std::string& grid_name) + { + if(mGridList.has(grid_name)) + { + return mGridList[grid_name]; + } + 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_name); + + + std::string getGridLabel() + { + return mGridList[mGridName][GRID_LABEL_VALUE]; + } + std::string getGridName() const { return mGridName; } + void getLoginURIs(std::vector& uris); + std::string getHelperURI() {return mGridList[mGridName][GRID_HELPER_URI_VALUE];} + std::string getLoginPage() {return mGridList[mGridName][GRID_LOGIN_PAGE_VALUE];} + std::string getGridID() { return mGridList[mGridName][GRID_ID_VALUE]; } + std::string getLoginPage(const std::string& grid_name) { return mGridList[grid_name][GRID_LOGIN_PAGE_VALUE]; } + + // build a slurl for the given region within the selected grid + std::string getSLURLBase(const std::string& grid_name); + std::string getSLURLBase() { return getSLURLBase(mGridName); } + + std::string getAppSLURLBase(const std::string& grid_name); + std::string getAppSLURLBase() { return getAppSLURLBase(mGridName); } + + LLSD getGridInfo() { return mGridList[mGridName]; } + + 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(mGridName); } + // 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[mGridName][GRID_IS_FAVORITE_VALUE] = TRUE; } + bool isInProductionGrid(); + void saveFavorites(); + void clearFavorites(); + +protected: -private: - EGridInfo mGridChoice; + // 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 mGridName; + 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 3c79045cc5..ce7b45bc11 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -4705,7 +4705,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; @@ -4742,7 +4742,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; @@ -4766,7 +4766,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; @@ -4790,7 +4790,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; @@ -4814,7 +4814,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; @@ -4838,7 +4838,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 8059f866ba..3f9a10a4a6 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -767,9 +767,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 83cbc8a1f9..f8e08dbf7d 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1815,7 +1815,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 ) @@ -1836,15 +1836,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" ); } diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index 423c46e14c..fc264a6fcf 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -1327,18 +1327,11 @@ void LLVoiceClient::connectorShutdown() } } -void LLVoiceClient::userAuthorized(const std::string& firstName, const std::string& lastName, const LLUUID &agentID) +void LLVoiceClient::userAuthorized(const std::string& user_id, const LLUUID &agentID) { - mAccountFirstName = firstName; - mAccountLastName = lastName; + LL_INFOS("Voice") << "name \"" << user_id << "\" , ID " << agentID << LL_ENDL; - mAccountDisplayName = firstName; - mAccountDisplayName += " "; - mAccountDisplayName += lastName; - - LL_INFOS("Voice") << "name \"" << mAccountDisplayName << "\" , ID " << agentID << LL_ENDL; - - sConnectingToAgni = LLViewerLogin::getInstance()->isInProductionGrid(); + sConnectingToAgni = LLGridManager::getInstance()->isInProductionGrid(); mAccountName = nameFromID(agentID); } diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index 724179847d..075834f7e0 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -428,10 +428,8 @@ static void updatePosition(void); void connectorShutdown(); void requestVoiceAccountProvision(S32 retries = 3); - void userAuthorized( - const std::string& firstName, - const std::string& lastName, - const LLUUID &agentID); + void userAuthorized(const std::string& user_id, + const LLUUID &agentID); void login( const std::string& account_name, const std::string& password, diff --git a/indra/newview/llweb.cpp b/indra/newview/llweb.cpp index 7866f735c5..45b4d3cad3 100644 --- a/indra/newview/llweb.cpp +++ b/indra/newview/llweb.cpp @@ -146,7 +146,7 @@ std::string LLWeb::expandURLSubstitutions(const std::string &url, substitution["VERSION_BUILD"] = LLVersionInfo::getBuild(); substitution["CHANNEL"] = LLVersionInfo::getChannel(); substitution["LANGUAGE"] = LLUI::getLanguage(); - substitution["GRID"] = LLViewerLogin::getInstance()->getGridLabel(); + substitution["GRID"] = LLGridManager::getInstance()->getGridLabel(); substitution["OS"] = LLAppViewer::instance()->getOSInfo().getOSStringSimple(); substitution["SESSION_ID"] = gAgent.getSessionID(); diff --git a/indra/newview/skins/default/xui/en/panel_login.xml b/indra/newview/skins/default/xui/en/panel_login.xml index 6187b8f1e2..6b6013a6ee 100644 --- a/indra/newview/skins/default/xui/en/panel_login.xml +++ b/indra/newview/skins/default/xui/en/panel_login.xml @@ -47,19 +47,19 @@ auto_resize="false" follows="left|bottom" name="login" layout="topleft" -width="695" -min_width="695" +width="775" +min_width="775" user_resize="false" height="80"> -First name: +Username: - - Last name: - +width="150" /> - + + width="307"> + width="307" /> + width="307" /> + @@ -377,7 +434,7 @@ If you're looking for people to hang out with, [secondlife:///app/worldmap try t height="23" layout="topleft" left="8" - top_pad="0" + top_pad="4" name="button_bar" width="313"> - + diff --git a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml index c74de043e9..44c44f5f59 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml @@ -9,33 +9,6 @@ name="Display panel" top="1" width="517"> - - UI size: - - Quality and speed: diff --git a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml index 2123e62daa..8d7b40b386 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_setup.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_setup.xml @@ -9,18 +9,6 @@ name="Input panel" top="1" width="517"> - Mouselook: -- cgit v1.2.3 From 8a22ffa622912f21908b35335e5fdb21e0596a14 Mon Sep 17 00:00:00 2001 From: Leyla Farazha Date: Thu, 1 Apr 2010 15:12:20 -0700 Subject: EXT-5110 Save Outfit dialog is non-standard modal dialog (off-center placement, empty titlebar) reviewed by Richard CC#176 --- indra/newview/llpaneloutfitsinventory.cpp | 113 +++++++-------------- indra/newview/llpaneloutfitsinventory.h | 2 +- .../newview/skins/default/xui/en/notifications.xml | 22 ++++ 3 files changed, 61 insertions(+), 76 deletions(-) diff --git a/indra/newview/llpaneloutfitsinventory.cpp b/indra/newview/llpaneloutfitsinventory.cpp index dd320f8328..7137022447 100644 --- a/indra/newview/llpaneloutfitsinventory.cpp +++ b/indra/newview/llpaneloutfitsinventory.cpp @@ -48,6 +48,7 @@ #include "lllandmark.h" #include "lllineeditor.h" #include "llmodaldialog.h" +#include "llnotificationsutil.h" #include "llsidepanelappearance.h" #include "llsidetray.h" #include "lltabcontainer.h" @@ -68,75 +69,13 @@ static const std::string COF_TAB_NAME = "cof_tab"; static LLRegisterPanelClassWrapper t_inventory("panel_outfits_inventory"); bool LLPanelOutfitsInventory::sShowDebugEditor = false; -class LLOutfitSaveAsDialog : public LLModalDialog -{ -private: - std::string mItemName; - std::string mTempItemName; - - boost::signals2::signal mSaveAsSignal; - -public: - LLOutfitSaveAsDialog( const LLSD& key ) - : LLModalDialog( key ), - mTempItemName(key.asString()) - { - } - - BOOL postBuild() - { - getChild("Save")->setCommitCallback(boost::bind(&LLOutfitSaveAsDialog::onSave, this )); - getChild("Cancel")->setCommitCallback(boost::bind(&LLOutfitSaveAsDialog::onCancel, this )); - - childSetTextArg("name ed", "[DESC]", mTempItemName); - return TRUE; - } - - void setSaveAsCommit( const boost::signals2::signal::slot_type& cb ) - { - mSaveAsSignal.connect(cb); - } - - virtual void onOpen(const LLSD& key) - { - LLLineEditor* edit = getChild("name ed"); - if (edit) - { - edit->setFocus(TRUE); - edit->selectAll(); - } - } - void onSave() - { - mItemName = childGetValue("name ed").asString(); - LLStringUtil::trim(mItemName); - if( !mItemName.empty() ) - { - mSaveAsSignal(mItemName); - closeFloater(); // destroys this object - } - } - - void onCancel() - { - closeFloater(); // destroys this object - } -}; - LLPanelOutfitsInventory::LLPanelOutfitsInventory() : mActivePanel(NULL), mParent(NULL) { mSavedFolderState = new LLSaveFolderState(); mSavedFolderState->setApply(FALSE); - - static bool registered_dialog = false; - if (!registered_dialog) - { - LLFloaterReg::add("outfit_save_as", "floater_outfit_save_as.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); - registered_dialog = true; - } } LLPanelOutfitsInventory::~LLPanelOutfitsInventory() @@ -268,6 +207,31 @@ void LLPanelOutfitsInventory::onEdit() { } +bool LLPanelOutfitsInventory::onSaveCommit(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (0 == option) + { + std::string outfit_name = response["message"].asString(); + LLStringUtil::trim(outfit_name); + if( !outfit_name.empty() ) + { + LLUUID outfit_folder = gAgentWearables.makeNewOutfitLinks(outfit_name); + LLSD key; + LLSideTray::getInstance()->showPanel("panel_outfits_inventory", key); + + if (mAppearanceTabs) + { + mAppearanceTabs->selectTabByName(OUTFITS_TAB_NAME); + } + } + } + + return false; +} + + + void LLPanelOutfitsInventory::onSave() { std::string outfit_name; @@ -277,23 +241,22 @@ void LLPanelOutfitsInventory::onSave() outfit_name = LLViewerFolderType::lookupNewCategoryName(LLFolderType::FT_OUTFIT); } + LLSD args; + args["DESC"] = outfit_name; + + LLSD payload; + //payload["ids"].append(*it); + + LLNotificationsUtil::add("SaveOutfitAs", args, payload, boost::bind(&LLPanelOutfitsInventory::onSaveCommit, this, _1, _2)); + + //) + +/* LLOutfitSaveAsDialog* save_as_dialog = LLFloaterReg::showTypedInstance("outfit_save_as", LLSD(outfit_name), TRUE); if (save_as_dialog) { save_as_dialog->setSaveAsCommit(boost::bind(&LLPanelOutfitsInventory::onSaveCommit, this, _1 )); - } -} - -void LLPanelOutfitsInventory::onSaveCommit(const std::string& outfit_name) -{ - LLUUID outfit_folder = gAgentWearables.makeNewOutfitLinks(outfit_name); - LLSD key; - LLSideTray::getInstance()->showPanel("panel_outfits_inventory", key); - - if (mAppearanceTabs) - { - mAppearanceTabs->selectTabByName(OUTFITS_TAB_NAME); - } + }*/ } void LLPanelOutfitsInventory::onSelectionChange(const std::deque &items, BOOL user_action) diff --git a/indra/newview/llpaneloutfitsinventory.h b/indra/newview/llpaneloutfitsinventory.h index ab25ef0a49..a4d38df740 100644 --- a/indra/newview/llpaneloutfitsinventory.h +++ b/indra/newview/llpaneloutfitsinventory.h @@ -61,7 +61,7 @@ public: void onEdit(); void onSave(); - void onSaveCommit(const std::string& item_name); + bool onSaveCommit(const LLSD& notification, const LLSD& response); void onSelectionChange(const std::deque &items, BOOL user_action); void onSelectorButtonClicked(); diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index c39a91281e..f52996724f 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -2023,6 +2023,28 @@ Would you be my friend? + + Save what I'm wearing as a new Outfit: +
+ + [DESC] (new) + +