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/newview/llviewernetwork.cpp | 683 +++++++++++++++++++++++++------------- 1 file changed, 445 insertions(+), 238 deletions(-) (limited to 'indra/newview/llviewernetwork.cpp') 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()); + } +} -- cgit v1.3 From 9e89819d55a3b6ee7fc56f3efb36f273e4e05c83 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Wed, 8 Jul 2009 00:45:17 +0000 Subject: DEV-34822 - merge with 1.23 certificate notification code -r 118191 ignore-dead-branch --- indra/llmessage/llcurl.cpp | 9 + indra/llmessage/llcurl.h | 1 + indra/newview/CMakeLists.txt | 25 + indra/newview/app_settings/settings.xml | 11 + indra/newview/llfloaterland.cpp | 1 - indra/newview/lllogininstance.cpp | 9 + indra/newview/llsecapi.cpp | 5 + indra/newview/llsecapi.h | 311 +++++-- indra/newview/llsechandler_basic.cpp | 913 +++++++++++++++++++-- indra/newview/llsechandler_basic.h | 149 +++- indra/newview/llstartup.cpp | 163 +++- indra/newview/llviewernetwork.cpp | 16 +- indra/newview/llviewernetwork.h | 4 +- indra/newview/llxmlrpclistener.cpp | 18 +- indra/newview/llxmlrpctransaction.cpp | 103 ++- indra/newview/llxmlrpctransaction.h | 3 + .../newview/skins/default/xui/en/notifications.xml | 42 + indra/newview/tests/llsecapi_test.cpp | 188 +++++ indra/newview/tests/llsechandler_basic_test.cpp | 625 ++++++++++++-- indra/newview/tests/llviewernetwork_test.cpp | 224 ++--- indra/viewer_components/login/lllogin.cpp | 5 + 21 files changed, 2466 insertions(+), 359 deletions(-) create mode 100644 indra/newview/tests/llsecapi_test.cpp (limited to 'indra/newview/llviewernetwork.cpp') diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index dc02367a62..91e11b8c0d 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -895,6 +895,15 @@ void LLCurlEasyRequest::setReadCallback(curl_read_callback callback, void* userd } } +void LLCurlEasyRequest::setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata) +{ + if (mEasy) + { + mEasy->setopt(CURLOPT_SSL_CTX_FUNCTION, (void*)callback); + mEasy->setopt(CURLOPT_SSL_CTX_DATA, userdata); + } +} + void LLCurlEasyRequest::slist_append(const char* str) { if (mEasy) diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h index 1bc1767966..b6a637ae5b 100644 --- a/indra/llmessage/llcurl.h +++ b/indra/llmessage/llcurl.h @@ -229,6 +229,7 @@ public: void setHeaderCallback(curl_header_callback callback, void* userdata); void setWriteCallback(curl_write_callback callback, void* userdata); void setReadCallback(curl_read_callback callback, void* userdata); + void setSSLCtxCallback(curl_ssl_ctx_callback callback, void* userdata); void slist_append(const char* str); void sendRequest(const std::string& url); void requestComplete(); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 4be6fb940e..35f0a5036d 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -1803,6 +1803,31 @@ if (LL_TESTS) "${CMAKE_SOURCE_DIR}/llmessage/tests/test_llsdmessage_peer.py" ) + set(test_libs + ${LLMESSAGE_LIBRARIES} + ${WINDOWS_LIBRARIES} + ${LLVFS_LIBRARIES} + ${LLMATH_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${GOOGLEMOCK_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIBRARIES} + ) + LL_ADD_INTEGRATION_TEST(llsechandler_basic + llsechandler_basic.cpp + "${test_libs}" + ) + + LL_ADD_INTEGRATION_TEST(llsecapi + llsecapi.cpp + "${test_libs}" + ) + + LL_ADD_INTEGRATION_TEST(llviewernetwork + llviewernetwork.cpp + "${test_libs}" + ) + #ADD_VIEWER_BUILD_TEST(llmemoryview viewer) #ADD_VIEWER_BUILD_TEST(llagentaccess viewer) #ADD_VIEWER_BUILD_TEST(llworldmap viewer) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 75ee389750..1641ab0ce1 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -1275,6 +1275,17 @@ Value 0 + CertStore + + Comment + Specifies the Certificate Store for certificate trust verification + Persist + 1 + Type + String + Value + default + ChatBarStealsFocus Comment diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 598a13de15..9b6e24f251 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -42,7 +42,6 @@ #include "llnotificationsutil.h" #include "llparcel.h" #include "message.h" -#include "lluserauth.h" #include "llagent.h" #include "llbutton.h" diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index bb45cc93ea..e4b8becdd7 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -246,6 +246,15 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event) LLSD data(LLSD::emptyMap()); data["message"] = message_response; data["reply_pump"] = TOS_REPLY_PUMP; + if(response.has("error_code")) + { + data["error_code"] = response["error_code"]; + } + if(response.has("certificate")) + { + data["certificate"] = response["certificate"]; + } + LLFloaterReg::showInstance("message_critical", data); LLEventPumps::instance().obtain(TOS_REPLY_PUMP) .listen(TOS_LISTENER_NAME, diff --git a/indra/newview/llsecapi.cpp b/indra/newview/llsecapi.cpp index cdf4a3fe01..70c247c2de 100644 --- a/indra/newview/llsecapi.cpp +++ b/indra/newview/llsecapi.cpp @@ -34,6 +34,7 @@ #include "llviewerprecompiledheaders.h" #include "llsecapi.h" #include "llsechandler_basic.h" +#include #include @@ -42,6 +43,9 @@ LLPointer gSecAPIHandler; void initializeSecHandler() { + OpenSSL_add_all_algorithms(); + OpenSSL_add_all_ciphers(); + OpenSSL_add_all_digests(); gHandlerMap[BASIC_SECHANDLER] = new LLSecAPIBasicHandler(); // Currently, we only have the Basic handler, so we can point the main sechandler @@ -76,6 +80,7 @@ std::ostream& operator <<(std::ostream& s, const LLCredential& cred) } + LLSD LLCredential::getLoginParams() { LLSD result = LLSD::emptyMap(); diff --git a/indra/newview/llsecapi.h b/indra/newview/llsecapi.h index d456ca95b1..6fd12c044a 100644 --- a/indra/newview/llsecapi.h +++ b/indra/newview/llsecapi.h @@ -36,11 +36,16 @@ #include #include +#ifdef LL_WINDOWS +#pragma warning(disable:4250) +#endif // LL_WINDOWS + // All error handling is via exceptions. #define CERT_SUBJECT_NAME "subject_name" #define CERT_ISSUER_NAME "issuer_name" +#define CERT_NAME_CN "commonName" #define CERT_SUBJECT_NAME_STRING "subject_name_string" #define CERT_ISSUER_NAME_STRING "issuer_name_string" @@ -51,23 +56,62 @@ #define CERT_VALID_TO "valid_to" #define CERT_SHA1_DIGEST "sha1_digest" #define CERT_MD5_DIGEST "md5_digest" +#define CERT_HOSTNAME "hostname" +#define CERT_BASIC_CONSTRAINTS "basicConstraints" +#define CERT_BASIC_CONSTRAINTS_CA "CA" +#define CERT_BASIC_CONSTRAINTS_PATHLEN "pathLen" + +#define CERT_KEY_USAGE "keyUsage" +#define CERT_KU_DIGITAL_SIGNATURE "digitalSignature" +#define CERT_KU_NON_REPUDIATION "nonRepudiation" +#define CERT_KU_KEY_ENCIPHERMENT "keyEncipherment" +#define CERT_KU_DATA_ENCIPHERMENT "dataEncipherment" +#define CERT_KU_KEY_AGREEMENT "keyAgreement" +#define CERT_KU_CERT_SIGN "certSigning" +#define CERT_KU_CRL_SIGN "crlSigning" +#define CERT_KU_ENCIPHER_ONLY "encipherOnly" +#define CERT_KU_DECIPHER_ONLY "decipherOnly" #define BASIC_SECHANDLER "BASIC_SECHANDLER" +#define CERT_VALIDATION_DATE "validation_date" + +#define CERT_EXTENDED_KEY_USAGE "extendedKeyUsage" +#define CERT_EKU_SERVER_AUTH SN_server_auth + +// validate the current time lies within +// the validation period of the cert +#define VALIDATION_POLICY_TIME 1 + +// validate that the CA, or some cert in the chain +// lies within the certificate store +#define VALIDATION_POLICY_TRUSTED 2 + +// validate that the subject name of +// the cert contains the passed in hostname +// or validates against the hostname +#define VALIDATION_POLICY_HOSTNAME 4 + + +// validate that the cert contains the SSL EKU +#define VALIDATION_POLICY_SSL_KU 8 + +// validate that the cert contains the SSL EKU +#define VALIDATION_POLICY_CA_KU 16 + +#define VALIDATION_POLICY_CA_BASIC_CONSTRAINTS 32 + +// validate that the cert is correct for SSL +#define VALIDATION_POLICY_SSL (VALIDATION_POLICY_TIME | \ + VALIDATION_POLICY_HOSTNAME | \ + VALIDATION_POLICY_TRUSTED | \ + VALIDATION_POLICY_SSL_KU | \ + VALIDATION_POLICY_CA_BASIC_CONSTRAINTS | \ + VALIDATION_POLICY_CA_KU) + + -// 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 { @@ -96,53 +140,88 @@ public: // return a PEM encoded certificate. The encoding // includes the -----BEGIN CERTIFICATE----- and end certificate elements - virtual std::string getPem()=0; + virtual std::string getPem() const=0; // return a DER encoded certificate - virtual std::vector getBinary()=0; + virtual std::vector getBinary() const=0; // return an LLSD object containing information about the certificate // such as its name, signature, expiry time, serial number - virtual LLSD getLLSD()=0; + virtual LLSD getLLSD() const=0; // return an openSSL X509 struct for the certificate - virtual X509* getOpenSSLX509()=0; + virtual X509* getOpenSSLX509() const=0; }; +// class LLCertificateVector +// base class for a list of certificates. -// class LLCertificateChain -// Class representing a chain of certificates in order, with the -// 0th element being the CA -class LLCertificateChain : public LLRefCount + +class LLCertificateVector : 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() {} + LLCertificateVector() {}; + virtual ~LLCertificateVector() {}; + + // base iterator implementation class, providing + // the functionality needed for the iterator class. + class iterator_impl : public LLRefCount + { + public: + iterator_impl() {}; + virtual ~iterator_impl() {}; + virtual void seek(bool incr)=0; + virtual LLPointer clone() const=0; + virtual bool equals(const LLPointer& _iter) const=0; + virtual LLPointer get()=0; + }; + + // iterator class + class iterator + { + public: + iterator(LLPointer impl) : mImpl(impl) {} + iterator() : mImpl(NULL) {} + iterator(const iterator& _iter) {mImpl = _iter.mImpl->clone(); } + ~iterator() {} + iterator& operator++() { if(mImpl.notNull()) mImpl->seek(true); return *this;} + iterator& operator--() { if(mImpl.notNull()) mImpl->seek(false); return *this;} + + iterator operator++(int) { iterator result = *this; if(mImpl.notNull()) mImpl->seek(true); return result;} + iterator operator--(int) { iterator result = *this; if(mImpl.notNull()) mImpl->seek(false); return result;} + LLPointer operator*() { return mImpl->get(); } + + LLPointer mImpl; + protected: + friend bool operator==(const LLCertificateVector::iterator& _lhs, const LLCertificateVector::iterator& _rhs); + bool equals(const iterator& _iter) const { return mImpl->equals(_iter.mImpl); } + }; - virtual X509_STORE getOpenSSLX509Store()=0; // return an openssl X509_STORE - // for this store + // numeric indexer + virtual LLPointer operator[](int)=0; - virtual void appendCert(const LLCertificate& cert)=0; // append a cert to the end - //of the chain + // Iteration + virtual iterator begin()=0; - virtual LLPointer& operator [](int index)=0; // retrieve a certificate - // from the chain by index - // -1 == end of chain + virtual iterator end()=0; - virtual int len() const =0; // return number of certificates in the chain + // find a cert given params + virtual iterator find(const LLSD& params) =0; - // 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; + // return the number of certs in the store + virtual int size() const = 0; + + // append the cert to the store. if a copy of the cert already exists in the store, it is removed first + virtual void add(LLPointer cert)=0; + + // insert the cert to the store. if a copy of the cert already exists in the store, it is removed first + virtual void insert(iterator location, LLPointer cert)=0; + + // remove a certificate from the store + virtual LLPointer erase(iterator cert)=0; }; @@ -151,43 +230,55 @@ public: // certificates. The store can be persisted, and can be used to validate // a cert chain // -class LLCertificateStore : public LLRefCount +class LLCertificateStore : virtual public LLCertificateVector { + 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; + virtual std::string storeId() const=0; +}; + +// class LLCertificateChain +// Class representing a chain of certificates in order, with the +// first element being the child cert. +class LLCertificateChain : virtual public LLCertificateVector +{ + +public: + LLCertificateChain() {} - // validate a cert chain - virtual bool validate(const LLCertificateChain& cert_chain) const=0; + virtual ~LLCertificateChain() {} + + // validate a certificate chain given the params. + // Will throw exceptions on error + + virtual void validate(int validation_policy, + LLPointer ca_store, + const LLSD& validation_params) =0; }; + + + +inline +bool operator==(const LLCertificateVector::iterator& _lhs, const LLCertificateVector::iterator& _rhs) +{ + return _lhs.equals(_rhs); +} +inline +bool operator!=(const LLCertificateVector::iterator& _lhs, const LLCertificateVector::iterator& _rhs) +{ + return !(_lhs == _rhs); +} + + // // LLCredential - interface for credentials providing the following functionality: // * persistance of credential information based on grid (for saving username/password) @@ -232,6 +323,98 @@ protected: std::ostream& operator <<(std::ostream& s, const LLCredential& cred); +// All error handling is via exceptions. + +class LLCertException +{ +public: + LLCertException(LLPointer cert, const char* msg) + { + + mCert = cert; + + LL_WARNS("SECAPI") << "Certificate Error: " << (std::string)msg << LL_ENDL; + mMsg = (std::string)msg; + } + LLPointer getCert() { return mCert; } + std::string getMessage() { return mMsg; } +protected: + LLPointer mCert; + std::string mMsg; +}; + +class LLInvalidCertificate : public LLCertException +{ +public: + LLInvalidCertificate(LLPointer cert) : LLCertException(cert, "CertInvalid") + { + } +protected: +}; + +class LLCertValidationTrustException : public LLCertException +{ +public: + LLCertValidationTrustException(LLPointer cert) : LLCertException(cert, "CertUntrusted") + { + } +protected: +}; + +class LLCertValidationHostnameException : public LLCertException +{ +public: + LLCertValidationHostnameException(std::string hostname, + LLPointer cert) : LLCertException(cert, "CertInvalidHostname") + { + mHostname = hostname; + } + + std::string getHostname() { return mHostname; } +protected: + std::string mHostname; +}; + +class LLCertValidationExpirationException : public LLCertException +{ +public: + LLCertValidationExpirationException(LLPointer cert, + LLDate current_time) : LLCertException(cert, "CertExpired") + { + mTime = current_time; + } + LLDate GetTime() { return mTime; } +protected: + LLDate mTime; +}; + +class LLCertKeyUsageValidationException : public LLCertException +{ +public: + LLCertKeyUsageValidationException(LLPointer cert) : LLCertException(cert, "CertKeyUsage") + { + } +protected: +}; + +class LLCertBasicConstraintsValidationException : public LLCertException +{ +public: + LLCertBasicConstraintsValidationException(LLPointer cert) : LLCertException(cert, "CertBasicConstraints") + { + } +protected: +}; + +class LLCertValidationInvalidSignatureException : public LLCertException +{ +public: + LLCertValidationInvalidSignatureException(LLPointer cert) : LLCertException(cert, "CertInvalidSignature") + { + } +protected: +}; + // LLSecAPIHandler Class // Interface handler class for the various security storage handlers. class LLSecAPIHandler : public LLRefCount diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp index 4180f578b9..097cc395d7 100644 --- a/indra/newview/llsechandler_basic.cpp +++ b/indra/newview/llsechandler_basic.cpp @@ -20,7 +20,7 @@ * 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 +LLS * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * @@ -42,17 +42,26 @@ #include "llviewercontrol.h" #include #include +#include #include +#include #include #include #include +#include #include #include #include + + // 128 bits of salt data... #define STORE_SALT_SIZE 16 #define BUFFER_READ_SIZE 256 +std::string cert_string_from_asn1_string(ASN1_STRING* value); +LLSD _basic_constraints_ext(X509* cert); +LLSD _key_usage_ext(X509* cert); +LLSD _ext_key_usage_ext(X509* cert); LLBasicCertificate::LLBasicCertificate(const std::string& pem_cert) @@ -61,14 +70,19 @@ LLBasicCertificate::LLBasicCertificate(const std::string& pem_cert) // BIO_new_mem_buf returns a read only bio, but takes a void* which isn't const // so we need to cast it. BIO * pem_bio = BIO_new_mem_buf((void*)pem_cert.c_str(), pem_cert.length()); - + if(pem_bio == NULL) + { + LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL; + throw LLInvalidCertificate(this); + } mCert = NULL; PEM_read_bio_X509(pem_bio, &mCert, 0, NULL); BIO_free(pem_bio); if (!mCert) { - throw LLCertException("Error parsing certificate"); + throw LLInvalidCertificate(this); } + _initLLSD(); } @@ -76,20 +90,23 @@ LLBasicCertificate::LLBasicCertificate(X509* pCert) { if (!pCert || !pCert->cert_info) { - throw LLCertException("Invalid certificate"); + throw LLInvalidCertificate(this); } mCert = X509_dup(pCert); + _initLLSD(); } LLBasicCertificate::~LLBasicCertificate() { - - X509_free(mCert); + if(mCert) + { + X509_free(mCert); + } } // // retrieve the pem using the openssl functionality -std::string LLBasicCertificate::getPem() +std::string LLBasicCertificate::getPem() const { char * pem_bio_chars = NULL; // a BIO is the equivalent of a 'std::stream', and @@ -98,7 +115,8 @@ std::string LLBasicCertificate::getPem() BIO *pem_bio = BIO_new(BIO_s_mem()); if (!pem_bio) { - throw LLCertException("couldn't allocate memory buffer"); + LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL; + return std::string(); } PEM_write_bio_X509(pem_bio, mCert); int length = BIO_get_mem_data(pem_bio, &pem_bio_chars); @@ -109,14 +127,15 @@ std::string LLBasicCertificate::getPem() // get the DER encoding for the cert // DER is a binary encoding format for certs... -std::vector LLBasicCertificate::getBinary() +std::vector LLBasicCertificate::getBinary() const { 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"); + LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL; + return std::vector(); } i2d_X509_bio(der_bio, mCert); int length = BIO_get_mem_data(der_bio, &der_bio_data); @@ -128,32 +147,131 @@ std::vector LLBasicCertificate::getBinary() } -LLSD LLBasicCertificate::getLLSD() +LLSD LLBasicCertificate::getLLSD() const +{ + return mLLSDInfo; +} + +// Initialize the LLSD info for the certificate +LLSD& LLBasicCertificate::_initLLSD() { - 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)); + mLLSDInfo[CERT_SUBJECT_NAME] = cert_name_from_X509_NAME(X509_get_subject_name(mCert)); + mLLSDInfo[CERT_ISSUER_NAME] = cert_name_from_X509_NAME(X509_get_issuer_name(mCert)); + mLLSDInfo[CERT_SUBJECT_NAME_STRING] = cert_string_name_from_X509_NAME(X509_get_subject_name(mCert)); + mLLSDInfo[CERT_ISSUER_NAME_STRING] = cert_string_name_from_X509_NAME(X509_get_issuer_name(mCert)); ASN1_INTEGER *sn = X509_get_serialNumber(mCert); if (sn != NULL) { - result[CERT_SERIAL_NUMBER] = cert_string_from_asn1_integer(sn); + mLLSDInfo[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); + mLLSDInfo[CERT_VALID_TO] = cert_date_from_asn1_time(X509_get_notAfter(mCert)); + mLLSDInfo[CERT_VALID_FROM] = cert_date_from_asn1_time(X509_get_notBefore(mCert)); + mLLSDInfo[CERT_SHA1_DIGEST] = cert_get_digest("sha1", mCert); + mLLSDInfo[CERT_MD5_DIGEST] = cert_get_digest("md5", mCert); + // add the known extensions + mLLSDInfo[CERT_BASIC_CONSTRAINTS] = _basic_constraints_ext(mCert); + mLLSDInfo[CERT_KEY_USAGE] = _key_usage_ext(mCert); + mLLSDInfo[CERT_EXTENDED_KEY_USAGE] = _ext_key_usage_ext(mCert); + return mLLSDInfo; +} + +// Retrieve the basic constraints info +LLSD _basic_constraints_ext(X509* cert) +{ + LLSD result; + BASIC_CONSTRAINTS *bs = (BASIC_CONSTRAINTS *)X509_get_ext_d2i(cert, NID_basic_constraints, NULL, NULL); + if(bs) + { + result = LLSD::emptyMap(); + // Determines whether the cert can be used as a CA + result[CERT_BASIC_CONSTRAINTS_CA] = (bool)bs->ca; + + if(bs->pathlen) + { + // the pathlen determines how deep a certificate chain can be from + // this CA + if((bs->pathlen->type == V_ASN1_NEG_INTEGER) + || !bs->ca) + { + result[CERT_BASIC_CONSTRAINTS_PATHLEN] = 0; + } + else + { + result[CERT_BASIC_CONSTRAINTS_PATHLEN] = (int)ASN1_INTEGER_get(bs->pathlen); + } + } + } + return result; +} +// retrieve the key usage, which specifies how the cert can be used. +// +LLSD _key_usage_ext(X509* cert) +{ + LLSD result; + ASN1_STRING *usage_str = (ASN1_STRING *)X509_get_ext_d2i(cert, NID_key_usage, NULL, NULL); + if(usage_str) + { + result = LLSD::emptyArray(); + long usage = 0; + if(usage_str->length > 0) + { + usage = usage_str->data[0]; + if(usage_str->length > 1) + { + usage |= usage_str->data[1] << 8; + } + } + ASN1_STRING_free(usage_str); + if(usage) + { + if(usage & KU_DIGITAL_SIGNATURE) result.append(LLSD((std::string)CERT_KU_DIGITAL_SIGNATURE)); + if(usage & KU_NON_REPUDIATION) result.append(LLSD((std::string)CERT_KU_NON_REPUDIATION)); + if(usage & KU_KEY_ENCIPHERMENT) result.append(LLSD((std::string)CERT_KU_KEY_ENCIPHERMENT)); + if(usage & KU_DATA_ENCIPHERMENT) result.append(LLSD((std::string)CERT_KU_DATA_ENCIPHERMENT)); + if(usage & KU_KEY_AGREEMENT) result.append(LLSD((std::string)CERT_KU_KEY_AGREEMENT)); + if(usage & KU_KEY_CERT_SIGN) result.append(LLSD((std::string)CERT_KU_CERT_SIGN)); + if(usage & KU_CRL_SIGN) result.append(LLSD((std::string)CERT_KU_CRL_SIGN)); + if(usage & KU_ENCIPHER_ONLY) result.append(LLSD((std::string)CERT_KU_ENCIPHER_ONLY)); + if(usage & KU_DECIPHER_ONLY) result.append(LLSD((std::string)CERT_KU_DECIPHER_ONLY)); + } + } + return result; +} - return result; +// retrieve the extended key usage for the cert +LLSD _ext_key_usage_ext(X509* cert) +{ + LLSD result; + EXTENDED_KEY_USAGE *eku = (EXTENDED_KEY_USAGE *)X509_get_ext_d2i(cert, NID_ext_key_usage, NULL, NULL); + if(eku) + { + result = LLSD::emptyArray(); + while(sk_ASN1_OBJECT_num(eku)) + { + ASN1_OBJECT *usage = sk_ASN1_OBJECT_pop(eku); + if(usage) + { + int nid = OBJ_obj2nid(usage); + if (nid) + { + std::string sn = OBJ_nid2sn(nid); + result.append(sn); + } + ASN1_OBJECT_free(usage); + } + } + } + return result; } -X509* LLBasicCertificate::getOpenSSLX509() +// retrieve an openssl x509 object, +// which must be freed by X509_free +X509* LLBasicCertificate::getOpenSSLX509() const { return X509_dup(mCert); } @@ -203,13 +321,46 @@ LLSD cert_name_from_X509_NAME(X509_NAME* name) std::string cert_string_from_asn1_integer(ASN1_INTEGER* value) { + std::string result; BIGNUM *bn = ASN1_INTEGER_to_BN(value, NULL); - char * ascii_bn = BN_bn2hex(bn); + if(bn) + { + char * ascii_bn = BN_bn2hex(bn); - BN_free(bn); + if(ascii_bn) + { + result = ascii_bn; + OPENSSL_free(ascii_bn); + } + BN_free(bn); + } + 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 result(ascii_bn); - OPENSSL_free(ascii_bn); +std::string cert_string_from_asn1_string(ASN1_STRING* value) +{ + char * string_bio_chars = NULL; + std::string result; + // get a memory bio + BIO *string_bio = BIO_new(BIO_s_mem()); + if(!string_bio) + { + // stream the name into the bio. The name will be in the 'short name' format + ASN1_STRING_print_ex(string_bio, value, ASN1_STRFLGS_RFC2253); + int length = BIO_get_mem_data(string_bio, &string_bio_chars); + result = std::string(string_bio_chars, length); + BIO_free(string_bio); + } + else + { + LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL; + } + return result; } @@ -222,8 +373,9 @@ LLDate cert_date_from_asn1_time(ASN1_TIME* asn1_time) int i = asn1_time->length; if (i < 10) - throw LLCertException("invalid certificate time value"); - + { + return LLDate(); + } // convert the date from the ASN1 time (which is a string in ZULU time), to // a timeval. timestruct.tm_year = (asn1_time->data[0]-'0') * 10 + (asn1_time->data[1]-'0'); @@ -237,19 +389,23 @@ LLDate cert_date_from_asn1_time(ASN1_TIME* asn1_time) 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)); + +#if LL_WINDOWS + return LLDate((F64)_mkgmtime(×truct)); +#else // LL_WINDOWS + return LLDate((F64)timegm(×truct)); +#endif // LL_WINDOWS } + // Generate a string containing a digest. The digest time is 'ssh1' or // 'md5', and the resulting string is of the form "aa:12:5c:' and so on std::string cert_get_digest(const std::string& digest_type, X509 *cert) { - unsigned char digest_data[1024]; - unsigned int len = 1024; + unsigned char digest_data[BUFFER_READ_SIZE]; + unsigned int len = sizeof(digest_data); 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") @@ -262,7 +418,7 @@ std::string cert_get_digest(const std::string& digest_type, X509 *cert) } else { - throw LLCertException("Invalid digest"); + return std::string(); } X509_digest(cert, digest, digest_data, &len); @@ -279,75 +435,588 @@ std::string cert_get_digest(const std::string& digest_type, X509 *cert) } +// class LLBasicCertificateVector +// This class represents a list of certificates, implemented by a vector of certificate pointers. +// it contains implementations of the virtual functions for iterators, search, add, remove, etc. +// + +// Find a certificate in the list. +// It will find a cert that has minimally the params listed, with the values being the same +LLBasicCertificateVector::iterator LLBasicCertificateVector::find(const LLSD& params) +{ + BOOL found = FALSE; + // loop through the entire vector comparing the values in the certs + // against those passed in via the params. + // params should be a map. Only the items specified in the map will be + // checked, but they must match exactly, even if they're maps or arrays. + + for(iterator cert = begin(); + cert != end(); + cert++) + { + + found= TRUE; + LLSD cert_info = (*cert)->getLLSD(); + for (LLSD::map_const_iterator param = params.beginMap(); + param != params.endMap(); + param++) + { + + if (!cert_info.has((std::string)param->first) || + (!valueCompareLLSD(cert_info[(std::string)param->first], param->second))) + { + found = FALSE; + break; + } + } + if (found) + { + return (cert); + } + } + return end(); +} + +// Insert a certificate into the store. If the certificate already +// exists in the store, nothing is done. +void LLBasicCertificateVector::insert(iterator _iter, + LLPointer cert) +{ + LLSD cert_info = cert->getLLSD(); + if (cert_info.isMap() && cert_info.has(CERT_SHA1_DIGEST)) + { + LLSD existing_cert_info = LLSD::emptyMap(); + existing_cert_info[CERT_MD5_DIGEST] = cert_info[CERT_MD5_DIGEST]; + if(find(existing_cert_info) == end()) + { + BasicIteratorImpl *basic_iter = dynamic_cast(_iter.mImpl.get()); + mCerts.insert(basic_iter->mIter, cert); + } + } +} + +// remove a certificate from the store +LLPointer LLBasicCertificateVector::erase(iterator _iter) +{ + + if (_iter != end()) + { + BasicIteratorImpl *basic_iter = dynamic_cast(_iter.mImpl.get()); + LLPointer result = (*_iter); + mCerts.erase(basic_iter->mIter); + return result; + } + return NULL; +} + + // // LLBasicCertificateStore -// +// This class represents a store of CA certificates. The basic implementation +// uses a pem file such as the legacy CA.pem stored in the existing +// SL implementation. LLBasicCertificateStore::LLBasicCertificateStore(const std::string& filename) { + mFilename = filename; + load_from_file(filename); } -LLBasicCertificateStore::LLBasicCertificateStore(const X509_STORE* store) + +void LLBasicCertificateStore::load_from_file(const std::string& filename) { + // scan the PEM file extracting each certificate + BIO* file_bio = BIO_new(BIO_s_file()); + if(file_bio) + { + if (BIO_read_filename(file_bio, filename.c_str()) > 0) + { + X509 *cert_x509 = NULL; + while((PEM_read_bio_X509(file_bio, &cert_x509, 0, NULL)) && + (cert_x509 != NULL)) + { + try + { + add(new LLBasicCertificate(cert_x509)); + } + catch (...) + { + LL_WARNS("SECAPI") << "Failure creating certificate from the certificate store file." << LL_ENDL; + } + X509_free(cert_x509); + cert_x509 = NULL; + } + BIO_free(file_bio); + } + } + else + { + LL_WARNS("SECAPI") << "Could not allocate a file BIO" << LL_ENDL; + } } + LLBasicCertificateStore::~LLBasicCertificateStore() { } - -X509_STORE* LLBasicCertificateStore::getOpenSSLX509Store() + +// persist the store +void LLBasicCertificateStore::save() { - return NULL; + llofstream file_store(mFilename, llofstream::binary); + if(!file_store.fail()) + { + for(iterator cert = begin(); + cert != end(); + cert++) + { + std::string pem = (*cert)->getPem(); + if(!pem.empty()) + { + file_store << (*cert)->getPem() << std::endl; + } + } + file_store.close(); + } + else + { + LL_WARNS("SECAPI") << "Could not open certificate store " << mFilename << "for save" << LL_ENDL; + } } - - // add a copy of a cert to the store -void LLBasicCertificateStore::append(const LLCertificate& cert) + +// return the store id +std::string LLBasicCertificateStore::storeId() const { + // this is the basic handler which uses the CA.pem store, + // so we ignore this. + return std::string(""); } - - // add a copy of a cert to the store -void LLBasicCertificateStore::insert(const int index, const LLCertificate& cert) + + +// +// LLBasicCertificateChain +// This class represents a chain of certs, each cert being signed by the next cert +// in the chain. Certs must be properly signed by the parent +LLBasicCertificateChain::LLBasicCertificateChain(const X509_STORE_CTX* store) { + + // we're passed in a context, which contains a cert, and a blob of untrusted + // certificates which compose the chain. + if((store == NULL) || (store->cert == NULL)) + { + LL_WARNS("SECAPI") << "An invalid store context was passed in when trying to create a certificate chain" << LL_ENDL; + return; + } + // grab the child cert + LLPointer current = new LLBasicCertificate(store->cert); + + add(current); + if(store->untrusted != NULL) + { + // if there are other certs in the chain, we build up a vector + // of untrusted certs so we can search for the parents of each + // consecutive cert. + LLBasicCertificateVector untrusted_certs; + for(int i = 0; i < sk_X509_num(store->untrusted); i++) + { + LLPointer cert = new LLBasicCertificate(sk_X509_value(store->untrusted, i)); + untrusted_certs.add(cert); + + } + while(untrusted_certs.size() > 0) + { + LLSD find_data = LLSD::emptyMap(); + LLSD cert_data = current->getLLSD(); + // we simply build the chain via subject/issuer name as the + // client should not have passed in multiple CA's with the same + // subject name. If they did, it'll come out in the wash during + // validation. + find_data[CERT_SUBJECT_NAME_STRING] = cert_data[CERT_ISSUER_NAME_STRING]; + LLBasicCertificateVector::iterator issuer = untrusted_certs.find(find_data); + if (issuer != untrusted_certs.end()) + { + current = untrusted_certs.erase(issuer); + add(current); + } + else + { + break; + } + } + } } - - // remove a certificate from the store -void LLBasicCertificateStore::remove(int index) + + +// subdomain wildcard specifiers can be divided into 3 parts +// the part before the first *, the part after the first * but before +// the second *, and the part after the second *. +// It then iterates over the second for each place in the string +// that it matches. ie if the subdomain was testfoofoobar, and +// the wildcard was test*foo*bar, it would match test, then +// recursively match foofoobar and foobar + +bool _cert_subdomain_wildcard_match(const std::string& subdomain, + const std::string& wildcard) { + // split wildcard into the portion before the *, and the portion after + + int wildcard_pos = wildcard.find_first_of('*'); + // check the case where there is no wildcard. + if(wildcard_pos == wildcard.npos) + { + return (subdomain == wildcard); + } + + // we need to match the first part of the subdomain string up to the wildcard + // position + if(subdomain.substr(0, wildcard_pos) != wildcard.substr(0, wildcard_pos)) + { + // the first portions of the strings didn't match + return FALSE; + } + + // as the portion of the wildcard string before the * matched, we need to check the + // portion afterwards. Grab that portion. + std::string new_wildcard_string = wildcard.substr( wildcard_pos+1, wildcard.npos); + if(new_wildcard_string.empty()) + { + // we had nothing after the *, so it's an automatic match + return TRUE; + } + + // grab the portion of the remaining wildcard string before the next '*'. We need to find this + // within the remaining subdomain string. and then recursively check. + std::string new_wildcard_match_string = new_wildcard_string.substr(0, new_wildcard_string.find_first_of('*')); + + // grab the portion of the subdomain after the part that matched the initial wildcard portion + std::string new_subdomain = subdomain.substr(wildcard_pos, subdomain.npos); + + // iterate through the current subdomain, finding instances of the match string. + int sub_pos = new_subdomain.find_first_of(new_wildcard_match_string); + while(sub_pos != std::string::npos) + { + new_subdomain = new_subdomain.substr(sub_pos, std::string::npos); + if(_cert_subdomain_wildcard_match(new_subdomain, new_wildcard_string)) + { + return TRUE; + } + sub_pos = new_subdomain.find_first_of(new_wildcard_match_string, 1); + + + } + // didn't find any instances of the match string that worked in the subdomain, so fail. + return FALSE; } - - // return a certificate at the index -LLPointer LLBasicCertificateStore::operator[](int index) + + +// RFC2459 does not address wildcards as part of it's name matching +// specification, and there is no RFC specifying wildcard matching, +// RFC2818 does a few statements about wildcard matching, but is very +// general. Generally, wildcard matching is per implementation, although +// it's pretty similar. +// in our case, we use the '*' wildcard character only, within each +// subdomain. The hostname and the CN specification should have the +// same number of subdomains. +// We then iterate that algorithm over each subdomain. +bool _cert_hostname_wildcard_match(const std::string& hostname, const std::string& common_name) { - LLPointer result = NULL; - return result; + std::string new_hostname = hostname; + std::string new_cn = common_name; + int subdomain_pos = new_hostname.find_first_of('.'); + int subcn_pos = new_cn.find_first_of('.'); + + while((subcn_pos != std::string::npos) && (subdomain_pos != std::string::npos)) + { + // snip out the first subdomain and cn element + + if(!_cert_subdomain_wildcard_match(new_hostname.substr(0, subdomain_pos), + new_cn.substr(0, subcn_pos))) + { + return FALSE; + } + new_hostname = new_hostname.substr(subdomain_pos+1, std::string::npos); + new_cn = new_cn.substr(subcn_pos+1, std::string::npos); + subdomain_pos = new_hostname.find_first_of('.'); + subcn_pos = new_cn.find_first_of('.'); + } + return _cert_subdomain_wildcard_match(new_hostname, new_cn); + } - // return the number of certs in the store -int LLBasicCertificateStore::len() const + +// validate that the LLSD array in llsd_set contains the llsd_value +bool _LLSDArrayIncludesValue(const LLSD& llsd_set, LLSD llsd_value) { - return 0; + for(LLSD::array_const_iterator set_value = llsd_set.beginArray(); + set_value != llsd_set.endArray(); + set_value++) + { + if(valueCompareLLSD((*set_value), llsd_value)) + { + return TRUE; + } + } + return FALSE; } - - // load the store from a persisted location -void LLBasicCertificateStore::load(const std::string& store_id) + +void _validateCert(int validation_policy, + const LLPointer cert, + const LLSD& validation_params, + int depth) { -} + + LLSD current_cert_info = cert->getLLSD(); + // check basic properties exist in the cert + if(!current_cert_info.has(CERT_SUBJECT_NAME) || !current_cert_info.has(CERT_SUBJECT_NAME_STRING)) + { + throw LLCertException(cert, "Cert doesn't have a Subject Name"); + } + + if(!current_cert_info.has(CERT_ISSUER_NAME_STRING)) + { + throw LLCertException(cert, "Cert doesn't have an Issuer Name"); + } + + // check basic properties exist in the cert + if(!current_cert_info.has(CERT_VALID_FROM) || !current_cert_info.has(CERT_VALID_TO)) + { + throw LLCertException(cert, "Cert doesn't have an expiration period"); + } + if (!current_cert_info.has(CERT_SHA1_DIGEST)) + { + throw LLCertException(cert, "No SHA1 digest"); + } + + if (validation_policy & VALIDATION_POLICY_TIME) + { + + LLDate validation_date(time(NULL)); + if(validation_params.has(CERT_VALIDATION_DATE)) + { + validation_date = validation_params[CERT_VALIDATION_DATE]; + } - // persist the store -void LLBasicCertificateStore::save() -{ + if((validation_date < current_cert_info[CERT_VALID_FROM].asDate()) || + (validation_date > current_cert_info[CERT_VALID_TO].asDate())) + { + throw LLCertValidationExpirationException(cert, validation_date); + } + } + if (validation_policy & VALIDATION_POLICY_SSL_KU) + { + if (current_cert_info.has(CERT_KEY_USAGE) && current_cert_info[CERT_KEY_USAGE].isArray() && + (!(_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE], + LLSD((std::string)CERT_KU_DIGITAL_SIGNATURE))) || + !(_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE], + LLSD((std::string)CERT_KU_KEY_ENCIPHERMENT))))) + { + throw LLCertKeyUsageValidationException(cert); + } + // only validate EKU if the cert has it + if(current_cert_info.has(CERT_EXTENDED_KEY_USAGE) && current_cert_info[CERT_EXTENDED_KEY_USAGE].isArray() && + (!_LLSDArrayIncludesValue(current_cert_info[CERT_EXTENDED_KEY_USAGE], + LLSD((std::string)CERT_EKU_SERVER_AUTH)))) + { + throw LLCertKeyUsageValidationException(cert); + } + } + if (validation_policy & VALIDATION_POLICY_CA_KU) + { + if (current_cert_info.has(CERT_KEY_USAGE) && current_cert_info[CERT_KEY_USAGE].isArray() && + (!_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE], + (std::string)CERT_KU_CERT_SIGN))) + { + throw LLCertKeyUsageValidationException(cert); + } + } + + // validate basic constraints + if ((validation_policy & VALIDATION_POLICY_CA_BASIC_CONSTRAINTS) && + current_cert_info.has(CERT_BASIC_CONSTRAINTS) && + current_cert_info[CERT_BASIC_CONSTRAINTS].isMap()) + { + if(!current_cert_info[CERT_BASIC_CONSTRAINTS].has(CERT_BASIC_CONSTRAINTS_CA) || + !current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_CA]) + { + throw LLCertBasicConstraintsValidationException(cert); + } + if (current_cert_info[CERT_BASIC_CONSTRAINTS].has(CERT_BASIC_CONSTRAINTS_PATHLEN) && + ((current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_PATHLEN].asInteger() != 0) && + (depth > current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_PATHLEN].asInteger()))) + { + throw LLCertBasicConstraintsValidationException(cert); + } + } } - - // return the store id -std::string LLBasicCertificateStore::storeId() + +bool _verify_signature(LLPointer parent, + LLPointer child) { - return std::string(""); -} + bool verify_result = FALSE; + LLSD cert1 = parent->getLLSD(); + LLSD cert2 = child->getLLSD(); + X509 *signing_cert = parent->getOpenSSLX509(); + X509 *child_cert = child->getOpenSSLX509(); + if((signing_cert != NULL) && (child_cert != NULL)) + { + EVP_PKEY *pkey = X509_get_pubkey(signing_cert); + - // validate a cert chain -bool LLBasicCertificateStore::validate(const LLCertificateChain& cert_chain) const + if(pkey) + { + int verify_code = X509_verify(child_cert, pkey); + verify_result = ( verify_code > 0); + EVP_PKEY_free(pkey); + } + else + { + LL_WARNS("SECAPI") << "Could not validate the cert chain signature, as the public key of the signing cert could not be retrieved" << LL_ENDL; + } + + } + else + { + LL_WARNS("SECAPI") << "Signature verification failed as there are no certs in the chain" << LL_ENDL; + } + if(child_cert) + { + X509_free(child_cert); + } + if(signing_cert) + { + X509_free(signing_cert); + } + return verify_result; +} + +// validate the certificate chain against a store. +// There are many aspects of cert validatioin policy involved in +// trust validation. The policies in this validation algorithm include +// * Hostname matching for SSL certs +// * Expiration time matching +// * Signature validation +// * Chain trust (is the cert chain trusted against the store) +// * Basic constraints +// * key usage and extended key usage +// TODO: We should add 'authority key identifier' for chaining. +// This algorithm doesn't simply validate the chain by itself +// and verify the last cert is in the certificate store, or points +// to a cert in the store. It validates whether any cert in the chain +// is trusted in the store, even if it's not the last one. +void LLBasicCertificateChain::validate(int validation_policy, + LLPointer ca_store, + const LLSD& validation_params) { - return FALSE; + + if(size() < 1) + { + throw LLCertException(NULL, "No certs in chain"); + } + iterator current_cert = begin(); + LLSD current_cert_info = (*current_cert)->getLLSD(); + LLSD validation_date; + if (validation_params.has(CERT_VALIDATION_DATE)) + { + validation_date = validation_params[CERT_VALIDATION_DATE]; + } + + if (validation_policy & VALIDATION_POLICY_HOSTNAME) + { + if(!validation_params.has(CERT_HOSTNAME)) + { + throw LLCertException((*current_cert), "No hostname passed in for validation"); + } + if(!current_cert_info.has(CERT_SUBJECT_NAME) || !current_cert_info[CERT_SUBJECT_NAME].has(CERT_NAME_CN)) + { + throw LLInvalidCertificate((*current_cert)); + } + + LL_INFOS("SECAPI") << "Validating the hostname " << validation_params[CERT_HOSTNAME].asString() << + "against the cert CN " << current_cert_info[CERT_SUBJECT_NAME][CERT_NAME_CN].asString() << LL_ENDL; + if(!_cert_hostname_wildcard_match(validation_params[CERT_HOSTNAME].asString(), + current_cert_info[CERT_SUBJECT_NAME][CERT_NAME_CN].asString())) + { + throw LLCertValidationHostnameException(validation_params[CERT_HOSTNAME].asString(), + (*current_cert)); + } + } + + + int depth = 0; + LLPointer previous_cert; + // loop through the cert chain, validating the current cert against the next one. + while(current_cert != end()) + { + + int local_validation_policy = validation_policy; + if(current_cert == begin()) + { + // for the child cert, we don't validate CA stuff + local_validation_policy &= ~(VALIDATION_POLICY_CA_KU | + VALIDATION_POLICY_CA_BASIC_CONSTRAINTS); + } + else + { + // for non-child certs, we don't validate SSL Key usage + local_validation_policy &= ~VALIDATION_POLICY_SSL_KU; + if(!_verify_signature((*current_cert), + previous_cert)) + { + throw LLCertValidationInvalidSignatureException(previous_cert); + } + } + _validateCert(local_validation_policy, + (*current_cert), + validation_params, + depth); + + // look for a CA in the CA store that may belong to this chain. + LLSD cert_llsd = (*current_cert)->getLLSD(); + LLSD cert_search_params = LLSD::emptyMap(); + // is the cert itself in the store? + cert_search_params[CERT_SHA1_DIGEST] = cert_llsd[CERT_SHA1_DIGEST]; + LLCertificateStore::iterator found_store_cert = ca_store->find(cert_search_params); + if(found_store_cert != ca_store->end()) + { + return; + } + + // is the parent in the cert store? + + cert_search_params = LLSD::emptyMap(); + cert_search_params[CERT_SUBJECT_NAME_STRING] = cert_llsd[CERT_ISSUER_NAME_STRING]; + found_store_cert = ca_store->find(cert_search_params); + + if(found_store_cert != ca_store->end()) + { + LLSD foo = (*found_store_cert)->getLLSD(); + // validate the store cert against the depth + _validateCert(validation_policy & VALIDATION_POLICY_CA_BASIC_CONSTRAINTS, + (*found_store_cert), + LLSD(), + depth); + + // verify the signature of the CA + if(!_verify_signature((*found_store_cert), + (*current_cert))) + { + throw LLCertValidationInvalidSignatureException(*current_cert); + } + // successfully validated. + return; + } + previous_cert = (*current_cert); + current_cert++; + depth++; + } + if (validation_policy & VALIDATION_POLICY_TRUSTED) + { + LLPointer untrusted_ca_cert = (*this)[size()-1]; + // we reached the end without finding a trusted cert. + throw LLCertValidationTrustException((*this)[size()-1]); + + } } + // LLSecAPIBasicHandler Class // Interface handler class for the various security storage handlers. @@ -369,7 +1038,39 @@ LLSecAPIBasicHandler::LLSecAPIBasicHandler() mLegacyPasswordPath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "password.dat"); mProtectedDataMap = LLSD::emptyMap(); + + mProtectedDataFilename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, + "bin_conf.dat"); _readProtectedData(); + + std::string store_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, + "CA.pem"); + // copy the CA file to a user writable location so we can manipulate it. + // for this provider, by using a user writable file, there is a risk that + // an attacking program can modify the file, but OS dependent providers + // will reduce that risk. + // by using a user file, modifications will be limited to one user if + // we read-only the main file + + + if (!LLFile::isfile(store_file)) + { + + std::string ca_file_path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "CA.pem"); + llifstream ca_file(ca_file_path.c_str(), llifstream::binary | llifstream::in); + llofstream copied_store_file(store_file.c_str(), llofstream::binary | llofstream::out); + + while(!ca_file.fail()) + { + char buffer[BUFFER_READ_SIZE]; + ca_file.read(buffer, sizeof(buffer)); + copied_store_file.write(buffer, ca_file.gcount()); + } + ca_file.close(); + copied_store_file.close(); + } + LL_INFOS("SECAPI") << "Loading certificate store from " << store_file << LL_ENDL; + mStore = new LLBasicCertificateStore(store_file); } LLSecAPIBasicHandler::~LLSecAPIBasicHandler() @@ -537,7 +1238,7 @@ LLPointer LLSecAPIBasicHandler::getCertificate(X509* openssl_cert // instantiate a chain from an X509_STORE_CTX LLPointer LLSecAPIBasicHandler::getCertificateChain(const X509_STORE_CTX* chain) { - LLPointer result = NULL; + LLPointer result = new LLBasicCertificateChain(chain); return result; } @@ -546,8 +1247,7 @@ LLPointer LLSecAPIBasicHandler::getCertificateChain(const X5 // persisted) LLPointer LLSecAPIBasicHandler::getCertificateStore(const std::string& store_id) { - LLPointer result; - return result; + return mStore; } // retrieve protected data @@ -659,7 +1359,7 @@ void LLSecAPIBasicHandler::saveCredential(LLPointer cred, bool sav { credential["authenticator"] = cred->getAuthenticator(); } - LL_INFOS("Credentials") << "Saving Credential " << cred->getGrid() << ":" << cred->userID() << " " << save_authenticator << LL_ENDL; + LL_INFOS("SECAPI") << "Saving Credential " << cred->getGrid() << ":" << cred->userID() << " " << save_authenticator << LL_ENDL; setProtectedData("credential", cred->getGrid(), credential); //*TODO: If we're saving Agni credentials, should we write the // credentials to the legacy password.dat/etc? @@ -742,3 +1442,64 @@ std::string LLSecAPIBasicCredential::asString() const } +bool valueCompareLLSD(const LLSD& lhs, const LLSD& rhs) +{ + if (lhs.type() != rhs.type()) + { + return FALSE; + } + if (lhs.isMap()) + { + // iterate through the map, verifying the right hand side has all of the + // values that the left hand side has. + for (LLSD::map_const_iterator litt = lhs.beginMap(); + litt != lhs.endMap(); + litt++) + { + if (!rhs.has(litt->first)) + { + return FALSE; + } + } + + // Now validate that the left hand side has everything the + // right hand side has, and that the values are equal. + for (LLSD::map_const_iterator ritt = rhs.beginMap(); + ritt != rhs.endMap(); + ritt++) + { + if (!lhs.has(ritt->first)) + { + return FALSE; + } + if (!valueCompareLLSD(lhs[ritt->first], ritt->second)) + { + return FALSE; + } + } + return TRUE; + } + else if (lhs.isArray()) + { + LLSD::array_const_iterator ritt = rhs.beginArray(); + // iterate through the array, comparing + for (LLSD::array_const_iterator litt = lhs.beginArray(); + litt != lhs.endArray(); + litt++) + { + if (!valueCompareLLSD(*ritt, *litt)) + { + return FALSE; + } + ritt++; + } + + return (ritt == rhs.endArray()); + } + else + { + // simple type, compare as string + return (lhs.asString() == rhs.asString()); + } + +} diff --git a/indra/newview/llsechandler_basic.h b/indra/newview/llsechandler_basic.h index 5d81b6e190..e041322260 100644 --- a/indra/newview/llsechandler_basic.h +++ b/indra/newview/llsechandler_basic.h @@ -57,59 +57,147 @@ public: virtual ~LLBasicCertificate(); - virtual std::string getPem(); - virtual std::vector getBinary(); - virtual LLSD getLLSD(); + virtual std::string getPem() const; + virtual std::vector getBinary() const; + virtual LLSD getLLSD() const; - virtual X509* getOpenSSLX509(); + virtual X509* getOpenSSLX509() const; + + // set llsd elements for testing + void setLLSD(const std::string name, const LLSD& value) { mLLSDInfo[name] = value; } protected: + // certificates are stored as X509 objects, as validation and // other functionality is via openssl X509* mCert; + + LLSD& _initLLSD(); + LLSD mLLSDInfo; }; -// class 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 + +// class LLBasicCertificateVector +// Class representing a list of certificates +// This implementation uses a stl vector of certificates. +class LLBasicCertificateVector : virtual public LLCertificateVector { + public: - LLBasicCertificateStore(const std::string& filename); - LLBasicCertificateStore(const X509_STORE* store); - virtual ~LLBasicCertificateStore(); + LLBasicCertificateVector() {} - virtual X509_STORE* getOpenSSLX509Store(); // return an openssl X509_STORE - // for this store + virtual ~LLBasicCertificateVector() {} - // add a copy of a cert to the store - virtual void append(const LLCertificate& cert); + // Implementation of the basic iterator implementation. + // The implementation uses a vector iterator derived from + // the vector in the LLBasicCertificateVector class + class BasicIteratorImpl : public iterator_impl + { + public: + BasicIteratorImpl(std::vector >::iterator _iter) { mIter = _iter;} + virtual ~BasicIteratorImpl() {}; + // seek forward or back. Used by the operator++/operator-- implementations + virtual void seek(bool incr) + { + if(incr) + { + mIter++; + } + else + { + mIter--; + } + } + // create a copy of the iterator implementation class, used by the iterator copy constructor + virtual LLPointer clone() const + { + return new BasicIteratorImpl(mIter); + } + + virtual bool equals(const LLPointer& _iter) const + { + const BasicIteratorImpl *rhs_iter = dynamic_cast(_iter.get()); + return (mIter == rhs_iter->mIter); + } + virtual LLPointer get() + { + return *mIter; + } + protected: + friend class LLBasicCertificateVector; + std::vector >::iterator mIter; + }; - // add a copy of a cert to the store - virtual void insert(const int index, const LLCertificate& cert); + // numeric index of the vector + virtual LLPointer operator[](int _index) { return mCerts[_index];} - // remove a certificate from the store - virtual void remove(int index); + // Iteration + virtual iterator begin() { return iterator(new BasicIteratorImpl(mCerts.begin())); } + + virtual iterator end() { return iterator(new BasicIteratorImpl(mCerts.end())); } + + // find a cert given params + virtual iterator find(const LLSD& params); - // return a certificate at the index - virtual LLPointer operator[](int index); // return the number of certs in the store - virtual int len() const; + virtual int size() const { return mCerts.size(); } + + // insert the cert to the store. if a copy of the cert already exists in the store, it is removed first + virtual void add(LLPointer cert) { insert(end(), cert); } - // load the store from a persisted location - virtual void load(const std::string& store_id); + // insert the cert to the store. if a copy of the cert already exists in the store, it is removed first + virtual void insert(iterator _iter, LLPointer cert); + + // remove a certificate from the store + virtual LLPointer erase(iterator _iter); + +protected: + std::vector >mCerts; +}; + +// class LLCertificateStore +// represents a store of certificates, typically a store of root CA +// certificates. The store can be persisted, and can be used to validate +// a cert chain +// +class LLBasicCertificateStore : virtual public LLBasicCertificateVector, public LLCertificateStore +{ +public: + LLBasicCertificateStore(const std::string& filename); + void load_from_file(const std::string& filename); + + virtual ~LLBasicCertificateStore(); // persist the store virtual void save(); // return the store id - virtual std::string storeId(); + virtual std::string storeId() const; - // validate a cert chain - virtual bool validate(const LLCertificateChain& cert_chain) const; +protected: + std::vector >mCerts; + std::string mFilename; }; +// class LLCertificateChain +// Class representing a chain of certificates in order, with the +// first element being the child cert. +class LLBasicCertificateChain : virtual public LLBasicCertificateVector, public LLCertificateChain +{ + +public: + LLBasicCertificateChain(const X509_STORE_CTX * store); + + virtual ~LLBasicCertificateChain() {} + + // validate a certificate chain against a certificate store, using the + // given validation policy. + virtual void validate(int validation_policy, + LLPointer ca_store, + const LLSD& validation_params); +}; + + + // LLSecAPIBasicCredential class class LLSecAPIBasicCredential : public LLCredential { @@ -182,10 +270,13 @@ protected: std::string mProtectedDataFilename; LLSD mProtectedDataMap; + LLPointer mStore; std::string mLegacyPasswordPath; }; +bool valueCompareLLSD(const LLSD& lhs, const LLSD& rhs); + #endif // LLSECHANDLER_BASIC diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index dd991c8eff..6f7a4e2f6a 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -261,6 +261,9 @@ bool callback_choose_gender(const LLSD& notification, const LLSD& response); void init_start_screen(S32 location_id); void release_start_screen(); void reset_login(); +LLSD transform_cert_args(LLPointer cert); +void general_cert_done(const LLSD& notification, const LLSD& response); +void trust_cert_done(const LLSD& notification, const LLSD& response); void apply_udp_blacklist(const std::string& csv); bool process_login_success_response(); void transition_back_to_login_panel(const std::string& emsg); @@ -1053,10 +1056,11 @@ bool idle_startup() { LL_INFOS("LLStartup") << "Login failed, LLLoginInstance::getResponse(): " << LLLoginInstance::getInstance()->getResponse() << LL_ENDL; + LLSD response = LLLoginInstance::getInstance()->getResponse(); // Still have error conditions that may need some // sort of handling. - std::string reason_response = LLLoginInstance::getInstance()->getResponse("reason"); - std::string message_response = LLLoginInstance::getInstance()->getResponse("message"); + std::string reason_response = response["reason"]; + std::string message_response = response["message"]; if(!message_response.empty()) { @@ -1090,18 +1094,65 @@ bool idle_startup() LLLoginInstance::getInstance()->disconnect(); LLAppViewer::instance()->forceQuit(); } - else + else { - // Don't pop up a notification in the TOS case because - // LLFloaterTOS::onCancel() already scolded the user. - if (reason_response != "tos") + if (reason_response != "tos") { - LLSD args; - args["ERROR_MESSAGE"] = emsg.str(); - LL_INFOS("LLStartup") << "Notification: " << args << LL_ENDL; - LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done); + // Don't pop up a notification in the TOS case because + // LLFloaterTOS::onCancel() already scolded the user. + std::string error_code; + if(response.has("errorcode")) + { + error_code = response["errorcode"].asString(); + } + if ((reason_response == "CURLError") && + (error_code == "SSL_CACERT" || error_code == "SSL_PEER_CERTIFICATE") && + response.has("certificate")) + { + // This was a certificate error, so grab the certificate + // and throw up the appropriate dialog. + LLPointer certificate = gSecAPIHandler->getCertificate(response["certificate"]); + if(certificate) + { + LLSD args = transform_cert_args(certificate); + + if(error_code == "SSL_CACERT") + { + // if we are handling an untrusted CA, throw up the dialog + // with the 'trust this CA' button. + LLNotificationsUtil::add("TrustCertificateError", args, response, + trust_cert_done); + + show_connect_box = true; + } + else + { + // the certificate exception returns a unique string for each type of exception. + // we grab this string via the LLUserAuth object, and use that to grab the localized + // string. + args["REASON"] = LLTrans::getString(message_response); + + LLNotificationsUtil::add("GeneralCertificateError", args, response, + general_cert_done); + + reset_login(); + gSavedSettings.setBOOL("AutoLogin", FALSE); + show_connect_box = true; + + } + + } + } + else + { + // This wasn't a certificate error, so throw up the normal + // notificatioin message. + LLSD args; + args["ERROR_MESSAGE"] = emsg.str(); + LL_INFOS("LLStartup") << "Notification: " << args << LL_ENDL; + LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done); + } } - //setup map of datetime strings to codes and slt & local time offset from utc // *TODO: Does this need to be here? LLStringOps::setupDatetimeInfo (false); @@ -1126,6 +1177,7 @@ bool idle_startup() LLNotificationsUtil::add("ErrorMessage", args, LLSD(), login_alert_done); transition_back_to_login_panel(emsg.str()); show_connect_box = true; + return FALSE; } } return FALSE; @@ -2370,7 +2422,9 @@ const std::string FEMALE_OUTFIT_FOLDER = "Female Shape & Outfit"; const S32 OPT_CLOSED_WINDOW = -1; const S32 OPT_MALE = 0; const S32 OPT_FEMALE = 1; - +const S32 OPT_TRUST_CERT = 0; +const S32 OPT_CANCEL_TRUST = 1; + bool callback_choose_gender(const LLSD& notification, const LLSD& response) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); @@ -2633,6 +2687,91 @@ bool login_alert_done(const LLSD& notification, const LLSD& response) return false; } +// parse the certificate information into args for the +// certificate notifications +LLSD transform_cert_args(LLPointer cert) +{ + LLSD args = LLSD::emptyMap(); + std::string value; + LLSD cert_info = cert->getLLSD(); + // convert all of the elements in the cert into + // args for the xml dialog, so we have flexability to + // display various parts of the cert by only modifying + // the cert alert dialog xml. + for(LLSD::map_iterator iter = cert_info.beginMap(); + iter != cert_info.endMap(); + iter++) + { + // key usage and extended key usage + // are actually arrays, and we want to format them as comma separated + // strings, so special case those. + LLSDSerialize::toXML(cert_info[iter->first], std::cout); + if((iter->first== std::string(CERT_KEY_USAGE)) | + (iter->first == std::string(CERT_EXTENDED_KEY_USAGE))) + { + value = ""; + LLSD usage = cert_info[iter->first]; + for (LLSD::array_iterator usage_iter = usage.beginArray(); + usage_iter != usage.endArray(); + usage_iter++) + { + + if(usage_iter != usage.beginArray()) + { + value += ", "; + } + + value += (*usage_iter).asString(); + } + + } + else + { + value = iter->second.asString(); + } + + std::string name = iter->first; + std::transform(name.begin(), name.end(), name.begin(), + (int(*)(int))toupper); + args[name.c_str()] = value; + } + return args; +} + + +// when we handle a cert error, give focus back to the login panel +void general_cert_done(const LLSD& notification, const LLSD& response) +{ + LLStartUp::setStartupState( STATE_LOGIN_SHOW ); + LLPanelLogin::giveFocus(); +} + +// check to see if the user wants to trust the cert. +// if they do, add it to the cert store and +void trust_cert_done(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotification::getSelectedOption(notification, response); + switch(option) + { + case OPT_TRUST_CERT: + { + LLPointer cert = gSecAPIHandler->getCertificate(notification["payload"]["certificate"]); + LLPointer store = gSecAPIHandler->getCertificateStore(gSavedSettings.getString("CertStore")); + store->add(cert); + store->save(); + LLStartUp::setStartupState( STATE_LOGIN_CLEANUP ); + break; + } + case OPT_CANCEL_TRUST: + reset_login(); + gSavedSettings.setBOOL("AutoLogin", FALSE); + LLStartUp::setStartupState( STATE_LOGIN_SHOW ); + default: + LLPanelLogin::giveFocus(); + break; + } + +} void apply_udp_blacklist(const std::string& csv) { diff --git a/indra/newview/llviewernetwork.cpp b/indra/newview/llviewernetwork.cpp index fcfaf1eef2..82dc459777 100644 --- a/indra/newview/llviewernetwork.cpp +++ b/indra/newview/llviewernetwork.cpp @@ -5,7 +5,7 @@ * * $LicenseInfo:firstyear=2006&license=viewergpl$ * - * Copyright (c) 2006-2007, Linden Research, Inc. + * Copyright (c) 2006-2010, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab @@ -60,12 +60,6 @@ LLGridManager::LLGridManager() } -LLGridManager::LLGridManager(const std::string& grid_file) -{ - // initialize with an explicity grid file for testing. - initialize(grid_file); -} - // // LLGridManager - class for managing the list of known grids, and the current // selection @@ -240,12 +234,8 @@ void LLGridManager::initialize(const std::string& grid_file) // 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")) - { - mGridName = gSavedSettings.getString("CmdLineGridChoice"); - LL_INFOS("GridManager") << "Grid Name: " << mGridName << LL_ENDL; - } + 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 diff --git a/indra/newview/llviewernetwork.h b/indra/newview/llviewernetwork.h index 7b3ce9c499..0642845d54 100644 --- a/indra/newview/llviewernetwork.h +++ b/indra/newview/llviewernetwork.h @@ -5,7 +5,7 @@ * * $LicenseInfo:firstyear=2006&license=viewergpl$ * - * Copyright (c) 2006-2007, Linden Research, Inc. + * Copyright (c) 2006-2010, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab @@ -43,6 +43,7 @@ extern const char* DEFAULT_LOGIN_PAGE; #define GRID_LOGIN_PAGE_VALUE "login_page" #define GRID_IS_SYSTEM_GRID_VALUE "system_grid" #define GRID_IS_FAVORITE_VALUE "favorite" +#define GRID_IS_VISIBLE_VALUE "visible" #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" @@ -78,7 +79,6 @@ public: // 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(); diff --git a/indra/newview/llxmlrpclistener.cpp b/indra/newview/llxmlrpclistener.cpp index 15417614af..8237132ac5 100644 --- a/indra/newview/llxmlrpclistener.cpp +++ b/indra/newview/llxmlrpclistener.cpp @@ -28,6 +28,7 @@ #include "llerror.h" #include "stringize.h" #include "llxmlrpctransaction.h" +#include "llsecapi.h" #if LL_WINDOWS #pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally @@ -356,7 +357,22 @@ public: << data["errorcode"].asString() << " (" << data["error"].asString() << ")" << LL_ENDL; - // In addition to CURLE_OK, LLUserAuth distinguishes different error + + switch (curlcode) + { + case CURLE_SSL_PEER_CERTIFICATE: + case CURLE_SSL_CACERT: + { + LLPointer error_cert(mTransaction->getErrorCert()); + if(error_cert) + { + data["certificate"] = error_cert->getPem(); + } + break; + } + default: + break; + } // values of 'curlcode': // CURLE_COULDNT_RESOLVE_HOST, // CURLE_SSL_PEER_CERTIFICATE, diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index 70859e8ea5..da61840761 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -31,6 +31,9 @@ */ #include "llviewerprecompiledheaders.h" +#include +#include +#include "llsecapi.h" #include "llxmlrpctransaction.h" #include "llxmlrpclistener.h" @@ -176,6 +179,8 @@ public: std::string mResponseText; XMLRPC_REQUEST mResponse; + std::string mCertStore; + LLPointer mErrorCert; Impl(const std::string& uri, XMLRPC_REQUEST request, bool useGzip); Impl(const std::string& uri, @@ -190,7 +195,8 @@ public: private: void init(XMLRPC_REQUEST request, bool useGzip); - + static int _sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param); + static CURLcode _sslCtxFunction(CURL * curl, void *sslctx, void *param); static size_t curlDownloadCallback( char* data, size_t size, size_t nmemb, void* user_data); }; @@ -228,8 +234,74 @@ LLXMLRPCTransaction::Impl::Impl(const std::string& uri, XMLRPC_RequestFree(request, 1); } +// _sslCertVerifyCallback +// callback called when a cert verification is requested. +// calls SECAPI to validate the context +int LLXMLRPCTransaction::Impl::_sslCertVerifyCallback(X509_STORE_CTX *ctx, void *param) +{ + LLXMLRPCTransaction::Impl *transaction = (LLXMLRPCTransaction::Impl *)param; + LLPointer store = gSecAPIHandler->getCertificateStore(transaction->mCertStore); + LLPointer chain = gSecAPIHandler->getCertificateChain(ctx); + LLSD validation_params = LLSD::emptyMap(); + LLURI uri(transaction->mURI); + validation_params[CERT_HOSTNAME] = uri.hostName(); + try + { + chain->validate(VALIDATION_POLICY_SSL, store, validation_params); + } + catch (LLCertValidationTrustException& cert_exception) + { + // this exception is is handled differently than the general cert + // exceptions, as we allow the user to actually add the certificate + // for trust. + // therefore we pass back a different error code + // NOTE: We're currently 'wired' to pass around CURL error codes. This is + // somewhat clumsy, as we may run into errors that do not map directly to curl + // error codes. Should be refactored with login refactoring, perhaps. + transaction->mCurlCode = CURLE_SSL_CACERT; + // set the status directly. set curl status generates error messages and we want + // to use the fixed ones from the exceptions + transaction->setStatus(StatusCURLError, cert_exception.getMessage(), std::string()); + // We should probably have a more generic way of passing information + // back to the error handlers. + transaction->mErrorCert = cert_exception.getCert(); + return 0; + } + catch (LLCertException& cert_exception) + { + transaction->mCurlCode = CURLE_SSL_PEER_CERTIFICATE; + // set the status directly. set curl status generates error messages and we want + // to use the fixed ones from the exceptions + transaction->setStatus(StatusCURLError, cert_exception.getMessage(), std::string()); + transaction->mErrorCert = cert_exception.getCert(); + return 0; + } + catch (...) + { + // any other odd error, we just handle as a connect error. + transaction->mCurlCode = CURLE_SSL_CONNECT_ERROR; + transaction->setCurlStatus(CURLE_SSL_CONNECT_ERROR); + return 0; + } + return 1; +} +// _sslCtxFunction +// Callback function called when an SSL Context is created via CURL +// used to configure the context for custom cert validate(<, <#const & xs#>, <#T * #>, <#long #>)tion +// based on SECAPI +CURLcode LLXMLRPCTransaction::Impl::_sslCtxFunction(CURL * curl, void *sslctx, void *param) +{ + SSL_CTX * ctx = (SSL_CTX *) sslctx; + // disable any default verification for server certs + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); + // set the verification callback. + SSL_CTX_set_cert_verify_callback(ctx, _sslCertVerifyCallback, param); + // the calls are void + return CURLE_OK; + +} void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) { @@ -237,6 +309,7 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) { mCurlRequest = new LLCurlEasyRequest(); } + mErrorCert = NULL; if (gSavedSettings.getBOOL("BrowserProxyEnabled")) { @@ -252,11 +325,13 @@ void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip) // mCurlRequest->setopt(CURLOPT_VERBOSE, 1); // usefull for debugging mCurlRequest->setopt(CURLOPT_NOSIGNAL, 1); mCurlRequest->setWriteCallback(&curlDownloadCallback, (void*)this); - BOOL vefifySSLCert = !gSavedSettings.getBOOL("NoVerifySSLCert"); + BOOL vefifySSLCert = !gSavedSettings.getBOOL("NoVerifySSLCert"); + mCertStore = gSavedSettings.getString("CertStore"); mCurlRequest->setopt(CURLOPT_SSL_VERIFYPEER, vefifySSLCert); mCurlRequest->setopt(CURLOPT_SSL_VERIFYHOST, vefifySSLCert ? 2 : 0); // Be a little impatient about establishing connections. mCurlRequest->setopt(CURLOPT_CONNECTTIMEOUT, 40L); + mCurlRequest->setSSLCtxCallback(_sslCtxFunction, (void *)this); /* Setting the DNS cache timeout to -1 disables it completely. This might help with bug #503 */ @@ -342,11 +417,19 @@ bool LLXMLRPCTransaction::Impl::process() { if (result != CURLE_OK) { - setCurlStatus(result); - llwarns << "LLXMLRPCTransaction CURL error " - << mCurlCode << ": " << mCurlRequest->getErrorString() << llendl; - llwarns << "LLXMLRPCTransaction request URI: " - << mURI << llendl; + if ((result != CURLE_SSL_PEER_CERTIFICATE) && + (result != CURLE_SSL_CACERT)) + { + // if we have a curl error that's not already been handled + // (a non cert error), then generate the error message as + // appropriate + setCurlStatus(result); + + llwarns << "LLXMLRPCTransaction CURL error " + << mCurlCode << ": " << mCurlRequest->getErrorString() << llendl; + llwarns << "LLXMLRPCTransaction request URI: " + << mURI << llendl; + } return true; } @@ -424,7 +507,6 @@ void LLXMLRPCTransaction::Impl::setStatus(EStatus status, case StatusComplete: mStatusMessage = "(done)"; break; - default: // Usually this means that there's a problem with the login server, // not with the client. Direct user to status page. @@ -540,6 +622,11 @@ std::string LLXMLRPCTransaction::statusMessage() return impl.mStatusMessage; } +LLPointer LLXMLRPCTransaction::getErrorCert() +{ + return impl.mErrorCert; +} + std::string LLXMLRPCTransaction::statusURI() { return impl.mStatusURI; diff --git a/indra/newview/llxmlrpctransaction.h b/indra/newview/llxmlrpctransaction.h index c835423d67..8beb2e2623 100644 --- a/indra/newview/llxmlrpctransaction.h +++ b/indra/newview/llxmlrpctransaction.h @@ -38,6 +38,7 @@ typedef struct _xmlrpc_request* XMLRPC_REQUEST; typedef struct _xmlrpc_value* XMLRPC_VALUE; // foward decl of types from xmlrpc.h (this usage is type safe) +class LLCertificate; class LLXMLRPCValue // a c++ wrapper around XMLRPC_VALUE @@ -115,6 +116,8 @@ public: EStatus status(int* curlCode); // return status, and extended CURL code, if code isn't null + + LLPointer getErrorCert(); std::string statusMessage(); // return a message string, suitable for showing the user std::string statusURI(); diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 6d5f0bedb0..c4cbcb1dc8 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -2371,6 +2371,48 @@ Please choose the male or female avatar. You can change your mind later. notext="Female" yestext="Male"/> + +Could not connect to the server. +[REASON] + +SubjectName: [SUBJECT_NAME_STRING] +IssuerName: [ISSUER_NAME_STRING] +Valid From: [VALID_FROM] +Valid To: [VALID_TO] +MD5 Fingerprint: [SHA1_DIGEST] +SHA1 Fingerprint: [MD5_DIGEST] +Key Usage: [KEYUSAGE] +Extended Key Usage: [EXTENDEDKEYUSAGE] +Subject Key Identifier: [SUBJECTKEYIDENTIFIER] + + + + +The certification authority for this server is not known. + +Certificate Information: +SubjectName: [SUBJECT_NAME_STRING] +IssuerName: [ISSUER_NAME_STRING] +Valid From: [VALID_FROM] +Valid To: [VALID_TO] +MD5 Fingerprint: [SHA1_DIGEST] +SHA1 Fingerprint: [MD5_DIGEST] +Key Usage: [KEYUSAGE] +Extended Key Usage: [EXTENDEDKEYUSAGE] +Subject Key Identifier: [SUBJECTKEYIDENTIFIER] + +Would you like to trust this authority? + + (name) {} +LLControlGroup::~LLControlGroup() {} +BOOL LLControlGroup::declareString(const std::string& name, + const std::string& initial_val, + const std::string& comment, + BOOL persist) {return TRUE;} +void LLControlGroup::setString(const std::string& name, const std::string& val){} +std::string LLControlGroup::getString(const std::string& name) +{ + return ""; +} + + +LLControlGroup gSavedSettings("test"); +class LLSecAPIBasicHandler : public LLSecAPIHandler +{ +protected: + LLPointer mCertChain; + LLPointer mCert; + LLPointer mCertStore; + LLSD mLLSD; + +public: + LLSecAPIBasicHandler() {} + + virtual ~LLSecAPIBasicHandler() {} + + // instantiate a certificate from a pem string + virtual LLPointer getCertificate(const std::string& pem_cert) + { + return mCert; + } + + + // instiate a certificate from an openssl X509 structure + virtual LLPointer getCertificate(X509* openssl_cert) + { + return mCert; + } + + + // instantiate a chain from an X509_STORE_CTX + virtual LLPointer getCertificateChain(const X509_STORE_CTX* chain) + { + return mCertChain; + } + + // instantiate a cert store given it's id. if a persisted version + // exists, it'll be loaded. If not, one will be created (but not + // persisted) + virtual LLPointer getCertificateStore(const std::string& store_id) + { + return mCertStore; + } + + // persist data in a protected store + virtual void setProtectedData(const std::string& data_type, + const std::string& data_id, + const LLSD& data) {} + + // retrieve protected data + virtual LLSD getProtectedData(const std::string& data_type, + const std::string& data_id) + { + return mLLSD; + } + + virtual void deleteProtectedData(const std::string& data_type, + const std::string& data_id) + { + } + + virtual LLPointer createCredential(const std::string& grid, + const LLSD& identifier, + const LLSD& authenticator) + { + LLPointer cred = NULL; + return cred; + } + + virtual LLPointer loadCredential(const std::string& grid) + { + LLPointer cred = NULL; + return cred; + } + + virtual void saveCredential(LLPointer cred, bool save_authenticator) {} + + virtual void deleteCredential(LLPointer cred) {} +}; + +// ------------------------------------------------------------------------------------------- +// TUT +// ------------------------------------------------------------------------------------------- +namespace tut +{ + // Test wrapper declaration : wrapping nothing for the moment + struct secapiTest + { + + secapiTest() + { + } + ~secapiTest() + { + } + }; + + // Tut templating thingamagic: test group, object and test instance + typedef test_group secapiTestFactory; + typedef secapiTestFactory::object secapiTestObject; + tut::secapiTestFactory tut_test("llsecapi"); + + // --------------------------------------------------------------------------------------- + // Test functions + // --------------------------------------------------------------------------------------- + // registration + template<> template<> + void secapiTestObject::test<1>() + { + // retrieve an unknown handler + + ensure("'Unknown' handler should be NULL", !(BOOL)getSecHandler("unknown")); + LLPointer test1_handler = new LLSecAPIBasicHandler(); + registerSecHandler("sectest1", test1_handler); + ensure("'Unknown' handler should be NULL", !(BOOL)getSecHandler("unknown")); + LLPointer retrieved_test1_handler = getSecHandler("sectest1"); + ensure("Retrieved sectest1 handler should be the same", + retrieved_test1_handler == test1_handler); + + // insert a second handler + LLPointer test2_handler = new LLSecAPIBasicHandler(); + registerSecHandler("sectest2", test2_handler); + ensure("'Unknown' handler should be NULL", !(BOOL)getSecHandler("unknown")); + retrieved_test1_handler = getSecHandler("sectest1"); + ensure("Retrieved sectest1 handler should be the same", + retrieved_test1_handler == test1_handler); + + LLPointer retrieved_test2_handler = getSecHandler("sectest2"); + ensure("Retrieved sectest1 handler should be the same", + retrieved_test2_handler == test2_handler); + + } +} \ No newline at end of file diff --git a/indra/newview/tests/llsechandler_basic_test.cpp b/indra/newview/tests/llsechandler_basic_test.cpp index a5554d55a5..f52ebc198d 100644 --- a/indra/newview/tests/llsechandler_basic_test.cpp +++ b/indra/newview/tests/llsechandler_basic_test.cpp @@ -44,9 +44,60 @@ #include #include #include +#include +#include #include "llxorcipher.h" -LLControlGroup gSavedSettings; +#define ensure_throws(str, exc_type, cert, func, ...) \ +try \ +{ \ +func(__VA_ARGS__); \ +fail("throws, " str); \ +} \ +catch(exc_type& except) \ +{ \ +ensure("Exception cert is incorrect for " str, except.getCert() == cert); \ +} + +extern bool _cert_hostname_wildcard_match(const std::string& hostname, const std::string& wildcard_string); + +//---------------------------------------------------------------------------- +// Mock objects for the dependencies of the code we're testing + +std::string gFirstName; +std::string gLastName; +LLControlGroup::LLControlGroup(const std::string& name) +: LLInstanceTracker(name) {} +LLControlGroup::~LLControlGroup() {} +BOOL LLControlGroup::declareString(const std::string& name, + const std::string& initial_val, + const std::string& comment, + BOOL persist) {return TRUE;} +void LLControlGroup::setString(const std::string& name, const std::string& val){} +std::string LLControlGroup::getString(const std::string& name) +{ + + if (name == "FirstName") + return gFirstName; + else if (name == "LastName") + return gLastName; + return ""; +} + +LLSD LLCredential::getLoginParams() +{ + LLSD result = LLSD::emptyMap(); + + // legacy credential + result["passwd"] = "$1$testpasssd"; + result["first"] = "myfirst"; + result["last"] ="mylast"; + return result; +} + + + +LLControlGroup gSavedSettings("test"); unsigned char gMACAddress[MAC_ADDRESS_BYTES] = {77,21,46,31,89,2}; // ------------------------------------------------------------------------------------------- @@ -57,44 +108,91 @@ namespace tut // Test wrapper declaration : wrapping nothing for the moment struct sechandler_basic_test { - std::string mPemTestCert; + std::string mPemTestCert, mPemRootCert, mPemIntermediateCert, mPemChildCert; std::string mDerFormat; - X509 *mX509TestCert; - LLBasicCertificate* mTestCert; + X509 *mX509TestCert, *mX509RootCert, *mX509IntermediateCert, *mX509ChildCert; sechandler_basic_test() { + OpenSSL_add_all_algorithms(); + OpenSSL_add_all_ciphers(); + OpenSSL_add_all_digests(); + ERR_load_crypto_strings(); + gFirstName = ""; + gLastName = ""; LLFile::remove("test_password.dat"); LLFile::remove("sechandler_settings.tmp"); mPemTestCert = "-----BEGIN CERTIFICATE-----\n" -"MIIEuDCCA6CgAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBtDELMAkGA1UEBhMCQlIx\n" -"EzARBgNVBAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25h\n" -"bCBkZSBUZWNub2xvZ2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxETAPBgNVBAcTCEJy\n" -"YXNpbGlhMQswCQYDVQQIEwJERjExMC8GA1UEAxMoQXV0b3JpZGFkZSBDZXJ0aWZp\n" -"Y2Fkb3JhIFJhaXogQnJhc2lsZWlyYTAeFw0wMTExMzAxMjU4MDBaFw0xMTExMzAy\n" -"MzU5MDBaMIG0MQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDE9MDsG\n" -"A1UECxM0SW5zdGl0dXRvIE5hY2lvbmFsIGRlIFRlY25vbG9naWEgZGEgSW5mb3Jt\n" -"YWNhbyAtIElUSTERMA8GA1UEBxMIQnJhc2lsaWExCzAJBgNVBAgTAkRGMTEwLwYD\n" -"VQQDEyhBdXRvcmlkYWRlIENlcnRpZmljYWRvcmEgUmFpeiBCcmFzaWxlaXJhMIIB\n" -"IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwPMudwX/hvm+Uh2b/lQAcHVA\n" -"isamaLkWdkwP9/S/tOKIgRrL6Oy+ZIGlOUdd6uYtk9Ma/3pUpgcfNAj0vYm5gsyj\n" -"Qo9emsc+x6m4VWwk9iqMZSCK5EQkAq/Ut4n7KuLE1+gdftwdIgxfUsPt4CyNrY50\n" -"QV57KM2UT8x5rrmzEjr7TICGpSUAl2gVqe6xaii+bmYR1QrmWaBSAG59LrkrjrYt\n" -"bRhFboUDe1DK+6T8s5L6k8c8okpbHpa9veMztDVC9sPJ60MWXh6anVKo1UcLcbUR\n" -"yEeNvZneVRKAAU6ouwdjDvwlsaKydFKwed0ToQ47bmUKgcm+wV3eTRk36UOnTwID\n" -"AQABo4HSMIHPME4GA1UdIARHMEUwQwYFYEwBAQAwOjA4BggrBgEFBQcCARYsaHR0\n" -"cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0RQQ2FjcmFpei5wZGYwPQYDVR0f\n" -"BDYwNDAyoDCgLoYsaHR0cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0xDUmFj\n" -"cmFpei5jcmwwHQYDVR0OBBYEFIr68VeEERM1kEL6V0lUaQ2kxPA3MA8GA1UdEwEB\n" -"/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAZA5c1\n" -"U/hgIh6OcgLAfiJgFWpvmDZWqlV30/bHFpj8iBobJSm5uDpt7TirYh1Uxe3fQaGl\n" -"YjJe+9zd+izPRbBqXPVQA34EXcwk4qpWuf1hHriWfdrx8AcqSqr6CuQFwSr75Fos\n" -"SzlwDADa70mT7wZjAmQhnZx2xJ6wfWlT9VQfS//JYeIc7Fue2JNLd00UOSMMaiK/\n" -"t79enKNHEA2fupH3vEigf5Eh4bVAN5VohrTm6MY53x7XQZZr1ME7a55lFEnSeT0u\n" -"mlOAjR2mAbvSM5X5oSZNrmetdzyTj2flCM8CC7MLab0kkdngRIlUBGHF1/S5nmPb\n" -"K+9A46sd33oqK8n8\n" -"-----END CERTIFICATE-----\n" -""; + "MIIEuDCCA6CgAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBtDELMAkGA1UEBhMCQlIx\n" + "EzARBgNVBAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25h\n" + "bCBkZSBUZWNub2xvZ2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxETAPBgNVBAcTCEJy\n" + "YXNpbGlhMQswCQYDVQQIEwJERjExMC8GA1UEAxMoQXV0b3JpZGFkZSBDZXJ0aWZp\n" + "Y2Fkb3JhIFJhaXogQnJhc2lsZWlyYTAeFw0wMTExMzAxMjU4MDBaFw0xMTExMzAy\n" + "MzU5MDBaMIG0MQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDE9MDsG\n" + "A1UECxM0SW5zdGl0dXRvIE5hY2lvbmFsIGRlIFRlY25vbG9naWEgZGEgSW5mb3Jt\n" + "YWNhbyAtIElUSTERMA8GA1UEBxMIQnJhc2lsaWExCzAJBgNVBAgTAkRGMTEwLwYD\n" + "VQQDEyhBdXRvcmlkYWRlIENlcnRpZmljYWRvcmEgUmFpeiBCcmFzaWxlaXJhMIIB\n" + "IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwPMudwX/hvm+Uh2b/lQAcHVA\n" + "isamaLkWdkwP9/S/tOKIgRrL6Oy+ZIGlOUdd6uYtk9Ma/3pUpgcfNAj0vYm5gsyj\n" + "Qo9emsc+x6m4VWwk9iqMZSCK5EQkAq/Ut4n7KuLE1+gdftwdIgxfUsPt4CyNrY50\n" + "QV57KM2UT8x5rrmzEjr7TICGpSUAl2gVqe6xaii+bmYR1QrmWaBSAG59LrkrjrYt\n" + "bRhFboUDe1DK+6T8s5L6k8c8okpbHpa9veMztDVC9sPJ60MWXh6anVKo1UcLcbUR\n" + "yEeNvZneVRKAAU6ouwdjDvwlsaKydFKwed0ToQ47bmUKgcm+wV3eTRk36UOnTwID\n" + "AQABo4HSMIHPME4GA1UdIARHMEUwQwYFYEwBAQAwOjA4BggrBgEFBQcCARYsaHR0\n" + "cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0RQQ2FjcmFpei5wZGYwPQYDVR0f\n" + "BDYwNDAyoDCgLoYsaHR0cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0xDUmFj\n" + "cmFpei5jcmwwHQYDVR0OBBYEFIr68VeEERM1kEL6V0lUaQ2kxPA3MA8GA1UdEwEB\n" + "/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAZA5c1\n" + "U/hgIh6OcgLAfiJgFWpvmDZWqlV30/bHFpj8iBobJSm5uDpt7TirYh1Uxe3fQaGl\n" + "YjJe+9zd+izPRbBqXPVQA34EXcwk4qpWuf1hHriWfdrx8AcqSqr6CuQFwSr75Fos\n" + "SzlwDADa70mT7wZjAmQhnZx2xJ6wfWlT9VQfS//JYeIc7Fue2JNLd00UOSMMaiK/\n" + "t79enKNHEA2fupH3vEigf5Eh4bVAN5VohrTm6MY53x7XQZZr1ME7a55lFEnSeT0u\n" + "mlOAjR2mAbvSM5X5oSZNrmetdzyTj2flCM8CC7MLab0kkdngRIlUBGHF1/S5nmPb\n" + "K+9A46sd33oqK8n8\n" + "-----END CERTIFICATE-----\n"; + + mPemRootCert = "-----BEGIN CERTIFICATE-----\n" + "MIIB0TCCATqgAwIBAgIJANaTqrzEvHaRMA0GCSqGSIb3DQEBBAUAMBsxGTAXBgNV\n" + "BAMTEFJveGllcyB0ZXN0IHJvb3QwHhcNMDkwNDE1MjEwNzQ3WhcNMTAwNDE1MjEw\n" + "NzQ3WjAbMRkwFwYDVQQDExBSb3hpZXMgdGVzdCByb290MIGfMA0GCSqGSIb3DQEB\n" + "AQUAA4GNADCBiQKBgQCpo5nDW6RNz9IHUVZd7Tw2XAQiBniDF4xH0N1w7sUYTiFq\n" + "21mABsnOPJD3ra+MtOsXPHcaljm661JjTD8L40v5sfEbqDUPcOw76ClrPqnuAeyT\n" + "38qk8DHku/mT8YdprevGZdVcUXQg3vosVzOL93HOOHK+u61mEEoM9W5xoNVEdQID\n" + "AQABox0wGzAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQQF\n" + "AAOBgQAzn0aW/+zWPmcTbvxonyiYYUr9b4SOB/quhAkT8KT4ir1dcZAXRR59+kEn\n" + "HSTu1FAodV0gvESqyobftF5hZ1XMxdJqGu//xP+YCwlv244G/0pp7KLI8ihNO2+N\n" + "lPBUJgbo++ZkhiE1jotZi9Ay0Oedh3s/AfbMZPyfpJ23ll6+BA==\n" + "-----END CERTIFICATE-----\n"; + + + + mPemIntermediateCert = "-----BEGIN CERTIFICATE-----\n" + "MIIBzzCCATigAwIBAgIBATANBgkqhkiG9w0BAQQFADAbMRkwFwYDVQQDExBSb3hp\n" + "ZXMgdGVzdCByb290MB4XDTA5MDQxNTIxMzE1NloXDTEwMDQxNTIxMzE1NlowITEf\n" + "MB0GA1UEAxMWUm94aWVzIGludGVybWVkaWF0ZSBDQTCBnzANBgkqhkiG9w0BAQEF\n" + "AAOBjQAwgYkCgYEA15MM0W1R37rx/24Q2Qkb5bSiQZxTUcQAhJ2pA8mwUucXuCVt\n" + "6ayI2TuN32nkjmsCgUkiT/bdXWp0OJo7/MXRIFeUNMCRxrpeFnxuigYEqbIXAdN6\n" + "qu/vdG2X4PRv/v9Ijrju4cBEiKIldIgOurWEIfXEsVSFP2XmFQHesF04qDcCAwEA\n" + "AaMdMBswDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQEEBQAD\n" + "gYEAYljikYgak3W1jSo0vYthNHUy3lBVAKzDhpM96lY5OuXFslpCRX42zNL8X3kN\n" + "U/4IaJUVtZqx8WsUXl1eXHzBCaXCftapV4Ir6cENLIsXCdXs8paFYzN5nPJA5GYU\n" + "zWgkSEl1MEhNIc+bJW34vwi29EjrAShAhsIZ84Mt/lvD3Pc=\n" + "-----END CERTIFICATE-----\n"; + + mPemChildCert = "-----BEGIN CERTIFICATE-----\n" + "MIIB5DCCAU0CBEnm9eUwDQYJKoZIhvcNAQEEBQAwITEfMB0GA1UEAxMWUm94aWVz\n" + "IGludGVybWVkaWF0ZSBDQTAeFw0wOTA0MTYwMDAzNDlaFw0xMDA0MTYwMDAzNDla\n" + "MCAxHjAcBgNVBAMTFWVuaWFjNjMubGluZGVubGFiLmNvbTCBnzANBgkqhkiG9w0B\n" + "AQEFAAOBjQAwgYkCgYEAp9I5rofEzbjNht+9QejfnsIlEPqSxskoWKCG255TesWR\n" + "RTmw9wafHQQkJk/VIsaU4RMBYHkknGbHX2dGvMHmKZoWUPSQ/8FZz09o0Qx3TNUZ\n" + "l7KlGOD2d1c7ZxXDPqlLC6QW8DrE1/8zfwJ5cbYBXc8e7OKdSZeRrnwHyw4Q8r8C\n" + "AwEAAaMvMC0wEwYDVR0lBAwwCgYIKwYBBQUHAwEwCQYDVR0TBAIwADALBgNVHQ8E\n" + "BAMCBaAwDQYJKoZIhvcNAQEEBQADgYEAIG0M5tqYlXyMiGKPZfXy/R3M3ZZOapDk\n" + "W0dsXJYXAc35ftwtn0VYu9CNnZCcli17/d+AKhkK8a/oGPazqudjFF6WLJLTXaY9\n" + "NmhkJcOPADXkbyQPUPXzLe4YRrkEQeGhzMb4rKDQ1TKAcXfs0Y068pTpsixNSxja\n" + "NhAUUcve5Is=\n" + "-----END CERTIFICATE-----\n"; + mDerFormat = "MIIEuDCCA6CgAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBtDELMAkGA1UEBhMCQlIxEzARBgNVBAoT" "CklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25hbCBkZSBUZWNub2xvZ2lhIGRh" "IEluZm9ybWFjYW8gLSBJVEkxETAPBgNVBAcTCEJyYXNpbGlhMQswCQYDVQQIEwJERjExMC8GA1UE" @@ -118,24 +216,33 @@ namespace tut "1ME7a55lFEnSeT0umlOAjR2mAbvSM5X5oSZNrmetdzyTj2flCM8CC7MLab0kkdngRIlUBGHF1/S5" "nmPbK+9A46sd33oqK8n8"; - mTestCert = new LLBasicCertificate(mPemTestCert); - - gSavedSettings.cleanup(); - gSavedSettings.declareString("FirstName", "", "", FALSE); - gSavedSettings.declareString("LastName", "", "", FALSE); mX509TestCert = NULL; - BIO * validation_bio = BIO_new_mem_buf((void*)mPemTestCert.c_str(), mPemTestCert.length()); + mX509RootCert = NULL; + mX509IntermediateCert = NULL; + mX509ChildCert = NULL; + BIO * validation_bio = BIO_new_mem_buf((void*)mPemTestCert.c_str(), mPemTestCert.length()); PEM_read_bio_X509(validation_bio, &mX509TestCert, 0, NULL); BIO_free(validation_bio); - + validation_bio = BIO_new_mem_buf((void*)mPemRootCert.c_str(), mPemRootCert.length()); + PEM_read_bio_X509(validation_bio, &mX509RootCert, 0, NULL); + BIO_free(validation_bio); + validation_bio = BIO_new_mem_buf((void*)mPemIntermediateCert.c_str(), mPemIntermediateCert.length()); + PEM_read_bio_X509(validation_bio, &mX509IntermediateCert, 0, NULL); + BIO_free(validation_bio); + validation_bio = BIO_new_mem_buf((void*)mPemChildCert.c_str(), mPemChildCert.length()); + PEM_read_bio_X509(validation_bio, &mX509ChildCert, 0, NULL); + BIO_free(validation_bio); } ~sechandler_basic_test() { LLFile::remove("test_password.dat"); LLFile::remove("sechandler_settings.tmp"); - delete mTestCert; + LLFile::remove("mycertstore.pem"); X509_free(mX509TestCert); + X509_free(mX509RootCert); + X509_free(mX509IntermediateCert); + X509_free(mX509ChildCert); } }; @@ -152,18 +259,18 @@ namespace tut void sechandler_basic_test_object::test<1>() { - char buffer[4096]; - + LLPointer test_cert = new LLBasicCertificate(mPemTestCert); + ensure_equals("Resultant pem is correct", - mPemTestCert, mTestCert->getPem()); - std::vector binary_cert = mTestCert->getBinary(); + mPemTestCert, test_cert->getPem()); + std::vector binary_cert = test_cert->getBinary(); apr_base64_encode(buffer, (const char *)&binary_cert[0], binary_cert.size()); ensure_equals("Der Format is correct", memcmp(buffer, mDerFormat.c_str(), mDerFormat.length()), 0); - LLSD llsd_cert = mTestCert->getLLSD(); + LLSD llsd_cert = test_cert->getLLSD(); std::ostringstream llsd_value; llsd_value << LLSDOStreamer(llsd_cert) << std::endl; std::string llsd_cert_str = llsd_value.str(); @@ -194,10 +301,15 @@ namespace tut ensure_equals("serial number", (std::string)llsd_cert["serial_number"], "04"); // sha1 digest is giving a weird value, and I've no idea why...feh //ensure_equals("sha1 digest", (std::string)llsd_cert["sha1_digest"], "8e:fd:ca:bc:93:e6:1e:92:5d:4d:1d:ed:18:1a:43:20:a4:67:a1:39"); - ensure_equals("valid from", (std::string)llsd_cert["valid_from"], "2001-11-30T20:58:00Z"); - ensure_equals("valid to", (std::string)llsd_cert["valid_to"], "2011-12-01T07:59:00Z"); + ensure_equals("valid from", (std::string)llsd_cert["valid_from"], "2001-11-30T12:58:00Z"); + ensure_equals("valid to", (std::string)llsd_cert["valid_to"], "2011-11-30T23:59:00Z"); + LLSD expectedKeyUsage = LLSD::emptyArray(); + expectedKeyUsage.append(LLSD((std::string)"certSigning")); + expectedKeyUsage.append(LLSD((std::string)"crlSigning")); + ensure("key usage", valueCompareLLSD(llsd_cert["keyUsage"], expectedKeyUsage)); + ensure("basic constraints", (bool)llsd_cert["basicConstraints"]["CA"]); - ensure("x509 is equal", !X509_cmp(mX509TestCert, mTestCert->getOpenSSLX509())); + ensure("x509 is equal", !X509_cmp(mX509TestCert, test_cert->getOpenSSLX509())); } @@ -319,7 +431,6 @@ namespace tut void sechandler_basic_test_object::test<3>() { LLPointer handler = new LLSecAPIBasicHandler("sechandler_settings.tmp", "test_password.dat"); - LLSD my_id = LLSD::emptyMap(); LLSD my_authenticator = LLSD::emptyMap(); @@ -349,7 +460,7 @@ namespace tut // test loading of a credential, that hasn't been saved, without // any legacy saved credential data - LLPointer my_new_cred = handler->loadCredential("my_grid"); + LLPointer my_new_cred = handler->loadCredential("my_grid2"); ensure("unknown credential load test", my_new_cred->getIdentifier().isMap()); ensure("unknown credential load test", !my_new_cred->getIdentifier().has("type")); ensure("unknown credential load test", my_new_cred->getAuthenticator().isMap()); @@ -379,10 +490,8 @@ namespace tut // test loading of an unknown credential with legacy saved username, but without // saved password - - gSavedSettings.setString("FirstName", "myfirstname"); - gSavedSettings.setString("LastName", "mylastname"); - + gFirstName = "myfirstname"; + gLastName = "mylastname"; my_new_cred = handler->loadCredential("my_legacy_grid"); ensure_equals("legacy credential with no password: type", (const std::string)my_new_cred->getIdentifier()["type"], "agent"); @@ -438,19 +547,413 @@ namespace tut ensure("no authenticator values were saved", my_new_cred->getAuthenticator().isUndefined()); } + // test cert vector + template<> template<> + void sechandler_basic_test_object::test<4>() + { + + // validate create from empty vector + LLPointer test_vector = new LLBasicCertificateVector(); + ensure_equals("when loading with nothing, we should result in no certs in vector", test_vector->size(), 0); + + test_vector->add(new LLBasicCertificate(mPemTestCert)); + ensure_equals("one element in vector", test_vector->size(), 1); + test_vector->add(new LLBasicCertificate(mPemChildCert)); + ensure_equals("two elements in vector after add", test_vector->size(), 2); + + test_vector->add(new LLBasicCertificate(mPemChildCert)); + ensure_equals("two elements in vector after re-add", test_vector->size(), 2); + // validate order + X509* test_cert = (*test_vector)[0]->getOpenSSLX509(); + ensure("first cert added remains first cert", !X509_cmp(test_cert, mX509TestCert)); + X509_free(test_cert); + + test_cert = (*test_vector)[1]->getOpenSSLX509(); + ensure("adding a duplicate cert", !X509_cmp(test_cert, mX509ChildCert)); + X509_free(test_cert); + + // + // validate iterator + // + LLBasicCertificateVector::iterator current_cert = test_vector->begin(); + LLBasicCertificateVector::iterator copy_current_cert = current_cert; + // operator++(int) + ensure("validate iterator++ element in vector is expected cert", *current_cert++ == (*test_vector)[0]); + ensure("validate 2nd iterator++ element in vector is expected cert", *current_cert++ == (*test_vector)[1]); + ensure("validate end iterator++", current_cert == test_vector->end()); + + // copy + ensure("validate copy iterator element in vector is expected cert", *copy_current_cert == (*test_vector)[0]); + + // operator--(int) + current_cert--; + ensure("validate iterator-- element in vector is expected cert", *current_cert-- == (*test_vector)[1]); + ensure("validate iterator-- element in vector is expected cert", *current_cert == (*test_vector)[0]); + + ensure("begin iterator is equal", current_cert == test_vector->begin()); + + // operator++ + ensure("validate ++iterator element in vector is expected cert", *++current_cert == (*test_vector)[1]); + ensure("end of cert vector after ++iterator", ++current_cert == test_vector->end()); + // operator-- + ensure("validate --iterator element in vector is expected cert", *--current_cert == (*test_vector)[1]); + ensure("validate 2nd --iterator element in vector is expected cert", *--current_cert == (*test_vector)[0]); + + // validate remove + // validate create from empty vector + test_vector = new LLBasicCertificateVector(); + test_vector->add(new LLBasicCertificate(mPemTestCert)); + test_vector->add(new LLBasicCertificate(mPemChildCert)); + test_vector->erase(test_vector->begin()); + ensure_equals("one element in store after remove", test_vector->size(), 1); + test_cert = (*test_vector)[0]->getOpenSSLX509(); + ensure("validate cert was removed", !X509_cmp(test_cert, mX509ChildCert)); + X509_free(test_cert); + + // validate insert + test_vector->insert(test_vector->begin(), new LLBasicCertificate(mPemChildCert)); + test_cert = (*test_vector)[0]->getOpenSSLX509(); + + ensure("validate cert was inserted", !X509_cmp(test_cert, mX509ChildCert)); + X509_free(test_cert); + //validate find + LLSD find_info = LLSD::emptyMap(); + test_vector->insert(test_vector->begin(), new LLBasicCertificate(mPemRootCert)); + find_info["issuer_name"] = LLSD::emptyMap(); + find_info["issuer_name"]["commonName"] = "Roxies intermediate CA"; + find_info["md5_digest"] = "97:24:c7:4c:d4:ba:2d:0e:9c:a1:18:8e:3a:c6:1f:c3"; + current_cert = test_vector->find(find_info); + ensure("found", current_cert != test_vector->end()); + ensure("found cert", (*current_cert).get() == (*test_vector)[1].get()); + find_info["sha1_digest"] = "bad value"; + current_cert =test_vector->find(find_info); + ensure("didn't find cert", current_cert == test_vector->end()); + } + // test cert store template<> template<> - void sechandler_basic_test_object::test<4>() + void sechandler_basic_test_object::test<5>() { + // validate load with nothing + LLFile::remove("mycertstore.pem"); + LLPointer test_store = new LLBasicCertificateStore("mycertstore.pem"); + ensure_equals("when loading with nothing, we should result in no certs in store", test_store->size(), 0); + + // validate load with empty file + test_store->save(); + test_store = NULL; + test_store = new LLBasicCertificateStore("mycertstore.pem"); + ensure_equals("when loading with nothing, we should result in no certs in store", test_store->size(), 0); + test_store=NULL; + // instantiate a cert store from a file - llofstream certstorefile("mycertstore.pem", std::ios::out | std::ios::binary); - - certstorefile << mPemTestCert; + llofstream certstorefile("mycertstore.pem", std::ios::out); + certstorefile << mPemChildCert << std::endl << mPemTestCert << std::endl; certstorefile.close(); - // LLBasicCertificateStore test_store("mycertstore.pem"); - // X509* test_cert = test_store[0]->getOpenSSLX509(); + // validate loaded certs + test_store = new LLBasicCertificateStore("mycertstore.pem"); + ensure_equals("two elements in store", test_store->size(), 2); + + // operator[] + X509* test_cert = (*test_store)[0]->getOpenSSLX509(); + + ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); + X509_free(test_cert); + test_cert = (*test_store)[1]->getOpenSSLX509(); + ensure("validate second element in store is expected cert", !X509_cmp(test_cert, mX509TestCert)); + X509_free(test_cert); + + + // validate save + LLFile::remove("mycertstore.pem"); + test_store->save(); + test_store = NULL; + test_store = new LLBasicCertificateStore("mycertstore.pem"); + ensure_equals("two elements in store after save", test_store->size(), 2); + LLCertificateStore::iterator current_cert = test_store->begin(); + test_cert = (*current_cert)->getOpenSSLX509(); + ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); + current_cert++; + X509_free(test_cert); + test_cert = (*current_cert)->getOpenSSLX509(); + ensure("validate second element in store is expected cert", !X509_cmp(test_cert, mX509TestCert)); + X509_free(test_cert); + current_cert++; + ensure("end of cert store", current_cert == test_store->end()); + + } + + // cert name wildcard matching + template<> template<> + void sechandler_basic_test_object::test<6>() + { + ensure("simple name match", + _cert_hostname_wildcard_match("foo", "foo")); + + ensure("simple name match, with end period", + _cert_hostname_wildcard_match("foo.", "foo.")); + + ensure("simple name match, with begin period", + _cert_hostname_wildcard_match(".foo", ".foo")); + + ensure("simple name match, with subdomain", + _cert_hostname_wildcard_match("foo.bar", "foo.bar")); + + ensure("stutter name match", + _cert_hostname_wildcard_match("foobbbbfoo", "foo*bbbfoo")); + + ensure("simple name match, with beginning wildcard", + _cert_hostname_wildcard_match("foobar", "*bar")); + + ensure("simple name match, with ending wildcard", + _cert_hostname_wildcard_match("foobar", "foo*")); + + ensure("simple name match, with beginning null wildcard", + _cert_hostname_wildcard_match("foobar", "*foobar")); + + ensure("simple name match, with ending null wildcard", + _cert_hostname_wildcard_match("foobar", "foobar*")); + + ensure("simple name match, with embedded wildcard", + _cert_hostname_wildcard_match("foobar", "f*r")); + + ensure("simple name match, with embedded null wildcard", + _cert_hostname_wildcard_match("foobar", "foo*bar")); + + ensure("simple name match, with dual embedded wildcard", + _cert_hostname_wildcard_match("foobar", "f*o*ar")); + + ensure("simple name mismatch", + !_cert_hostname_wildcard_match("bar", "foo")); + + ensure("simple name mismatch, with end period", + !_cert_hostname_wildcard_match("foobar.", "foo.")); + + ensure("simple name mismatch, with begin period", + !_cert_hostname_wildcard_match(".foobar", ".foo")); + + ensure("simple name mismatch, with subdomain", + !_cert_hostname_wildcard_match("foobar.bar", "foo.bar")); + + ensure("simple name mismatch, with beginning wildcard", + !_cert_hostname_wildcard_match("foobara", "*bar")); + + ensure("simple name mismatch, with ending wildcard", + !_cert_hostname_wildcard_match("oobar", "foo*")); + + ensure("simple name mismatch, with embedded wildcard", + !_cert_hostname_wildcard_match("oobar", "f*r")); + + ensure("simple name mismatch, with dual embedded wildcard", + !_cert_hostname_wildcard_match("foobar", "f*d*ar")); + + ensure("simple wildcard", + _cert_hostname_wildcard_match("foobar", "*")); + + ensure("long domain", + _cert_hostname_wildcard_match("foo.bar.com", "foo.bar.com")); + + ensure("long domain with multiple wildcards", + _cert_hostname_wildcard_match("foo.bar.com", "*.b*r.com")); - // ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509TestCert)); + ensure("end periods", + _cert_hostname_wildcard_match("foo.bar.com.", "*.b*r.com.")); + + ensure("mismatch end period", + !_cert_hostname_wildcard_match("foo.bar.com.", "*.b*r.com")); + + ensure("mismatch end period2", + !_cert_hostname_wildcard_match("foo.bar.com", "*.b*r.com.")); + } + + // test cert chain + template<> template<> + void sechandler_basic_test_object::test<7>() + { + // validate create from empty chain + LLPointer test_chain = new LLBasicCertificateChain(NULL); + ensure_equals("when loading with nothing, we should result in no certs in chain", test_chain->size(), 0); + + // Single cert in the chain. + X509_STORE_CTX *test_store = X509_STORE_CTX_new(); + test_store->cert = mX509ChildCert; + test_store->untrusted = NULL; + test_chain = new LLBasicCertificateChain(test_store); + X509_STORE_CTX_free(test_store); + ensure_equals("two elements in store", test_chain->size(), 1); + X509* test_cert = (*test_chain)[0]->getOpenSSLX509(); + ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); + X509_free(test_cert); + + // cert + CA + + test_store = X509_STORE_CTX_new(); + test_store->cert = mX509ChildCert; + test_store->untrusted = sk_X509_new_null(); + sk_X509_push(test_store->untrusted, mX509IntermediateCert); + test_chain = new LLBasicCertificateChain(test_store); + X509_STORE_CTX_free(test_store); + ensure_equals("two elements in store", test_chain->size(), 2); + test_cert = (*test_chain)[0]->getOpenSSLX509(); + ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); + X509_free(test_cert); + test_cert = (*test_chain)[1]->getOpenSSLX509(); + ensure("validate second element in store is expected cert", !X509_cmp(test_cert, mX509IntermediateCert)); + X509_free(test_cert); + + // cert + nonrelated + + test_store = X509_STORE_CTX_new(); + test_store->cert = mX509ChildCert; + test_store->untrusted = sk_X509_new_null(); + sk_X509_push(test_store->untrusted, mX509TestCert); + test_chain = new LLBasicCertificateChain(test_store); + X509_STORE_CTX_free(test_store); + ensure_equals("two elements in store", test_chain->size(), 1); + test_cert = (*test_chain)[0]->getOpenSSLX509(); + ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); + X509_free(test_cert); + + // cert + CA + nonrelated + test_store = X509_STORE_CTX_new(); + test_store->cert = mX509ChildCert; + test_store->untrusted = sk_X509_new_null(); + sk_X509_push(test_store->untrusted, mX509IntermediateCert); + sk_X509_push(test_store->untrusted, mX509TestCert); + test_chain = new LLBasicCertificateChain(test_store); + X509_STORE_CTX_free(test_store); + ensure_equals("two elements in store", test_chain->size(), 2); + test_cert = (*test_chain)[0]->getOpenSSLX509(); + ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); + X509_free(test_cert); + test_cert = (*test_chain)[1]->getOpenSSLX509(); + ensure("validate second element in store is expected cert", !X509_cmp(test_cert, mX509IntermediateCert)); + X509_free(test_cert); + + // cert + intermediate + CA + test_store = X509_STORE_CTX_new(); + test_store->cert = mX509ChildCert; + test_store->untrusted = sk_X509_new_null(); + sk_X509_push(test_store->untrusted, mX509IntermediateCert); + sk_X509_push(test_store->untrusted, mX509RootCert); + test_chain = new LLBasicCertificateChain(test_store); + X509_STORE_CTX_free(test_store); + ensure_equals("three elements in store", test_chain->size(), 3); + test_cert = (*test_chain)[0]->getOpenSSLX509(); + ensure("validate first element in store is expected cert", !X509_cmp(test_cert, mX509ChildCert)); + X509_free(test_cert); + test_cert = (*test_chain)[1]->getOpenSSLX509(); + ensure("validate second element in store is expected cert", !X509_cmp(test_cert, mX509IntermediateCert)); + X509_free(test_cert); + + test_cert = (*test_chain)[2]->getOpenSSLX509(); + ensure("validate second element in store is expected cert", !X509_cmp(test_cert, mX509RootCert)); + X509_free(test_cert); + } + // test cert validation + template<> template<> + void sechandler_basic_test_object::test<8>() + { + // start with a trusted store with our known root cert + LLFile::remove("mycertstore.pem"); + LLPointer test_store = new LLBasicCertificateStore("mycertstore.pem"); + test_store->add(new LLBasicCertificate(mX509RootCert)); + LLSD validation_params; + + // validate basic trust for a chain containing only the intermediate cert. (1 deep) + LLPointer test_chain = new LLBasicCertificateChain(NULL); + + test_chain->add(new LLBasicCertificate(mX509IntermediateCert)); + + test_chain->validate(0, test_store, validation_params); + + // add the root certificate to the chain and revalidate + test_chain->add(new LLBasicCertificate(mX509RootCert)); + test_chain->validate(0, test_store, validation_params); + + // add the child cert at the head of the chain, and revalidate (3 deep chain) + test_chain->insert(test_chain->begin(), new LLBasicCertificate(mX509ChildCert)); + test_chain->validate(0, test_store, validation_params); + + // basic failure cases + test_chain = new LLBasicCertificateChain(NULL); + //validate with only the child cert + test_chain->add(new LLBasicCertificate(mX509ChildCert)); + ensure_throws("no CA, with only a child cert", + LLCertValidationTrustException, + (*test_chain)[0], + test_chain->validate, + VALIDATION_POLICY_TRUSTED, + test_store, + validation_params); + + + // validate without the trust flag. + test_chain->validate(0, test_store, validation_params); + + // clear out the store + test_store = new LLBasicCertificateStore("mycertstore.pem"); + // append the intermediate cert + test_chain->add(new LLBasicCertificate(mX509IntermediateCert)); + ensure_throws("no CA, with child and intermediate certs", + LLCertValidationTrustException, + (*test_chain)[1], + test_chain->validate, + VALIDATION_POLICY_TRUSTED, + test_store, + validation_params); + // validate without the trust flag + test_chain->validate(0, test_store, validation_params); + + // Test time validity + LLSD child_info = (*test_chain)[0]->getLLSD(); + validation_params = LLSD::emptyMap(); + validation_params[CERT_VALIDATION_DATE] = LLDate(child_info[CERT_VALID_FROM].asDate().secondsSinceEpoch() + 1.0); + test_chain->validate(VALIDATION_POLICY_TIME, test_store, validation_params); + + validation_params = LLSD::emptyMap(); + validation_params[CERT_VALIDATION_DATE] = child_info[CERT_VALID_FROM].asDate(); + + validation_params[CERT_VALIDATION_DATE] = LLDate(child_info[CERT_VALID_FROM].asDate().secondsSinceEpoch() - 1.0); + + // test not yet valid + ensure_throws("Child cert not yet valid", + LLCertValidationExpirationException, + (*test_chain)[0], + test_chain->validate, + VALIDATION_POLICY_TIME, + test_store, + validation_params); + validation_params = LLSD::emptyMap(); + validation_params[CERT_VALIDATION_DATE] = LLDate(child_info[CERT_VALID_TO].asDate().secondsSinceEpoch() + 1.0); + + // test cert expired + ensure_throws("Child cert expired", + LLCertValidationExpirationException, + (*test_chain)[0], + test_chain->validate, + VALIDATION_POLICY_TIME, + test_store, + validation_params); + + // test SSL KU + // validate basic trust for a chain containing child and intermediate. + test_chain = new LLBasicCertificateChain(NULL); + test_chain->add(new LLBasicCertificate(mX509ChildCert)); + test_chain->add(new LLBasicCertificate(mX509IntermediateCert)); + test_chain->validate(VALIDATION_POLICY_SSL_KU, test_store, validation_params); + + test_chain = new LLBasicCertificateChain(NULL); + test_chain->add(new LLBasicCertificate(mX509TestCert)); + + ensure_throws("Cert doesn't have ku", + LLCertKeyUsageValidationException, + (*test_chain)[0], + test_chain->validate, + VALIDATION_POLICY_SSL_KU, + test_store, + validation_params); } }; + diff --git a/indra/newview/tests/llviewernetwork_test.cpp b/indra/newview/tests/llviewernetwork_test.cpp index c7a6b2ad15..f341482d6f 100644 --- a/indra/newview/tests/llviewernetwork_test.cpp +++ b/indra/newview/tests/llviewernetwork_test.cpp @@ -37,7 +37,51 @@ #include "../../llxml/llcontrol.h" #include "llfile.h" -LLControlGroup gSavedSettings; +//---------------------------------------------------------------------------- +// Mock objects for the dependencies of the code we're testing + +LLControlGroup::LLControlGroup(const std::string& name) +: LLInstanceTracker(name) {} +LLControlGroup::~LLControlGroup() {} +BOOL LLControlGroup::declareString(const std::string& name, + const std::string& initial_val, + const std::string& comment, + BOOL persist) {return TRUE;} +void LLControlGroup::setString(const std::string& name, const std::string& val){} + +std::string gCmdLineLoginURI; +std::string gCmdLineGridChoice; +std::string gCmdLineHelperURI; +std::string gLoginPage; +std::string gCurrentGrid; +std::string LLControlGroup::getString(const std::string& name) +{ + if (name == "CmdLineGridChoice") + return gCmdLineGridChoice; + else if (name == "CmdLineHelperURI") + return gCmdLineHelperURI; + else if (name == "LoginPage") + return gLoginPage; + else if (name == "CurrentGrid") + return gCurrentGrid; + return ""; +} + +LLSD LLControlGroup::getLLSD(const std::string& name) +{ + if (name == "CmdLineLoginURI") + { + if(!gCmdLineLoginURI.empty()) + { + return LLSD(gCmdLineLoginURI); + } + } + return LLSD(); +} + + +LLControlGroup gSavedSettings("test"); + const char *gSampleGridFile = "" "grid1" " favorite1" @@ -68,13 +112,12 @@ namespace tut { viewerNetworkTest() { - gSavedSettings.cleanup(); - gSavedSettings.cleanup(); - gSavedSettings.declareString("CmdLineGridChoice", "", "", FALSE); - gSavedSettings.declareString("CmdLineHelperURI", "", "", FALSE); - gSavedSettings.declareString("LoginPage", "", "", FALSE); - gSavedSettings.declareString("CurrentGrid", "", "", FALSE); - gSavedSettings.declareLLSD("CmdLineLoginURI", LLSD(), "", FALSE); + LLFile::remove("grid_test.xml"); + gCmdLineLoginURI.clear(); + gCmdLineGridChoice.clear(); + gCmdLineHelperURI.clear(); + gLoginPage.clear(); + gCurrentGrid.clear(); } ~viewerNetworkTest() { @@ -95,20 +138,25 @@ namespace tut void viewerNetworkTestObject::test<1>() { - LLGridManager manager("grid_test.xml"); + LLGridManager *manager = LLGridManager::getInstance(); + // grid file doesn't exist + manager->initialize("grid_test.xml"); // validate that some of the defaults are available. - std::map known_grids = manager.getKnownGrids(); + std::map known_grids = manager->getKnownGrids(); #ifndef LL_RELEASE_FOR_DOWNLOAD ensure_equals("Known grids is a string-string map of size 18", known_grids.size(), 18); + ensure_equals("Agni has the right name and label", + known_grids[std::string("util.agni.lindenlab.com")], std::string("Agni")); #else // LL_RELEASE_FOR_DOWNLOAD - ensure_equals("Known grids is a string-string map of size 18", known_grids.size(), 2); + ensure_equals("Known grids is a string-string map of size 2", known_grids.size(), 2); + ensure_equals("Agni has the right name and label", + known_grids[std::string("util.agni.lindenlab.com")], std::string("Secondlife.com")); #endif // LL_RELEASE_FOR_DOWNLOAD - ensure_equals("Agni has the right name and label", - known_grids[std::string("util.agni.lindenlab.com")], std::string("Agni")); + ensure_equals("None exists", known_grids[""], "None"); - LLSD grid = manager.getGridInfo("util.agni.lindenlab.com"); + LLSD grid = LLGridManager::getInstance()->getGridInfo("util.agni.lindenlab.com"); ensure("Grid info for agni is a map", grid.isMap()); ensure_equals("name is correct for agni", grid[GRID_NAME_VALUE].asString(), std::string("util.agni.lindenlab.com")); @@ -130,8 +178,8 @@ namespace tut ensure_equals("Agni login page is correct", grid[GRID_LOGIN_PAGE_VALUE].asString(), std::string("http://secondlife.com/app/login/")); - ensure("Agni is not a favorite", - !grid.has(GRID_IS_FAVORITE_VALUE)); + ensure("Agni is a favorite", + grid.has(GRID_IS_FAVORITE_VALUE)); ensure("Agni is a system grid", grid.has(GRID_IS_SYSTEM_GRID_VALUE)); ensure("Grid file wasn't greated as it wasn't saved", @@ -146,20 +194,25 @@ namespace tut gridfile << gSampleGridFile; gridfile.close(); - LLGridManager manager("grid_test.xml"); - std::map known_grids = manager.getKnownGrids(); + LLGridManager::getInstance()->initialize("grid_test.xml"); + std::map known_grids = LLGridManager::getInstance()->getKnownGrids(); #ifndef LL_RELEASE_FOR_DOWNLOAD ensure_equals("adding a grid via a grid file increases known grid size", known_grids.size(), 19); + ensure_equals("Agni is still there after we've added a grid via a grid file", + known_grids["util.agni.lindenlab.com"], std::string("Agni")); + #else ensure_equals("adding a grid via a grid file increases known grid size", known_grids.size(), 3); -#endif ensure_equals("Agni is still there after we've added a grid via a grid file", - known_grids["util.agni.lindenlab.com"], std::string("Agni")); + known_grids["util.agni.lindenlab.com"], std::string("Secondlife.com")); + +#endif + // assure Agni doesn't get overwritten - LLSD grid = manager.getGridInfo("util.agni.lindenlab.com"); + LLSD grid = LLGridManager::getInstance()->getGridInfo("util.agni.lindenlab.com"); #ifndef LL_RELEASE_FOR_DOWNLOAD ensure_equals("Agni grid label was not modified by grid file", grid[GRID_LABEL_VALUE].asString(), std::string("Agni")); @@ -181,14 +234,14 @@ namespace tut ensure_equals("Agni login page the same after grid file", grid[GRID_LOGIN_PAGE_VALUE].asString(), std::string("http://secondlife.com/app/login/")); - ensure("Agni still not favorite after grid file", - !grid.has(GRID_IS_FAVORITE_VALUE)); + ensure("Agni still a favorite after grid file", + grid.has(GRID_IS_FAVORITE_VALUE)); ensure("Agni system grid still set after grid file", grid.has(GRID_IS_SYSTEM_GRID_VALUE)); ensure_equals("Grid file adds to name<->label map", known_grids["grid1"], std::string("mylabel")); - grid = manager.getGridInfo("grid1"); + grid = LLGridManager::getInstance()->getGridInfo("grid1"); ensure_equals("grid file grid name is set", grid[GRID_NAME_VALUE].asString(), std::string("grid1")); ensure_equals("grid file label is set", @@ -217,22 +270,16 @@ namespace tut template<> template<> void viewerNetworkTestObject::test<3>() { - LLSD loginURI = std::string("https://my.login.uri/cgi-bin/login.cgi"); - gSavedSettings.setLLSD("CmdLineLoginURI", loginURI); - LLGridManager manager("grid_test.xml"); + gCmdLineLoginURI = "https://my.login.uri/cgi-bin/login.cgi"; + LLGridManager::getInstance()->initialize("grid_test.xml"); // with single login uri specified. - std::map known_grids = manager.getKnownGrids(); -#ifndef LL_RELEASE_FOR_DOWNLOAD + std::map known_grids = LLGridManager::getInstance()->getKnownGrids(); ensure_equals("adding a command line grid increases known grid size", known_grids.size(), 19); -#else - ensure_equals("adding a command line grid increases known grid size", - known_grids.size(), 3); -#endif ensure_equals("Command line grid is added to the list of grids", known_grids["my.login.uri"], std::string("my.login.uri")); - LLSD grid = manager.getGridInfo("my.login.uri"); + LLSD grid = LLGridManager::getInstance()->getGridInfo("my.login.uri"); ensure_equals("Command line grid name is set", grid[GRID_NAME_VALUE].asString(), std::string("my.login.uri")); ensure_equals("Command line grid label is set", @@ -254,19 +301,14 @@ namespace tut !grid.has(GRID_IS_SYSTEM_GRID_VALUE)); // now try a command line with a custom grid identifier - gSavedSettings.setString("CmdLineGridChoice", "mycustomgridchoice"); - manager = LLGridManager("grid_test.xml"); - known_grids = manager.getKnownGrids(); -#ifndef LL_RELEASE_FOR_DOWNLOAD + gCmdLineGridChoice = "mycustomgridchoice"; + LLGridManager::getInstance()->initialize("grid_test.xml"); + known_grids = LLGridManager::getInstance()->getKnownGrids(); ensure_equals("adding a command line grid with custom name increases known grid size", known_grids.size(), 19); -#else - ensure_equals("adding a command line grid with custom name inceases known grid size", - known_grids.size(), 3); -#endif ensure_equals("Custom Command line grid is added to the list of grids", known_grids["mycustomgridchoice"], std::string("mycustomgridchoice")); - grid = manager.getGridInfo("mycustomgridchoice"); + grid = LLGridManager::getInstance()->getGridInfo("mycustomgridchoice"); ensure_equals("Custom Command line grid name is set", grid[GRID_NAME_VALUE].asString(), std::string("mycustomgridchoice")); ensure_equals("Custom Command line grid label is set", @@ -278,16 +320,16 @@ namespace tut std::string("https://my.login.uri/cgi-bin/login.cgi")); // add a helperuri - gSavedSettings.setString("CmdLineHelperURI", "myhelperuri"); - manager = LLGridManager("grid_test.xml"); - grid = manager.getGridInfo("mycustomgridchoice"); + gCmdLineHelperURI = "myhelperuri"; + LLGridManager::getInstance()->initialize("grid_test.xml"); + grid = LLGridManager::getInstance()->getGridInfo("mycustomgridchoice"); ensure_equals("Validate command line helper uri", grid[GRID_HELPER_URI_VALUE].asString(), std::string("myhelperuri")); // add a login page - gSavedSettings.setString("LoginPage", "myloginpage"); - manager = LLGridManager("grid_test.xml"); - grid = manager.getGridInfo("mycustomgridchoice"); + gLoginPage = "myloginpage"; + LLGridManager::getInstance()->initialize("grid_test.xml"); + grid = LLGridManager::getInstance()->getGridInfo("mycustomgridchoice"); ensure_equals("Validate command line helper uri", grid[GRID_LOGIN_PAGE_VALUE].asString(), std::string("myloginpage")); } @@ -301,36 +343,34 @@ namespace tut // adding a grid with simply a name will populate the values. grid[GRID_NAME_VALUE] = "myaddedgrid"; - loginURI.append(std::string("https://my.login.uri/cgi-bin/login.cgi")); - gSavedSettings.setLLSD("CmdLineLoginURI", loginURI); - LLGridManager manager("grid_test.xml"); - manager.addGrid(grid); - manager.setGridChoice("util.agni.lindenlab.com"); + LLGridManager::getInstance()->initialize("grid_test.xml"); + LLGridManager::getInstance()->addGrid(grid); + LLGridManager::getInstance()->setGridChoice("util.agni.lindenlab.com"); #ifndef LL_RELEASE_FOR_DOWNLOAD - ensure_equals("getGridLabel", manager.getGridLabel(), std::string("Agni")); + ensure_equals("getGridLabel", LLGridManager::getInstance()->getGridLabel(), std::string("Agni")); #else // LL_RELEASE_FOR_DOWNLOAD - ensure_equals("getGridLabel", manager.getGridLabel(), std::string("Secondlife.com")); + ensure_equals("getGridLabel", LLGridManager::getInstance()->getGridLabel(), std::string("Secondlife.com")); #endif // LL_RELEASE_FOR_DOWNLOAD - ensure_equals("getGridName", manager.getGridName(), + ensure_equals("getGridName", LLGridManager::getInstance()->getGridName(), std::string("util.agni.lindenlab.com")); - ensure_equals("getHelperURI", manager.getHelperURI(), + ensure_equals("getHelperURI", LLGridManager::getInstance()->getHelperURI(), std::string("https://secondlife.com/helpers/")); - ensure_equals("getLoginPage", manager.getLoginPage(), + ensure_equals("getLoginPage", LLGridManager::getInstance()->getLoginPage(), std::string("http://secondlife.com/app/login/")); - ensure_equals("getLoginPage2", manager.getLoginPage("util.agni.lindenlab.com"), + ensure_equals("getLoginPage2", LLGridManager::getInstance()->getLoginPage("util.agni.lindenlab.com"), std::string("http://secondlife.com/app/login/")); - ensure("Is Agni a production grid", manager.isInProductionGrid()); + ensure("Is Agni a production grid", LLGridManager::getInstance()->isInProductionGrid()); std::vector uris; - manager.getLoginURIs(uris); + LLGridManager::getInstance()->getLoginURIs(uris); ensure_equals("getLoginURIs size", uris.size(), 1); ensure_equals("getLoginURIs", uris[0], std::string("https://login.agni.lindenlab.com/cgi-bin/login.cgi")); - manager.setGridChoice("myaddedgrid"); - ensure_equals("getGridLabel", manager.getGridLabel(), std::string("myaddedgrid")); - ensure("Is myaddedgrid a production grid", !manager.isInProductionGrid()); + LLGridManager::getInstance()->setGridChoice("myaddedgrid"); + ensure_equals("getGridLabel", LLGridManager::getInstance()->getGridLabel(), std::string("myaddedgrid")); + ensure("Is myaddedgrid a production grid", !LLGridManager::getInstance()->isInProductionGrid()); - manager.setFavorite(); - grid = manager.getGridInfo("myaddedgrid"); + LLGridManager::getInstance()->setFavorite(); + grid = LLGridManager::getInstance()->getGridInfo("myaddedgrid"); ensure("setting favorite", grid.has(GRID_IS_FAVORITE_VALUE)); } @@ -338,12 +378,12 @@ namespace tut template<> template<> void viewerNetworkTestObject::test<5>() { - LLGridManager manager("grid_test.xml"); + LLGridManager::getInstance()->initialize("grid_test.xml"); LLSD grid = LLSD::emptyMap(); // adding a grid with simply a name will populate the values. grid[GRID_NAME_VALUE] = "myaddedgrid"; - manager.addGrid(grid); - grid = manager.getGridInfo("myaddedgrid"); + LLGridManager::getInstance()->addGrid(grid); + grid = LLGridManager::getInstance()->getGridInfo("myaddedgrid"); ensure_equals("name based grid has name value", grid[GRID_NAME_VALUE].asString(), @@ -380,29 +420,29 @@ namespace tut { // try with initial grid list without a grid file, // without setting the grid to a saveable favorite. - LLGridManager manager("grid_test.xml"); + LLGridManager::getInstance()->initialize("grid_test.xml"); LLSD grid = LLSD::emptyMap(); grid[GRID_NAME_VALUE] = std::string("mynewgridname"); - manager.addGrid(grid); - manager.saveFavorites(); + LLGridManager::getInstance()->addGrid(grid); + LLGridManager::getInstance()->saveFavorites(); ensure("Grid file exists after saving", LLFile::isfile("grid_test.xml")); - manager = LLGridManager("grid_test.xml"); + LLGridManager::getInstance()->initialize("grid_test.xml"); // should not be there - std::map known_grids = manager.getKnownGrids(); + std::map known_grids = LLGridManager::getInstance()->getKnownGrids(); ensure("New grid wasn't added to persisted list without being marked a favorite", known_grids.find(std::string("mynewgridname")) == known_grids.end()); // mark a grid a favorite to make sure it's persisted - manager.addGrid(grid); - manager.setGridChoice("mynewgridname"); - manager.setFavorite(); - manager.saveFavorites(); + LLGridManager::getInstance()->addGrid(grid); + LLGridManager::getInstance()->setGridChoice("mynewgridname"); + LLGridManager::getInstance()->setFavorite(); + LLGridManager::getInstance()->saveFavorites(); ensure("Grid file exists after saving", LLFile::isfile("grid_test.xml")); - manager = LLGridManager("grid_test.xml"); + LLGridManager::getInstance()->initialize("grid_test.xml"); // should not be there - known_grids = manager.getKnownGrids(); + known_grids = LLGridManager::getInstance()->getKnownGrids(); ensure("New grid wasn't added to persisted list after being marked a favorite", known_grids.find(std::string("mynewgridname")) != known_grids.end()); @@ -417,28 +457,28 @@ namespace tut gridfile << gSampleGridFile; gridfile.close(); - LLGridManager manager("grid_test.xml"); + LLGridManager::getInstance()->initialize("grid_test.xml"); LLSD grid = LLSD::emptyMap(); grid[GRID_NAME_VALUE] = std::string("mynewgridname"); - manager.addGrid(grid); - manager.saveFavorites(); + LLGridManager::getInstance()->addGrid(grid); + LLGridManager::getInstance()->saveFavorites(); // validate we didn't lose existing favorites - manager = LLGridManager("grid_test.xml"); - std::map known_grids = manager.getKnownGrids(); + LLGridManager::getInstance()->initialize("grid_test.xml"); + std::map known_grids = LLGridManager::getInstance()->getKnownGrids(); ensure("New grid wasn't added to persisted list after being marked a favorite", known_grids.find(std::string("grid1")) != known_grids.end()); // add a grid - manager.addGrid(grid); - manager.setGridChoice("mynewgridname"); - manager.setFavorite(); - manager.saveFavorites(); - known_grids = manager.getKnownGrids(); + LLGridManager::getInstance()->addGrid(grid); + LLGridManager::getInstance()->setGridChoice("mynewgridname"); + LLGridManager::getInstance()->setFavorite(); + LLGridManager::getInstance()->saveFavorites(); + known_grids = LLGridManager::getInstance()->getKnownGrids(); ensure("New grid wasn't added to persisted list after being marked a favorite", known_grids.find(std::string("grid1")) != known_grids.end()); - known_grids = manager.getKnownGrids(); + known_grids = LLGridManager::getInstance()->getKnownGrids(); ensure("New grid wasn't added to persisted list after being marked a favorite", known_grids.find(std::string("mynewgridname")) != known_grids.end()); diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp index 7723c3ca8f..452930a3b3 100644 --- a/indra/viewer_components/login/lllogin.cpp +++ b/indra/viewer_components/login/lllogin.cpp @@ -292,7 +292,12 @@ void LLLogin::Impl::login_(LLCoros::self& self, std::string uri, LLSD login_para // to success, add a data/message and data/reason fields. LLSD error_response; error_response["reason"] = mAuthResponse["status"]; + error_response["errorcode"] = mAuthResponse["errorcode"]; error_response["message"] = mAuthResponse["error"]; + if(mAuthResponse.has("certificate")) + { + error_response["certificate"] = mAuthResponse["certificate"]; + } sendProgressEvent("offline", "fail.login", error_response); } catch (...) { -- cgit v1.3 From 7a64aad1def1b0612addbf2e66c66db061d7e182 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Thu, 9 Jul 2009 20:56:23 +0000 Subject: DEV-34822 - merge 1.23 merge -r119443 - basic slurl handling ignore dead branch --- indra/llcommon/lluri.cpp | 3 +- indra/llui/llurlentry.cpp | 29 +- indra/llui/tests/llurlentry_test.cpp | 24 ++ indra/newview/CMakeLists.txt | 11 +- indra/newview/Info-SecondLife.plist | 28 ++ .../installers/windows/installer_template.nsi | 6 + indra/newview/llagent.h | 5 +- indra/newview/llagentlistener.cpp | 5 +- indra/newview/llagentui.cpp | 17 +- indra/newview/llagentui.h | 3 +- indra/newview/llappviewer.cpp | 57 +-- indra/newview/llappviewermacosx.cpp | 1 - indra/newview/llfloaterbuyland.cpp | 2 +- indra/newview/llfloaterchat.cpp | 2 +- indra/newview/llfloaterland.cpp | 6 +- indra/newview/llfloaterpreference.cpp | 4 +- indra/newview/llfloaterregioninfo.cpp | 3 +- indra/newview/llfloaterreporter.cpp | 6 +- indra/newview/llfloaterworldmap.cpp | 23 +- indra/newview/llfloaterworldmap.h | 3 +- indra/newview/llinspectobject.cpp | 6 +- indra/newview/llinspectremoteobject.cpp | 4 +- indra/newview/lllandmarkactions.cpp | 4 +- indra/newview/lllocationinputctrl.cpp | 5 +- indra/newview/llloginhandler.cpp | 20 +- indra/newview/llloginhandler.h | 2 +- indra/newview/lllogininstance.cpp | 16 +- indra/newview/llnavigationbar.cpp | 18 +- indra/newview/llpanellogin.cpp | 262 ++++++++---- indra/newview/llpanellogin.h | 13 +- indra/newview/llpanelplacestab.cpp | 5 +- indra/newview/llselectmgr.cpp | 8 +- indra/newview/llslurl.cpp | 470 +++++++++++++++++---- indra/newview/llslurl.h | 146 +++---- indra/newview/llstartup.cpp | 87 ++-- indra/newview/llstartup.h | 9 +- indra/newview/llstylemap.cpp | 2 +- indra/newview/llurldispatcher.cpp | 213 ++++------ indra/newview/llurldispatcher.h | 18 +- indra/newview/llurldispatcherlistener.cpp | 1 + indra/newview/llurllineeditorctrl.cpp | 2 +- indra/newview/llviewermessage.cpp | 10 +- indra/newview/llviewernetwork.cpp | 36 +- indra/newview/llviewernetwork.h | 9 +- indra/newview/llviewerwindow.cpp | 2 +- .../newview/skins/default/xui/en/notifications.xml | 9 + indra/newview/tests/lllogininstance_test.cpp | 9 +- indra/newview/tests/llslurl_test.cpp | 258 +++++++++++ 48 files changed, 1306 insertions(+), 576 deletions(-) create mode 100644 indra/newview/tests/llslurl_test.cpp (limited to 'indra/newview/llviewernetwork.cpp') diff --git a/indra/llcommon/lluri.cpp b/indra/llcommon/lluri.cpp index f6e8f01f0e..b28657e2f4 100644 --- a/indra/llcommon/lluri.cpp +++ b/indra/llcommon/lluri.cpp @@ -220,7 +220,8 @@ static BOOL isDefault(const std::string& scheme, U16 port) void LLURI::parseAuthorityAndPathUsingOpaque() { if (mScheme == "http" || mScheme == "https" || - mScheme == "ftp" || mScheme == "secondlife" ) + mScheme == "ftp" || mScheme == "secondlife" || + mScheme == "x-grid-location-info") { if (mEscapedOpaque.substr(0,2) != "//") { diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp index 1b6dd1b264..466ac2a7d1 100644 --- a/indra/llui/llurlentry.cpp +++ b/indra/llui/llurlentry.cpp @@ -39,6 +39,9 @@ #include "lltrans.h" #include "lluicolortable.h" +#define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))" + + LLUrlEntryBase::LLUrlEntryBase() : mColor(LLUIColorTable::instance().getColor("HTMLLinkColor")) { @@ -300,10 +303,11 @@ std::string LLUrlEntrySLURL::getLocation(const std::string &url) const // // LLUrlEntryAgent Describes a Second Life agent Url, e.g., // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about +// x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about // LLUrlEntryAgent::LLUrlEntryAgent() { - mPattern = boost::regex("secondlife:///app/agent/[\\da-f-]+/\\w+", + mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/\\w+", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_agent.xml"; mIcon = "Generic_Person"; @@ -358,10 +362,11 @@ std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCa // LLUrlEntryGroup Describes a Second Life group Url, e.g., // secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about // secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect +// x-grid-location-info://lincoln.lindenlab.com/app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect // LLUrlEntryGroup::LLUrlEntryGroup() { - mPattern = boost::regex("secondlife:///app/group/[\\da-f-]+/\\w+", + mPattern = boost::regex(APP_HEADER_REGEX "/group/[\\da-f-]+/\\w+", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_group.xml"; mIcon = "Generic_Group"; @@ -422,7 +427,8 @@ LLUrlEntryInventory::LLUrlEntryInventory() //*TODO: add supporting of inventory item names with whitespaces //this pattern cann't parse for example //secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces¶m2=value - mPattern = boost::regex("secondlife:///app/inventory/[\\da-f-]+/\\w+\\S*", + //x-grid-location-info://lincoln.lindenlab.com/app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces¶m2=value + mPattern = boost::regex(APP_HEADER_REGEX "/inventory/[\\da-f-]+/\\w+\\S*", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_inventory.xml"; } @@ -436,10 +442,11 @@ std::string LLUrlEntryInventory::getLabel(const std::string &url, const LLUrlLab /// /// LLUrlEntryParcel Describes a Second Life parcel Url, e.g., /// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about +/// x-grid-location-info://lincoln.lindenlab.com/app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about /// LLUrlEntryParcel::LLUrlEntryParcel() { - mPattern = boost::regex("secondlife:///app/parcel/[\\da-f-]+/about", + mPattern = boost::regex(APP_HEADER_REGEX "/parcel/[\\da-f-]+/about", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_parcel.xml"; mTooltip = LLTrans::getString("TooltipParcelUrl"); @@ -455,7 +462,7 @@ std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelC // LLUrlEntryPlace::LLUrlEntryPlace() { - mPattern = boost::regex("secondlife://\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?", + mPattern = boost::regex("((x-grid-location-info://[-\\w\\.]+/region/)|(secondlife://))\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_slurl.xml"; mTooltip = LLTrans::getString("TooltipSLURL"); @@ -500,10 +507,11 @@ std::string LLUrlEntryPlace::getLocation(const std::string &url) const // // LLUrlEntryTeleport Describes a Second Life teleport Url, e.g., // secondlife:///app/teleport/Ahern/50/50/50/ +// x-grid-location-info://lincoln.lindenlab.com/app/teleport/Ahern/50/50/50/ // LLUrlEntryTeleport::LLUrlEntryTeleport() { - mPattern = boost::regex("secondlife:///app/teleport/\\S+(/\\d+)?(/\\d+)?(/\\d+)?/?\\S*", + mPattern = boost::regex(APP_HEADER_REGEX "/teleport/\\S+(/\\d+)?(/\\d+)?(/\\d+)?/?\\S*", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_teleport.xml"; mTooltip = LLTrans::getString("TooltipTeleportUrl"); @@ -521,7 +529,12 @@ std::string LLUrlEntryTeleport::getLabel(const std::string &url, const LLUrlLabe LLURI uri(url); LLSD path_array = uri.pathArray(); S32 path_parts = path_array.size(); - const std::string label = LLTrans::getString("SLurlLabelTeleport"); + std::string host = uri.hostName(); + std::string label = LLTrans::getString("SLurlLabelTeleport"); + if (!host.empty()) + { + label += " " + host; + } if (path_parts == 6) { // handle teleport url with (X,Y,Z) coordinates @@ -606,7 +619,7 @@ std::string LLUrlEntrySLLabel::getUrl(const std::string &string) // LLUrlEntryWorldMap::LLUrlEntryWorldMap() { - mPattern = boost::regex("secondlife:///app/worldmap/\\S+/?(\\d+)?/?(\\d+)?/?(\\d+)?/?\\S*", + mPattern = boost::regex(APP_HEADER_REGEX "/worldmap/\\S+/?(\\d+)?/?(\\d+)?/?(\\d+)?/?\\S*", boost::regex::perl|boost::regex::icase); mMenuName = "menu_url_map.xml"; mTooltip = LLTrans::getString("TooltipMapUrl"); diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp index 38cf7124ce..1d66bc268b 100644 --- a/indra/llui/tests/llurlentry_test.cpp +++ b/indra/llui/tests/llurlentry_test.cpp @@ -289,6 +289,13 @@ namespace tut "XXX secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar", "secondlife:///App/AGENT/0E346D8B-4433-4d66-a6b0-fd37083abc4c/foobar"); + testRegex("Nebraska Agent Url ", r, + "x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about", + "x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); + + testRegex("Nebraska Agent Url Multicase with Text", r, + "M x-grid-location-info://lincoln.lindenlab.com/app/AGENT/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about M", + "x-grid-location-info://lincoln.lindenlab.com/app/AGENT/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); } template<> template<> @@ -319,6 +326,15 @@ namespace tut testRegex("Group Url multicase", r, "XXX secondlife:///APP/Group/00005FF3-4044-c79f-9de8-fb28ae0df991/About XXX", "secondlife:///APP/Group/00005FF3-4044-c79f-9de8-fb28ae0df991/About"); + + testRegex("Nebraska Group Url ", r, + "x-grid-location-info://lincoln.lindenlab.com/app/group/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about", + "x-grid-location-info://lincoln.lindenlab.com/app/group/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); + + testRegex("Nebraska Group Url Multicase ith Text", r, + "M x-grid-location-info://lincoln.lindenlab.com/app/GROUP/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about M", + "x-grid-location-info://lincoln.lindenlab.com/app/GROUP/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about"); + } template<> template<> @@ -367,6 +383,10 @@ namespace tut testRegex("SLURL with quote", r, "XXX secondlife://A'ksha%20Oasis/41/166/701 XXX", "secondlife://A'ksha%20Oasis/41/166/701"); + + testRegex("Nebraska All Hands (50,50) [2] with text", r, + "XXX x-grid-location-info://lincoln.lindenlab.com/region/All%20Hands/50/50/50 XXX", + "x-grid-location-info://lincoln.lindenlab.com/region/All%20Hands/50/50/50"); } template<> template<> @@ -468,6 +488,10 @@ namespace tut testRegex("Teleport url with quote", r, "XXX secondlife:///app/teleport/A'ksha%20Oasis/41/166/701 XXX", "secondlife:///app/teleport/A'ksha%20Oasis/41/166/701"); + + testRegex("Nebraska All Hands", r, + "XXX x-grid-location-info://lincoln.lindenlab.com/app/teleport/All%20Hands/50/50/50 XXX", + "x-grid-location-info://lincoln.lindenlab.com/app/teleport/All%20Hands/50/50/50"); } template<> template<> diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 35f0a5036d..7891add9b8 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -449,7 +449,6 @@ set(viewer_SOURCE_FILES llurldispatcherlistener.cpp llurlhistory.cpp llurllineeditorctrl.cpp - llurlsimstring.cpp llurlwhitelist.cpp llvectorperfoptions.cpp llversioninfo.cpp @@ -956,7 +955,6 @@ set(viewer_HEADER_FILES llurldispatcherlistener.h llurlhistory.h llurllineeditorctrl.h - llurlsimstring.h llurlwhitelist.h llvectorperfoptions.h llversioninfo.h @@ -1822,12 +1820,21 @@ if (LL_TESTS) llsecapi.cpp "${test_libs}" ) + set(llslurl_test_sources + llslurl.cpp + llviewernetwork.cpp + ) LL_ADD_INTEGRATION_TEST(llviewernetwork llviewernetwork.cpp "${test_libs}" ) + LL_ADD_INTEGRATION_TEST(llslurl + "${llslurl_test_sources}" + "${test_libs}" + ) + #ADD_VIEWER_BUILD_TEST(llmemoryview viewer) #ADD_VIEWER_BUILD_TEST(llagentaccess viewer) #ADD_VIEWER_BUILD_TEST(llworldmap viewer) diff --git a/indra/newview/Info-SecondLife.plist b/indra/newview/Info-SecondLife.plist index 38ebb22b84..a800f4ba79 100644 --- a/indra/newview/Info-SecondLife.plist +++ b/indra/newview/Info-SecondLife.plist @@ -18,6 +18,33 @@ APPL CFBundleSignature ???? + CFBundleDocumentTypes + + + CFBundleTypeExtensions + + slurl + + CFBundleTypeIconFile + seconlife + CFBundleTypeMIMETypes + + application/x-grid-location-info + + CFBundleTypeName + Secondlife SLURL + CFBundleTypeOSTypes + + SLRL + + CFBundleTypeRole + Viewer + LSTypeIsPackage + + NSDocumentClass + SecondLifeSLURL + + CFBundleURLTypes @@ -26,6 +53,7 @@ CFBundleURLSchemes secondlife + x-grid-location-info LSIsAppleDefaultForScheme diff --git a/indra/newview/installers/windows/installer_template.nsi b/indra/newview/installers/windows/installer_template.nsi index a7322749ca..b7b4c54001 100644 --- a/indra/newview/installers/windows/installer_template.nsi +++ b/indra/newview/installers/windows/installer_template.nsi @@ -797,6 +797,12 @@ WriteRegStr HKEY_CLASSES_ROOT "${URLNAME}\DefaultIcon" "" '"$INSTDIR\$INSTEXE"' ;; URL param must be last item passed to viewer, it ignores subsequent params ;; to avoid parameter injection attacks. WriteRegExpandStr HKEY_CLASSES_ROOT "${URLNAME}\shell\open\command" "" '"$INSTDIR\$INSTEXE" $INSTFLAGS -url "%1"' +WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info"(default)" "URL:Second Life" +WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info" "URL Protocol" "" +WriteRegStr HKEY_CLASSES_ROOT "x-grid-location-info\DefaultIcon" "" '"$INSTDIR\$INSTEXE"' +;; URL param must be last item passed to viewer, it ignores subsequent params +;; to avoid parameter injection attacks. +WriteRegExpandStr HKEY_CLASSES_ROOT "x-grid-location-info\shell\open\command" "" '"$INSTDIR\$INSTEXE" $INSTFLAGS -url "%1"' ; write out uninstaller WriteUninstaller "$INSTDIR\uninst.exe" diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index 2e95dc72be..4ea1185925 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -45,6 +45,7 @@ #include "llpointer.h" #include "lluicolor.h" #include "llvoavatardefines.h" +#include "llslurl.h" extern const BOOL ANIMATE; extern const U8 AGENT_STATE_TYPING; // Typing indication @@ -594,13 +595,13 @@ public: public: static void parseTeleportMessages(const std::string& xml_filename); - const std::string getTeleportSourceSLURL() const { return mTeleportSourceSLURL; } + const LLSLURL getTeleportSourceSLURL() const { return mTeleportSourceSLURL; } public: // ! TODO ! Define ERROR and PROGRESS enums here instead of exposing the mappings. static std::map sTeleportErrorMessages; static std::map sTeleportProgressMessages; private: - std::string mTeleportSourceSLURL; // SLURL where last TP began + LLSLURL mTeleportSourceSLURL; // SLURL where last TP began //-------------------------------------------------------------------- // Teleport Actions diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp index b3ed7c353e..7a8205acb5 100644 --- a/indra/newview/llagentlistener.cpp +++ b/indra/newview/llagentlistener.cpp @@ -53,7 +53,10 @@ void LLAgentListener::requestTeleport(LLSD const & event_data) const } else { - std::string url = LLSLURL::buildSLURL(event_data["regionname"], event_data["x"], event_data["y"], event_data["z"]); + std::string url = LLSLURL(event_data["regionname"], + LLVector3(event_data["x"].asReal(), + event_data["y"].asReal(), + event_data["z"].asReal())).getSLURLString(); LLURLDispatcher::dispatch(url, NULL, false); } } diff --git a/indra/newview/llagentui.cpp b/indra/newview/llagentui.cpp index 7404fe5bc4..e26c6470ef 100644 --- a/indra/newview/llagentui.cpp +++ b/indra/newview/llagentui.cpp @@ -77,16 +77,15 @@ void LLAgentUI::buildFullname(std::string& name) } //static -std::string LLAgentUI::buildSLURL(const bool escaped /*= true*/) +LLSLURL LLAgentUI::buildSLURL(const bool escaped /*= true*/) { - std::string slurl; - LLViewerRegion *regionp = gAgent.getRegion(); - if (regionp) - { - LLVector3d agentPos = gAgent.getPositionGlobal(); - slurl = LLSLURL::buildSLURLfromPosGlobal(regionp->getName(), agentPos, escaped); - } - return slurl; + LLSLURL slurl; + LLViewerRegion *regionp = gAgent.getRegion(); + if (regionp) + { + slurl = LLSLURL(regionp->getName(), gAgent.getPositionGlobal()); + } + return slurl; } //static diff --git a/indra/newview/llagentui.h b/indra/newview/llagentui.h index 3478793e38..9d10bc7322 100644 --- a/indra/newview/llagentui.h +++ b/indra/newview/llagentui.h @@ -32,6 +32,7 @@ #ifndef LLAGENTUI_H #define LLAGENTUI_H +#include "llslurl.h" class LLAgentUI { @@ -48,7 +49,7 @@ public: static void buildName(std::string& name); static void buildFullname(std::string &name); - static std::string buildSLURL(const bool escaped = true); + static LLSLURL buildSLURL(const bool escaped = true); //build location string using the current position of gAgent. static BOOL buildLocationString(std::string& str, ELocationFormat fmt = LOCATION_FORMAT_LANDMARK); //build location string using a region position of the avatar. diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index cc32346441..d1e33fa91a 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -150,7 +150,7 @@ #include "llworld.h" #include "llhudeffecttrail.h" #include "llvectorperfoptions.h" -#include "llurlsimstring.h" +#include "llslurl.h" #include "llwatchdog.h" // Included so that constants/settings might be initialized @@ -2078,30 +2078,21 @@ bool LLAppViewer::initConfiguration() // injection and steal passwords. Phoenix. SL-55321 if(clp.hasOption("url")) { - std::string slurl = clp.getOption("url")[0]; - if (LLSLURL::isSLURLCommand(slurl)) - { - LLStartUp::sSLURLCommand = slurl; - } - else - { - LLURLSimString::setString(slurl); - } + LLStartUp::setStartSLURL(LLSLURL(clp.getOption("url")[0])); + if(LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION) + { + LLGridManager::getInstance()->setGridChoice(LLStartUp::getStartSLURL().getGrid()); + + } } else if(clp.hasOption("slurl")) { - std::string slurl = clp.getOption("slurl")[0]; - if(LLSLURL::isSLURL(slurl)) - { - if (LLSLURL::isSLURLCommand(slurl)) - { - LLStartUp::sSLURLCommand = slurl; - } - else - { - LLURLSimString::setString(slurl); - } - } + LLStartUp::setStartSLURL(LLSLURL(clp.getOption("surl")[0])); + if(LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION) + { + LLGridManager::getInstance()->setGridChoice(LLStartUp::getStartSLURL().getGrid()); + + } } const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent"); @@ -2180,18 +2171,10 @@ bool LLAppViewer::initConfiguration() // don't call anotherInstanceRunning() when doing URL handoff, as // it relies on checking a marker file which will not work when running // out of different directories - std::string slurl; - if (!LLStartUp::sSLURLCommand.empty()) - { - slurl = LLStartUp::sSLURLCommand; - } - else if (LLURLSimString::parse()) - { - slurl = LLURLSimString::getURL(); - } - if (!slurl.empty()) + + if (LLStartUp::getStartSLURL().isValid()) { - if (sendURLToOtherInstance(slurl)) + if (sendURLToOtherInstance(LLStartUp::getStartSLURL().getSLURLString())) { // successfully handed off URL to existing instance, exit return false; @@ -2247,9 +2230,9 @@ bool LLAppViewer::initConfiguration() // need to do this here - need to have initialized global settings first std::string nextLoginLocation = gSavedSettings.getString( "NextLoginLocation" ); - if ( nextLoginLocation.length() ) + if ( !nextLoginLocation.empty() ) { - LLURLSimString::setString( nextLoginLocation ); + LLStartUp::setStartSLURL(LLSLURL(nextLoginLocation)); }; gLastRunVersion = gSavedSettings.getString("LastRunVersion"); @@ -4246,10 +4229,10 @@ void LLAppViewer::launchUpdater() LLAppViewer::sUpdaterInfo = new LLAppViewer::LLUpdaterInfo() ; // if a sim name was passed in via command line parameter (typically through a SLURL) - if ( LLURLSimString::sInstance.mSimString.length() ) + if ( LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION ) { // record the location to start at next time - gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString ); + gSavedSettings.setString( "NextLoginLocation", LLStartUp::getStartSLURL().getSLURLString()); }; #if LL_WINDOWS diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp index 1282e437f2..d8c4a7d9d9 100644 --- a/indra/newview/llappviewermacosx.cpp +++ b/indra/newview/llappviewermacosx.cpp @@ -44,7 +44,6 @@ #include "llviewernetwork.h" #include "llviewercontrol.h" #include "llmd5.h" -#include "llurlsimstring.h" #include "llfloaterworldmap.h" #include "llurldispatcher.h" #include diff --git a/indra/newview/llfloaterbuyland.cpp b/indra/newview/llfloaterbuyland.cpp index 10a4908f3a..5fb129b87b 100644 --- a/indra/newview/llfloaterbuyland.cpp +++ b/indra/newview/llfloaterbuyland.cpp @@ -808,7 +808,7 @@ void LLFloaterBuyLandUI::updateNames() else { mParcelSellerName = - LLSLURL::buildCommand("agent", parcelp->getOwnerID(), "inspect"); + LLSLURL("agent", parcelp->getOwnerID(), "inspect").getSLURLString(); } } diff --git a/indra/newview/llfloaterchat.cpp b/indra/newview/llfloaterchat.cpp index b9e0f928f1..09b47c9866 100644 --- a/indra/newview/llfloaterchat.cpp +++ b/indra/newview/llfloaterchat.cpp @@ -168,7 +168,7 @@ void add_timestamped_line(LLViewerTextEditor* edit, LLChat chat, const LLColor4& if (chat.mSourceType == CHAT_SOURCE_AGENT && chat.mFromID != LLUUID::null) { - chat.mURL = LLSLURL::buildCommand("agent", chat.mFromID, "inspect"); + chat.mURL = LLSLURL("agent", chat.mFromID, "inspect").getSLURLString(); } // If the chat line has an associated url, link it up to the name. diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index 9b6e24f251..920c7ac58c 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -767,7 +767,7 @@ void LLPanelLandGeneral::refreshNames() else { // Figure out the owner's name - owner = LLSLURL::buildCommand("agent", parcel->getOwnerID(), "inspect"); + owner = LLSLURL("agent", parcel->getOwnerID(), "inspect").getSLURLString(); } if(LLParcel::OS_LEASE_PENDING == parcel->getOwnershipStatus()) @@ -779,7 +779,7 @@ void LLPanelLandGeneral::refreshNames() std::string group; if (!parcel->getGroupID().isNull()) { - group = LLSLURL::buildCommand("group", parcel->getGroupID(), "inspect"); + group = LLSLURL("group", parcel->getGroupID(), "inspect").getSLURLString(); } mTextGroup->setText(group); @@ -787,7 +787,7 @@ void LLPanelLandGeneral::refreshNames() if(auth_buyer_id.notNull()) { std::string name; - name = LLSLURL::buildCommand("agent", auth_buyer_id, "inspect"); + name = LLSLURL("agent", auth_buyer_id, "inspect").getSLURLString(); mSaleInfoForSale2->setTextArg("[BUYER]", name); } else diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index d0716f67b8..f43aa697d1 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -614,7 +614,7 @@ void LLFloaterPreference::onBtnOK() llinfos << "Can't close preferences!" << llendl; } - LLPanelLogin::refreshLocation( false ); + LLPanelLogin::updateLocationCombo( false ); } // static @@ -631,7 +631,7 @@ void LLFloaterPreference::onBtnApply( ) apply(); saveSettings(); - LLPanelLogin::refreshLocation( false ); + LLPanelLogin::updateLocationCombo( false ); } // static diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index c4b87c1b2d..1bed47e7e1 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -2926,8 +2926,7 @@ bool LLDispatchEstateUpdateInfo::operator()( LLUUID owner_id(strings[1]); regionp->setOwner(owner_id); // Update estate owner name in UI - std::string owner_name = - LLSLURL::buildCommand("agent", owner_id, "inspect"); + std::string owner_name = LLSLURL("agent", owner_id, "inspect").getSLURLString(); panel->setOwnerName(owner_name); U32 estate_id = strtoul(strings[2].c_str(), NULL, 10); diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index a52fe131cd..3fd37c09dd 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -124,7 +124,7 @@ void LLFloaterReporter::processRegionInfo(LLMessageSystem* msg) // virtual BOOL LLFloaterReporter::postBuild() { - childSetText("abuse_location_edit", LLAgentUI::buildSLURL()); + childSetText("abuse_location_edit", LLAgentUI::buildSLURL().getSLURLString()); enableControls(TRUE); @@ -279,7 +279,7 @@ void LLFloaterReporter::getObjectInfo(const LLUUID& object_id) } childSetText("object_name", object_owner); std::string owner_link = - LLSLURL::buildCommand("agent", mObjectID, "inspect"); + LLSLURL("agent", mObjectID, "inspect").getSLURLString(); childSetText("owner_name", owner_link); childSetText("abuser_name_edit", object_owner); mAbuserID = object_id; @@ -483,7 +483,7 @@ void LLFloaterReporter::setPickedObjectProperties(const std::string& object_name { childSetText("object_name", object_name); std::string owner_link = - LLSLURL::buildCommand("agent", owner_id, "inspect"); + LLSLURL("agent", owner_id, "inspect").getSLURLString(); childSetText("owner_name", owner_link); childSetText("abuser_name_edit", owner_name); mAbuserID = owner_id; diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp index 0781d8ed06..49416fff1c 100644 --- a/indra/newview/llfloaterworldmap.cpp +++ b/indra/newview/llfloaterworldmap.cpp @@ -457,7 +457,7 @@ void LLFloaterWorldMap::draw() childSetEnabled("Teleport", (BOOL)tracking_status); // childSetEnabled("Clear", (BOOL)tracking_status); childSetEnabled("Show Destination", (BOOL)tracking_status || LLWorldMap::getInstance()->isTracking()); - childSetEnabled("copy_slurl", (mSLURL.size() > 0) ); + childSetEnabled("copy_slurl", (mSLURL.isValid()) ); setMouseOpaque(TRUE); getDragHandle()->setMouseOpaque(TRUE); @@ -656,14 +656,8 @@ void LLFloaterWorldMap::updateLocation() childSetValue("location", agent_sim_name); // Figure out where user is - LLVector3d agentPos = gAgent.getPositionGlobal(); - - S32 agent_x = llround( (F32)fmod( agentPos.mdV[VX], (F64)REGION_WIDTH_METERS ) ); - S32 agent_y = llround( (F32)fmod( agentPos.mdV[VY], (F64)REGION_WIDTH_METERS ) ); - S32 agent_z = llround( (F32)agentPos.mdV[VZ] ); - // Set the current SLURL - mSLURL = LLSLURL::buildSLURL(agent_sim_name, agent_x, agent_y, agent_z); + mSLURL = LLSLURL(agent_sim_name, gAgent.getPositionGlobal()); } } @@ -690,18 +684,15 @@ void LLFloaterWorldMap::updateLocation() } childSetValue("location", sim_name); - - F32 region_x = (F32)fmod( pos_global.mdV[VX], (F64)REGION_WIDTH_METERS ); - F32 region_y = (F32)fmod( pos_global.mdV[VY], (F64)REGION_WIDTH_METERS ); // simNameFromPosGlobal can fail, so don't give the user an invalid SLURL if ( gotSimName ) { - mSLURL = LLSLURL::buildSLURL(sim_name, llround(region_x), llround(region_y), llround((F32)pos_global.mdV[VZ])); + mSLURL = LLSLURL(sim_name, pos_global); } else { // Empty SLURL will disable the "Copy SLURL to clipboard" button - mSLURL = ""; + mSLURL = LLSLURL(); } } } @@ -1167,7 +1158,7 @@ void LLFloaterWorldMap::onClearBtn() mTrackedStatus = LLTracker::TRACKING_NOTHING; LLTracker::stopTracking((void *)(intptr_t)TRUE); LLWorldMap::getInstance()->cancelTracking(); - mSLURL = ""; // Clear the SLURL since it's invalid + mSLURL = LLSLURL(); // Clear the SLURL since it's invalid mSetToUserPosition = TRUE; // Revert back to the current user position } @@ -1190,10 +1181,10 @@ void LLFloaterWorldMap::onClickTeleportBtn() void LLFloaterWorldMap::onCopySLURL() { - getWindow()->copyTextToClipboard(utf8str_to_wstring(mSLURL)); + getWindow()->copyTextToClipboard(utf8str_to_wstring(mSLURL.getSLURLString())); LLSD args; - args["SLURL"] = mSLURL; + args["SLURL"] = mSLURL.getSLURLString(); LLNotificationsUtil::add("CopySLURL", args); } diff --git a/indra/newview/llfloaterworldmap.h b/indra/newview/llfloaterworldmap.h index 00f5e788fb..52809ff830 100644 --- a/indra/newview/llfloaterworldmap.h +++ b/indra/newview/llfloaterworldmap.h @@ -43,6 +43,7 @@ #include "llhudtext.h" #include "llmapimagetype.h" #include "lltracker.h" +#include "llslurl.h" class LLEventInfo; class LLFriendObserver; @@ -183,7 +184,7 @@ private: LLTracker::ETrackingStatus mTrackedStatus; std::string mTrackedSimName; std::string mTrackedAvatarName; - std::string mSLURL; + LLSLURL mSLURL; }; extern LLFloaterWorldMap* gFloaterWorldMap; diff --git a/indra/newview/llinspectobject.cpp b/indra/newview/llinspectobject.cpp index dd313c528d..e4ee953eae 100644 --- a/indra/newview/llinspectobject.cpp +++ b/indra/newview/llinspectobject.cpp @@ -479,7 +479,7 @@ void LLInspectObject::updateCreator(LLSelectNode* nodep) // Objects cannot be created by a group, so use agent URL format LLUUID creator_id = nodep->mPermissions->getCreator(); std::string creator_url = - LLSLURL::buildCommand("agent", creator_id, "about"); + LLSLURL("agent", creator_id, "about").getSLURLString(); args["[CREATOR]"] = creator_url; // created by one user but owned by another @@ -489,12 +489,12 @@ void LLInspectObject::updateCreator(LLSelectNode* nodep) if (group_owned) { owner_id = nodep->mPermissions->getGroup(); - owner_url = LLSLURL::buildCommand("group", owner_id, "about"); + owner_url = LLSLURL("group", owner_id, "about").getSLURLString(); } else { owner_id = nodep->mPermissions->getOwner(); - owner_url = LLSLURL::buildCommand("agent", owner_id, "about"); + owner_url = LLSLURL("agent", owner_id, "about").getSLURLString(); } args["[OWNER]"] = owner_url; diff --git a/indra/newview/llinspectremoteobject.cpp b/indra/newview/llinspectremoteobject.cpp index e4d2eec242..7319bd0331 100644 --- a/indra/newview/llinspectremoteobject.cpp +++ b/indra/newview/llinspectremoteobject.cpp @@ -176,11 +176,11 @@ void LLInspectRemoteObject::update() { if (mGroupOwned) { - owner = LLSLURL::buildCommand("group", mOwnerID, "about"); + owner = LLSLURL("group", mOwnerID, "about").getSLURLString(); } else { - owner = LLSLURL::buildCommand("agent", mOwnerID, "about"); + owner = LLSLURL("agent", mOwnerID, "about").getSLURLString(); } } getChild("object_owner")->setValue(owner); diff --git a/indra/newview/lllandmarkactions.cpp b/indra/newview/lllandmarkactions.cpp index f25d2ef574..eaa3a715cb 100644 --- a/indra/newview/lllandmarkactions.cpp +++ b/indra/newview/lllandmarkactions.cpp @@ -298,7 +298,7 @@ void LLLandmarkActions::getSLURLfromPosGlobal(const LLVector3d& global_pos, slur bool gotSimName = LLWorldMap::getInstance()->simNameFromPosGlobal(global_pos, sim_name); if (gotSimName) { - std::string slurl = LLSLURL::buildSLURLfromPosGlobal(sim_name, global_pos, escaped); + std::string slurl = LLSLURL(sim_name, global_pos).getSLURLString(); cb(slurl); return; @@ -350,7 +350,7 @@ void LLLandmarkActions::onRegionResponseSLURL(slurl_callback_t cb, bool gotSimName = LLWorldMap::getInstance()->simNameFromPosGlobal(global_pos, sim_name); if (gotSimName) { - slurl = LLSLURL::buildSLURLfromPosGlobal(sim_name, global_pos, escaped); + slurl = LLSLURL(sim_name, global_pos).getSLURLString(); } else { diff --git a/indra/newview/lllocationinputctrl.cpp b/indra/newview/lllocationinputctrl.cpp index 404e266806..75b7f9313b 100644 --- a/indra/newview/lllocationinputctrl.cpp +++ b/indra/newview/lllocationinputctrl.cpp @@ -572,8 +572,7 @@ void LLLocationInputCtrl::onLocationPrearrange(const LLSD& data) value["global_pos"] = result->mGlobalPos.getValue(); std::string region_name = result->mTitle.substr(0, result->mTitle.find(',')); //TODO*: add Surl to teleportitem or parse region name from title - value["tooltip"] = LLSLURL::buildSLURLfromPosGlobal(region_name, - result->mGlobalPos, false); + value["tooltip"] = LLSLURL(region_name, result->mGlobalPos).getSLURLString(); add(result->getTitle(), value); } result = std::find_if(result + 1, th_items.end(), boost::bind( @@ -832,7 +831,7 @@ void LLLocationInputCtrl::changeLocationPresentation() if(mTextEntry && !mTextEntry->hasSelection() && text == mHumanReadableLocation ) { //needs unescaped one - mTextEntry->setText(LLAgentUI::buildSLURL(false)); + mTextEntry->setText(LLAgentUI::buildSLURL(false).getSLURLString()); mTextEntry->selectAll(); } } diff --git a/indra/newview/llloginhandler.cpp b/indra/newview/llloginhandler.cpp index 7d43f6a8cc..482660459e 100644 --- a/indra/newview/llloginhandler.cpp +++ b/indra/newview/llloginhandler.cpp @@ -38,7 +38,7 @@ #include "llsecapi.h" #include "llpanellogin.h" // save_password_to_disk() #include "llstartup.h" // getStartupState() -#include "llurlsimstring.h" +#include "llslurl.h" #include "llviewercontrol.h" // gSavedSettings #include "llviewernetwork.h" // EGridInfo #include "llviewerwindow.h" // getWindow() @@ -69,7 +69,7 @@ void LLLoginHandler::parse(const LLSD& queryMap) if (queryMap.has("grid")) { - LLGridManager::getInstance()->setGridChoice(queryMap["grid"].asString()); + LLGridManager::getInstance()->setGridChoice(queryMap["grid"].asString()); } @@ -77,11 +77,16 @@ void LLLoginHandler::parse(const LLSD& queryMap) if (startLocation == "specify") { - LLURLSimString::setString(queryMap["region"].asString()); + LLStartUp::setStartSLURL(LLSLURL(LLGridManager::getInstance()->getGridID(), + queryMap["region"].asString())); } - else if (!startLocation.empty()) + else if (startLocation == "home") { - LLURLSimString::setString(startLocation); + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_HOME)); + } + else if (startLocation == "last") + { + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_LAST)); } } @@ -153,12 +158,9 @@ bool LLLoginHandler::handle(const LLSD& tokens, // 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) +LLPointer LLLoginHandler::initializeLoginInfo() { - 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()) diff --git a/indra/newview/llloginhandler.h b/indra/newview/llloginhandler.h index ec2459c835..c15b998c91 100644 --- a/indra/newview/llloginhandler.h +++ b/indra/newview/llloginhandler.h @@ -51,7 +51,7 @@ class LLLoginHandler : public LLCommandHandler //LLUUID getWebLoginKey() const { return mWebLoginKey; } LLPointer loadSavedUserLoginInfo(); - LLPointer initializeLoginInfo(const std::string& url); + LLPointer initializeLoginInfo(); private: void parse(const LLSD& queryMap); diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp index e4b8becdd7..04e5cef62e 100644 --- a/indra/newview/lllogininstance.cpp +++ b/indra/newview/lllogininstance.cpp @@ -48,7 +48,8 @@ // newview #include "llviewernetwork.h" #include "llviewercontrol.h" -#include "llurlsimstring.h" +#include "llslurl.h" +#include "llstartup.h" #include "llfloaterreg.h" #include "llnotifications.h" #include "llwindow.h" @@ -56,6 +57,7 @@ #include "lltrans.h" #endif #include "llsecapi.h" +#include "llstartup.h" static const char * const TOS_REPLY_PUMP = "lllogininstance_tos_callback"; static const char * const TOS_LISTENER_NAME = "lllogininstance_tos"; @@ -462,15 +464,17 @@ bool LLLoginInstance::updateDialogCallback(const LLSD& notification, const LLSD& std::string construct_start_string() { std::string start; - if (LLURLSimString::parse()) + LLSLURL start_slurl = LLStartUp::getStartSLURL(); + if (start_slurl.getType() == LLSLURL::LOCATION) { // a startup URL was specified + LLVector3 position = start_slurl.getPosition(); std::string unescaped_start = STRINGIZE( "uri:" - << LLURLSimString::sInstance.mSimName << "&" - << LLURLSimString::sInstance.mX << "&" - << LLURLSimString::sInstance.mY << "&" - << LLURLSimString::sInstance.mZ); + << start_slurl.getRegion() << "&" + << position[VX] << "&" + << position[VY] << "&" + << position[VZ]); start = xml_escape_string(unescaped_start); } else diff --git a/indra/newview/llnavigationbar.cpp b/indra/newview/llnavigationbar.cpp index 71dc0f9011..8c21fb6314 100644 --- a/indra/newview/llnavigationbar.cpp +++ b/indra/newview/llnavigationbar.cpp @@ -50,7 +50,6 @@ #include "llsearchcombobox.h" #include "llsidetray.h" #include "llslurl.h" -#include "llurlsimstring.h" #include "llurlregistry.h" #include "llurldispatcher.h" #include "llviewerinventory.h" @@ -421,16 +420,15 @@ void LLNavigationBar::onLocationSelection() std::string region_name; LLVector3 local_coords(128, 128, 0); - S32 x = 0, y = 0, z = 0; // Is the typed location a SLURL? - if (LLSLURL::isSLURL(typed_location)) + LLSLURL slurl = LLSLURL(typed_location); + if (slurl.getType() == LLSLURL::LOCATION) { // Yes. Extract region name and local coordinates from it. - if (LLURLSimString::parse(LLSLURL::stripProtocol(typed_location), ®ion_name, &x, &y, &z)) - local_coords.set(x, y, z); - else - return; - }else + region_name = slurl.getRegion(); + local_coords = slurl.getPosition(); + } + else { // assume that an user has typed the {region name} or possible {region_name, parcel} region_name = typed_location.substr(0,typed_location.find(',')); @@ -465,7 +463,7 @@ void LLNavigationBar::onTeleportFinished(const LLVector3d& global_agent_pos) */ LLAgentUI::buildLocationString(location, LLAgentUI::LOCATION_FORMAT_NO_MATURITY, gAgent.getPosAgentFromGlobal(global_agent_pos)); - std::string tooltip (LLSLURL::buildSLURLfromPosGlobal(gAgent.getRegion()->getName(), global_agent_pos, false)); + std::string tooltip (LLSLURL(gAgent.getRegion()->getName(), global_agent_pos).getSLURLString()); LLLocationHistoryItem item (location, global_agent_pos, tooltip,TYPED_REGION_SURL);// we can add into history only TYPED location @@ -567,7 +565,7 @@ void LLNavigationBar::onRegionNameResponse( LLVector3d region_pos = from_region_handle(region_handle); LLVector3d global_pos = region_pos + (LLVector3d) local_coords; - llinfos << "Teleporting to: " << LLSLURL::buildSLURLfromPosGlobal(region_name, global_pos, false) << llendl; + llinfos << "Teleporting to: " << LLSLURL(region_name, global_pos).getSLURLString() << llendl; gAgent.teleportViaLocation(global_pos); } diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 6978d05389..7752750a31 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -56,7 +56,7 @@ #include "lltextbox.h" #include "llui.h" #include "lluiconstants.h" -#include "llurlsimstring.h" +#include "llslurl.h" #include "llversioninfo.h" #include "llviewerhelp.h" #include "llviewertexturelist.h" @@ -106,7 +106,6 @@ public: LLLoginRefreshHandler gLoginRefreshHandler; - // helper class that trys to download a URL from a web site and calls a method // on parent class indicating if the web server is working or not class LLIamHereLogin : public LLHTTPClient::Responder @@ -155,10 +154,6 @@ namespace { boost::intrusive_ptr< LLIamHereLogin > gResponsePtr = 0; }; -void set_start_location(LLUICtrl* ctrl, void* data) -{ - LLURLSimString::setString(ctrl->getValue().asString()); -} //--------------------------------------------------------------------------- // Public methods @@ -228,12 +223,11 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, LLComboBox* combo = getChild("start_location_combo"); - std::string sim_string = LLURLSimString::sInstance.mSimString; - if(sim_string.empty()) + if(!LLStartUp::getStartSLURL().isValid()) { - LLURLSimString::setString(gSavedSettings.getString("LoginLocation")); + LLStartUp::setStartSLURL(LLSLURL(gSavedSettings.getString("LoginLocation"))); } - + std::string sim_string = LLStartUp::getStartSLURL().getRegion(); if (!sim_string.empty()) { // Replace "" with this region name @@ -242,8 +236,8 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, combo->setTextEntry(sim_string); combo->setCurrentByIndex( 2 ); } - - combo->setCommitCallback( &set_start_location, NULL ); + + combo->setCommitCallback(onSelectLocation, NULL); LLComboBox* server_choice_combo = sInstance->getChild("server_combo"); server_choice_combo->setCommitCallback(onSelectServer, NULL); @@ -306,11 +300,8 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, gResponsePtr = LLIamHereLogin::build( this ); LLHTTPClient::head( LLGridManager::getInstance()->getLoginPage(), gResponsePtr ); - -#if !USE_VIEWER_AUTH - // Initialize visibility (and don't force visibility - use prefs) - refreshLocation( false ); -#endif + + updateLocationCombo(false); } @@ -674,46 +665,141 @@ BOOL LLPanelLogin::isGridComboDirty() } // static -void LLPanelLogin::getLocation(std::string &location) +BOOL LLPanelLogin::areCredentialFieldsDirty() { if (!sInstance) { - llwarns << "Attempted getLocation with no login view shown" << llendl; + llwarns << "Attempted getServer with no login view shown" << llendl; + } + else + { + std::string username = sInstance->childGetText("username_edit"); + LLStringUtil::trim(username); + std::string password = sInstance->childGetText("password_edit"); + LLLineEditor* ctrl = sInstance->getChild("username_edit"); + if(ctrl && ctrl->isDirty()) + { + return true; + } + ctrl = sInstance->getChild("password_edit"); + if(ctrl && ctrl->isDirty()) + { + return true; + } + } + return false; +} + + +// static +void LLPanelLogin::updateLocationCombo( bool force_visible ) +{ + if (!sInstance) + { return; + } + LLComboBox* combo = sInstance->getChild("start_location_combo"); + + switch(LLStartUp::getStartSLURL().getType()) + { + case LLSLURL::LOCATION: + { + + combo->setCurrentByIndex( 2 ); + combo->setTextEntry(LLStartUp::getStartSLURL().getLocationString()); + break; + } + case LLSLURL::HOME_LOCATION: + combo->setCurrentByIndex(0); + break; + default: + combo->setCurrentByIndex(1); + break; } - LLComboBox* combo = sInstance->getChild("start_location_combo"); - location = combo->getValue().asString(); + BOOL show_start = TRUE; + + if ( ! force_visible ) + show_start = gSavedSettings.getBOOL("ShowStartLocation"); + + sInstance->childSetVisible("start_location_combo", show_start); + sInstance->childSetVisible("start_location_text", show_start); + + sInstance->childSetVisible("server_combo", TRUE); } // static -void LLPanelLogin::refreshLocation( bool force_visible ) +void LLPanelLogin::onSelectLocation(LLUICtrl*, void*) { if (!sInstance) return; - -#if USE_VIEWER_AUTH - loadLoginPage(); -#else - BOOL show_start = TRUE; - - if ( ! force_visible ) + + LLComboBox* combo = sInstance->getChild("start_location_combo"); + S32 index = combo->getCurrentIndex(); + + switch (index) { - // Don't show on first run after install - // Otherwise ShowStartLocation defaults to true. - show_start = gSavedSettings.getBOOL("ShowStartLocation") - && !gSavedSettings.getBOOL("FirstRunThisInstall"); + case 2: + { + LLSLURL slurl = LLSLURL(combo->getSelectedValue()); + if((slurl.getType() == LLSLURL::LOCATION) && + (slurl.getGrid() != LLStartUp::getStartSLURL().getGrid())) + { + LLStartUp::setStartSLURL(slurl); + // we've changed the grid, so update the grid selection + try + { + LLGridManager::getInstance()->setGridChoice(slurl.getGrid()); + } + catch (LLInvalidGridName ex) + { + LLSD args; + args["GRID"] = slurl.getGrid(); + LLNotificationsUtil::add("InvalidGrid", args); + return; + } + loadLoginPage(); + } + break; + } + case 1: + { + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_LAST)); + break; + } + default: + { + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_HOME)); + break; + } } +} - 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); -#endif +// static +LLSLURL LLPanelLogin::getLocation() +{ + LLSLURL result; + if (!sInstance) + { + llwarns << "Attempted getLocation with no login view shown" << llendl; + return result; + } + + LLComboBox* combo = sInstance->getChild("start_location_combo"); + + switch(combo->getCurrentIndex()) + { + case 0: + return LLSLURL(LLSLURL::SIM_LOCATION_HOME); + case 1: + return LLSLURL(LLSLURL::SIM_LOCATION_LAST); + default: + // construct a real slurl + return LLSLURL(LLURI::escape(combo->getValue().asString())); + } } + // static void LLPanelLogin::closePanel() { @@ -748,6 +834,7 @@ void LLPanelLogin::loadLoginPage() std::ostringstream oStr; std::string login_page = LLGridManager::getInstance()->getLoginPage(); + oStr << login_page; // Use the right delimeter depending on how LLURI parses the URL @@ -859,7 +946,7 @@ void LLPanelLogin::loadLoginPage() #endif LLMediaCtrl* web_browser = sInstance->getChild("login_html"); - + // navigate to the "real" page if (gSavedSettings.getBOOL("RegInClient")) { @@ -1006,30 +1093,59 @@ void LLPanelLogin::onPassKey(LLLineEditor* caller, void* user_data) } } + +void LLPanelLogin::updateServer(std::string grid) +{ + try + { + LLGridManager::getInstance()->setGridChoice(grid); + + updateServerCombo(); + // if they've selected another grid, we should load the credentials + // for that grid and set them to the UI. + if(sInstance && !sInstance->areCredentialFieldsDirty()) + { + LLPointer credential = gSecAPIHandler->loadCredential(grid); + bool remember = sInstance->childGetValue("remember_check"); + sInstance->setFields(credential, remember); + } + // grid changed so show new splash screen (possibly) + loadLoginPage(); + updateLocationCombo(LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION); + } + catch (LLInvalidGridName ex) + { + // do nothing + }} + void LLPanelLogin::updateServerCombo() { + if (!sInstance) + { + return; + } // 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(); +#ifdef LL_RELEASE_FOR_DOWNLOAD + std::map known_grids = LLGridManager::getInstance()->getKnownGrids(TRUE); +#else + std::map known_grids = LLGridManager::getInstance()->getKnownGrids(FALSE); +#endif for (std::map::iterator grid_choice = known_grids.begin(); grid_choice != known_grids.end(); grid_choice++) + { + if (!grid_choice->first.empty()) { - //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->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->add(LLGridManager::getInstance()->getGridLabel(), + LLGridManager::getInstance()->getGridName(), ADD_TOP); server_choice_combo->selectFirstItem(); } @@ -1040,37 +1156,26 @@ void LLPanelLogin::onSelectServer(LLUICtrl*, void*) // *NOTE: The paramters for this method are ignored. // LLPanelLogin::onServerComboLostFocus(LLFocusableElement* fe, void*) // calls this method. - + LL_INFOS("AppInit") << "onSelectServer" << LL_ENDL; // The user twiddled with the grid choice ui. // apply the selection to the grid setting. LLPointer credential; - BOOL remember = FALSE; - + LLComboBox* combo = sInstance->getChild("server_combo"); LLSD combo_val = combo->getSelectedValue(); if (combo_val.isUndefined()) { - combo_val = combo->getValue(); + combo_val = combo->getValue(); } - - // This new selection will override preset uris - // from the command line. - - 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()); + combo = sInstance->getChild("start_location_combo"); + combo->setCurrentByIndex(1); + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_LAST)); - - remember = sInstance->childGetValue("remember_check"); - sInstance->setFields(credential, remember); - - LL_INFOS("Credentials") << "Grid changed to:" << LLGridManager::getInstance()->getGridName() << LL_ENDL; + // This new seelction will override preset uris + // from the command line. + updateServer(combo_val.asString()); + updateLoginPanelLinks(); } void LLPanelLogin::onServerComboLostFocus(LLFocusableElement* fe) @@ -1083,3 +1188,14 @@ void LLPanelLogin::onServerComboLostFocus(LLFocusableElement* fe) onSelectServer(combo, NULL); } } + +void LLPanelLogin::updateLoginPanelLinks() +{ + LLSD grid_data = LLGridManager::getInstance()->getGridInfo(); + bool system_grid = grid_data.has(GRID_IS_SYSTEM_GRID_VALUE); + + // need to call through sInstance, as it's called from onSelectServer, which + // is static. + sInstance->childSetVisible("create_new_account_text", system_grid); + sInstance->childSetVisible("forgot_password_text", system_grid); +} diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h index d33aa2d550..4f18b7bfd8 100644 --- a/indra/newview/llpanellogin.h +++ b/indra/newview/llpanellogin.h @@ -38,6 +38,7 @@ #include "llmediactrl.h" // LLMediaCtrlObserver #include #include "llsecapi.h" +#include "llslurl.h" class LLLineEditor; class LLUIImage; @@ -68,13 +69,13 @@ public: static void setFields(LLPointer credential, BOOL remember); - static void refreshLocation( bool force_visible ); - static void getFields(LLPointer& credential, BOOL& remember); static BOOL isGridComboDirty(); - static void getLocation(std::string &location); - + static BOOL areCredentialFieldsDirty(); + static LLSLURL getLocation(); + + static void updateLocationCombo(bool force_visible); // simply update the combo box static void closePanel(); void setSiteIsAlive( bool alive ); @@ -99,7 +100,11 @@ private: static void onSelectServer(LLUICtrl*, void*); static void onServerComboLostFocus(LLFocusableElement*); static void updateServerCombo(); + static void onSelectLocation(LLUICtrl*, void*); + static void updateServer(std::string grid); // update the combo box, change the login page to the new server, clear the combo + static void updateLoginPanelLinks(); + private: LLPointer mLogoImage; boost::scoped_ptr mListener; diff --git a/indra/newview/llpanelplacestab.cpp b/indra/newview/llpanelplacestab.cpp index 9806b8c64d..6b12796e59 100644 --- a/indra/newview/llpanelplacestab.cpp +++ b/indra/newview/llpanelplacestab.cpp @@ -70,10 +70,7 @@ void LLPanelPlacesTab::onRegionResponse(const LLVector3d& landmark_global_pos, std::string sl_url; if ( gotSimName ) { - F32 region_x = (F32)fmod( landmark_global_pos.mdV[VX], (F64)REGION_WIDTH_METERS ); - F32 region_y = (F32)fmod( landmark_global_pos.mdV[VY], (F64)REGION_WIDTH_METERS ); - - sl_url = LLSLURL::buildSLURL(sim_name, llround(region_x), llround(region_y), llround((F32)landmark_global_pos.mdV[VZ])); + sl_url = LLSLURL(sim_name, landmark_global_pos).getSLURLString(); } else { diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 60a095506b..36ed1c466b 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -2434,7 +2434,7 @@ BOOL LLSelectMgr::selectGetCreator(LLUUID& result_id, std::string& name) if (identical) { - name = LLSLURL::buildCommand("agent", first_id, "inspect"); + name = LLSLURL("agent", first_id, "inspect").getSLURLString(); } else { @@ -2493,11 +2493,11 @@ BOOL LLSelectMgr::selectGetOwner(LLUUID& result_id, std::string& name) BOOL public_owner = (first_id.isNull() && !first_group_owned); if (first_group_owned) { - name = LLSLURL::buildCommand("group", first_id, "inspect"); + name = LLSLURL("group", first_id, "inspect").getSLURLString(); } else if(!public_owner) { - name = LLSLURL::buildCommand("agent", first_id, "inspect"); + name = LLSLURL("agent", first_id, "inspect").getSLURLString(); } else { @@ -2557,7 +2557,7 @@ BOOL LLSelectMgr::selectGetLastOwner(LLUUID& result_id, std::string& name) BOOL public_owner = (first_id.isNull()); if(!public_owner) { - name = LLSLURL::buildCommand("agent", first_id, "inspect"); + name = LLSLURL("agent", first_id, "inspect").getSLURLString(); } else { diff --git a/indra/newview/llslurl.cpp b/indra/newview/llslurl.cpp index 37e268ad34..43c505fae6 100644 --- a/indra/newview/llslurl.cpp +++ b/indra/newview/llslurl.cpp @@ -1,10 +1,11 @@ /** - * @file llslurl.cpp - * @brief SLURL manipulation + * @file llurlsimstring.cpp (was llsimurlstring.cpp) + * @brief Handles "SLURL fragments" like Ahern/123/45 for + * startup processing, login screen, prefs, etc. * - * $LicenseInfo:firstyear=2009&license=viewergpl$ + * $LicenseInfo:firstyear=2006&license=viewergpl$ * - * Copyright (c) 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 @@ -12,13 +13,12 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * online at http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -34,120 +34,422 @@ #include "llslurl.h" -#include "llweb.h" +#include "llpanellogin.h" +#include "llviewercontrol.h" +#include "llviewernetwork.h" +#include "llfiltersd2xmlrpc.h" +#include "curl/curl.h" +const char* LLSLURL::SLURL_HTTP_SCHEME = "http"; +const char* LLSLURL::SLURL_HTTPS_SCHEME = "https"; +const char* LLSLURL::SLURL_SECONDLIFE_SCHEME = "secondlife"; +const char* LLSLURL::SLURL_SECONDLIFE_PATH = "secondlife"; +const char* LLSLURL::SLURL_COM = "slurl.com"; +const char* LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME = "x-grid-location-info"; +const char* LLSLURL::SLURL_APP_PATH = "app"; +const char* LLSLURL::SLURL_REGION_PATH = "region"; +const char* LLSLURL::SIM_LOCATION_HOME = "MyHome"; +const char* LLSLURL::SIM_LOCATION_LAST = "MyLastLocation"; -const std::string LLSLURL::PREFIX_SL_HELP = "secondlife://app."; -const std::string LLSLURL::PREFIX_SL = "sl://"; -const std::string LLSLURL::PREFIX_SECONDLIFE = "secondlife://"; -const std::string LLSLURL::PREFIX_SLURL = "http://slurl.com/secondlife/"; - -const std::string LLSLURL::APP_TOKEN = "app/"; - -// static -std::string LLSLURL::stripProtocol(const std::string& url) +// resolve a simstring from a slurl +LLSLURL::LLSLURL(const std::string& slurl) { - std::string stripped = url; - if (matchPrefix(stripped, PREFIX_SL_HELP)) - { - stripped.erase(0, PREFIX_SL_HELP.length()); - } - else if (matchPrefix(stripped, PREFIX_SL)) + // by default we go to agni. + mType = INVALID; + LL_INFOS("AppInit") << "SLURL: " << slurl << LL_ENDL; + if(slurl == SIM_LOCATION_HOME) { - stripped.erase(0, PREFIX_SL.length()); + mType = HOME_LOCATION; } - else if (matchPrefix(stripped, PREFIX_SECONDLIFE)) + else if(slurl.empty() || (slurl == SIM_LOCATION_LAST)) { - stripped.erase(0, PREFIX_SECONDLIFE.length()); + mType = LAST_LOCATION; } - else if (matchPrefix(stripped, PREFIX_SLURL)) + else { - stripped.erase(0, PREFIX_SLURL.length()); + LLURI slurl_uri; + // parse the slurl as a uri + if(slurl.find(':') == std::string::npos) + { + // There may be no scheme ('secondlife:' etc.) passed in. In that case + // we want to normalize the slurl by putting the appropriate scheme + // in front of the slurl. So, we grab the appropriate slurl base + // from the grid manager which may be http://slurl.com/secondlife/ for maingrid, or + // https:///region/ for nebraska grid (the word region, not the region name) + // these slurls are typically passed in from the 'starting location' box on the login panel, + // where the user can type in /// + + std::string fixed_slurl = LLGridManager::getInstance()->getSLURLBase(); + // the slurl that was passed in might have a prepended /, or not. So, + // we strip off the prepended '/' so we don't end up with http://slurl.com/secondlife//// + // or some such. + if(slurl[0] == '/') + { + fixed_slurl += slurl.substr(1); + } + else + { + fixed_slurl += slurl; + } + // We then load the slurl into a LLURI form + slurl_uri = LLURI(fixed_slurl); + } + else + { + // as we did have a scheme, implying a URI style slurl, we + // simply parse it as a URI + slurl_uri = LLURI(slurl); + } + + LLSD path_array = slurl_uri.pathArray(); + + // determine whether it's a maingrid URI or an nebraska/open style URI + // by looking at the scheme. If it's a 'secondlife:' slurl scheme or + // 'sl:' scheme, we know it's maingrid + + // At the end of this if/else block, we'll have determined the grid, + // and the slurl type (APP or LOCATION) + if(slurl_uri.scheme() == LLSLURL::SLURL_SECONDLIFE_SCHEME) + { + // parse a maingrid style slurl. We know the grid is maingrid + // so grab it. + // A location slurl for maingrid (with the special schemes) can be in the form + // secondlife:///// + // or + // secondlife:///secondlife//// + // where if grid is empty, it specifies Agni + + // An app style slurl for maingrid can be + // secondlife:///app/ + // where an empty grid implies Agni + + // we'll start by checking the top of the 'path' which will be + // either 'app', 'secondlife', or . + + // default to maingrid + + mGrid = MAINGRID; + + if ((path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH) || + (path_array[0].asString() == LLSLURL::SLURL_APP_PATH)) + { + // it's in the form secondlife:///(app|secondlife) + // so parse the grid name to derive the grid ID + if (!slurl_uri.hostName().empty()) + { + mGrid = LLGridManager::getInstance()->getGridByLabel(slurl_uri.hostName()); + } + else if(path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH) + { + // If the slurl is in the form secondlife:///secondlife/ form, + // then we are in fact on maingrid. + mGrid = MAINGRID; + } + else if(path_array[0].asString() == LLSLURL::SLURL_APP_PATH) + { + // for app style slurls, where no grid name is specified, assume the currently + // selected or logged in grid. + mGrid = LLGridManager::getInstance()->getGridName(); + } + + if(mGrid.empty()) + { + // we couldn't find the grid in the grid manager, so bail + return; + } + // set the type as appropriate. + if(path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH) + { + mType = LOCATION; + } + else + { + mType = APP; + } + path_array.erase(0); + } + else + { + // it wasn't a /secondlife/ or /app/, so it must be secondlife:// + // therefore the hostname will be the region name, and it's a location type + mType = LOCATION; + // 'normalize' it so the region name is in fact the head of the path_array + path_array.insert(0, slurl_uri.hostName()); + } + } + else if((slurl_uri.scheme() == LLSLURL::SLURL_HTTP_SCHEME) || + (slurl_uri.scheme() == LLSLURL::SLURL_HTTPS_SCHEME) || + (slurl_uri.scheme() == LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME)) + { + // We're dealing with either a nebraska style slurl or slurl.com slurl + if (slurl_uri.hostName() == LLSLURL::SLURL_COM) + { + // slurl.com implies maingrid + mGrid = MAINGRID; + } + else + { + // As it's a nebraska grid/open, we will always have a hostname, as nebraska/open style + // urls are properly formed, unlike the stinky maingrid style + mGrid = slurl_uri.hostName(); + } + if (path_array.size() == 0) + { + // um, we need a path... + return; + } + + // we need to normalize the urls so + // the path portion starts with the 'command' that we want to do + // it can either be region or app. + if ((path_array[0].asString() == LLSLURL::SLURL_REGION_PATH) || + (path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH)) + { + // strip off 'region' or 'secondlife' + path_array.erase(0); + // it's a location + mType = LOCATION; + } + else if (path_array[0].asString() == LLSLURL::SLURL_APP_PATH) + { + mType = APP; + path_array.erase(0); + // leave app appended. + } + else + { + // not a valid https/http/x-grid-location-info slurl, so it'll likely just be a URL + return; + } + } + else + { + // invalid scheme, so bail + return; + } + + + if(path_array.size() == 0) + { + // we gotta have some stuff after the specifier as to whether it's a region or command + return; + } + + // now that we know whether it's an app slurl or a location slurl, + // parse the slurl into the proper data structures. + if(mType == APP) + { + // grab the app command type and strip it (could be a command to jump somewhere, + // or whatever ) + mAppCmd = path_array[0].asString(); + path_array.erase(0); + + // Grab the parameters + mAppPath = path_array; + // and the query + mAppQuery = slurl_uri.query(); + mAppQueryMap = slurl_uri.queryMap(); + return; + } + else if(mType == LOCATION) + { + // at this point, head of the path array should be [ , , , ] where x, y and z + // are collectively optional + // are optional + mRegion = LLURI::unescape(path_array[0].asString()); + path_array.erase(0); + + // parse the x, y, z + if(path_array.size() >= 3) + { + mPosition = LLVector3(path_array); + } + else + { + // if x, y and z were not fully passed in, go to the middle of the region. + // teleport will adjust the actual location to make sure you're on the ground + // and such + mPosition = LLVector3(REGION_WIDTH_METERS/2, REGION_WIDTH_METERS/2, 0); + } + } } - - return stripped; } -// static -bool LLSLURL::isSLURL(const std::string& url) + +// Create a slurl for the middle of the region +LLSLURL::LLSLURL(const std::string& grid, + const std::string& region) { - if (matchPrefix(url, PREFIX_SL_HELP)) return true; - if (matchPrefix(url, PREFIX_SL)) return true; - if (matchPrefix(url, PREFIX_SECONDLIFE)) return true; - if (matchPrefix(url, PREFIX_SLURL)) return true; - - return false; + mGrid = grid; + mRegion = region; + mType = LOCATION; + mPosition = LLVector3((F64)REGION_WIDTH_METERS/2, (F64)REGION_WIDTH_METERS/2, 0); +} + + + +// create a slurl given the position. The position will be modded with the region +// width handling global positions as well +LLSLURL::LLSLURL(const std::string& grid, + const std::string& region, + const LLVector3& position) +{ + mGrid = grid; + mRegion = region; + S32 x = llround( (F32)fmod( position[VX], (F32)REGION_WIDTH_METERS ) ); + S32 y = llround( (F32)fmod( position[VY], (F32)REGION_WIDTH_METERS ) ); + S32 z = llround( (F32)position[VZ] ); + mType = LOCATION; + mPosition = LLVector3(x, y, z); } -// static -bool LLSLURL::isSLURLCommand(const std::string& url) -{ - if (matchPrefix(url, PREFIX_SL + APP_TOKEN) || - matchPrefix(url, PREFIX_SECONDLIFE + "/" + APP_TOKEN) || - matchPrefix(url, PREFIX_SLURL + APP_TOKEN) ) - { - return true; - } - return false; +// create a simstring +LLSLURL::LLSLURL(const std::string& region, + const LLVector3& position) +{ + *this = LLSLURL(LLGridManager::getInstance()->getGridName(), + region, position); } -// static -bool LLSLURL::isSLURLHelp(const std::string& url) +// create a slurl from a global position +LLSLURL::LLSLURL(const std::string& grid, + const std::string& region, + const LLVector3d& global_position) { - return matchPrefix(url, PREFIX_SL_HELP); + *this = LLSLURL(grid, + region, LLVector3(global_position.mdV[VX], + global_position.mdV[VY], + global_position.mdV[VZ])); } -// static -std::string LLSLURL::buildSLURL(const std::string& regionname, S32 x, S32 y, S32 z) +// create a slurl from a global position +LLSLURL::LLSLURL(const std::string& region, + const LLVector3d& global_position) { - std::string slurl = PREFIX_SLURL + regionname + llformat("/%d/%d/%d",x,y,z); - slurl = LLWeb::escapeURL( slurl ); - return slurl; + *this = LLSLURL(LLGridManager::getInstance()->getGridName(), + region, global_position); } -// static -std::string LLSLURL::buildCommand(const char* noun, const LLUUID& id, const char* verb) +LLSLURL::LLSLURL(const std::string& command, const LLUUID&id, const std::string& verb) { - std::string slurl = llformat("secondlife:///app/%s/%s/%s", - noun, id.asString().c_str(), verb); - return slurl; + mType = APP; + mAppCmd = command; + mAppPath = LLSD::emptyArray(); + mAppPath.append(LLSD(id)); + mAppPath.append(LLSD(verb)); } -// static -std::string LLSLURL::buildUnescapedSLURL(const std::string& regionname, S32 x, S32 y, S32 z) + +std::string LLSLURL::getSLURLString() const { - std::string unescapedslurl = PREFIX_SLURL + regionname + llformat("/%d/%d/%d",x,y,z); - return unescapedslurl; + switch(mType) + { + case HOME_LOCATION: + return SIM_LOCATION_HOME; + case LAST_LOCATION: + return SIM_LOCATION_LAST; + case LOCATION: + { + // lookup the grid + S32 x = llround( (F32)mPosition[VX] ); + S32 y = llround( (F32)mPosition[VY] ); + S32 z = llround( (F32)mPosition[VZ] ); + return LLGridManager::getInstance()->getSLURLBase(mGrid) + + LLURI::escape(mRegion) + llformat("/%d/%d/%d",x,y,z); + } + case APP: + { + std::ostringstream app_url; + app_url << LLGridManager::getInstance()->getAppSLURLBase() << "/" << mAppCmd; + for(LLSD::array_const_iterator i = mAppPath.beginArray(); + i != mAppPath.endArray(); + i++) + { + app_url << "/" << i->asString(); + } + if(mAppQuery.length() > 0) + { + app_url << "?" << mAppQuery; + } + return app_url.str(); + } + default: + LL_WARNS("AppInit") << "Unexpected SLURL type for SLURL string" << (int)mType << LL_ENDL; + return std::string(); + } } -// static -std::string LLSLURL::buildSLURLfromPosGlobal(const std::string& regionname, - const LLVector3d& global_pos, - bool escaped /*= true*/) +std::string LLSLURL::getLoginString() const { - S32 x, y, z; - globalPosToXYZ(global_pos, x, y, z); - if(escaped) + + std::stringstream unescaped_start; + switch(mType) { - return buildSLURL(regionname, x, y, z); + case LOCATION: + unescaped_start << "uri:" + << mRegion << "&" + << llround(mPosition[0]) << "&" + << llround(mPosition[1]) << "&" + << llround(mPosition[2]); + break; + case HOME_LOCATION: + unescaped_start << "home"; + break; + case LAST_LOCATION: + unescaped_start << "last"; + break; + default: + LL_WARNS("AppInit") << "Unexpected SLURL type for login string" << (int)mType << LL_ENDL; + break; } - else + return xml_escape_string(unescaped_start.str()); +} + +bool LLSLURL::operator==(const LLSLURL& rhs) +{ + if(rhs.mType != mType) return false; + switch(mType) { - return buildUnescapedSLURL(regionname, x, y, z); + case LOCATION: + return ((mGrid == rhs.mGrid) && + (mRegion == rhs.mRegion) && + (mPosition == rhs.mPosition)); + case APP: + return getSLURLString() == rhs.getSLURLString(); + + case HOME_LOCATION: + case LAST_LOCATION: + return true; + default: + return false; } } -// static -bool LLSLURL::matchPrefix(const std::string& url, const std::string& prefix) +bool LLSLURL::operator !=(const LLSLURL& rhs) { - std::string test_prefix = url.substr(0, prefix.length()); - LLStringUtil::toLower(test_prefix); - return test_prefix == prefix; + return !(*this == rhs); } -void LLSLURL::globalPosToXYZ(const LLVector3d& pos, S32& x, S32& y, S32& z) +std::string LLSLURL::getLocationString() const { - x = llround((F32)fmod(pos.mdV[VX], (F64)REGION_WIDTH_METERS)); - y = llround((F32)fmod(pos.mdV[VY], (F64)REGION_WIDTH_METERS)); - z = llround((F32)pos.mdV[VZ]); + return llformat("%s/%d/%d/%d", + mRegion.c_str(), + (int)llround(mPosition[0]), + (int)llround(mPosition[1]), + (int)llround(mPosition[2])); } +std::string LLSLURL::asString() const +{ + std::ostringstream result; + result << " mAppCmd:" << getAppCmd() << + " mAppPath:" + getAppPath().asString() << + " mAppQueryMap:" + getAppQueryMap().asString() << + " mAppQuery: " + getAppQuery() << + " mGrid: " + getGrid() << + " mRegion: " + getRegion() << + " mPosition: " << + " mType: " << mType << + " mPosition: " << mPosition; + return result.str(); +} + diff --git a/indra/newview/llslurl.h b/indra/newview/llslurl.h index 05b0143e72..c12ace17fd 100644 --- a/indra/newview/llslurl.h +++ b/indra/newview/llslurl.h @@ -1,6 +1,7 @@ -/** +/** * @file llslurl.h - * @brief SLURL manipulation + * @brief Handles "SLURL fragments" like Ahern/123/45 for + * startup processing, login screen, prefs, etc. * * $LicenseInfo:firstyear=2009&license=viewergpl$ * @@ -12,13 +13,12 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * online at http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -29,78 +29,82 @@ * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ +#ifndef LLSLURL_H +#define LLSLURL_H -#ifndef LL_SLURL_H -#define LL_SLURL_H +#include "llstring.h" -#include -// IAN BUG: where should this live? -// IAN BUG: are static utility functions right? See LLUUID. -// question of whether to have a LLSLURL object or a -// some of this was moved from LLURLDispatcher +// represents a location in a grid -/** - * SLURL manipulation - */ class LLSLURL { public: - static const std::string PREFIX_SL_HELP; - static const std::string PREFIX_SL; - static const std::string PREFIX_SECONDLIFE; - static const std::string PREFIX_SLURL; - - static const std::string APP_TOKEN; - - /** - * Is this any sort of secondlife:// or sl:// URL? - */ - static bool isSLURL(const std::string& url); - - /** - * Is this a special secondlife://app/ URL? - */ - static bool isSLURLCommand(const std::string& url); - - /** - * Not sure what it is. - */ - static bool isSLURLHelp(const std::string& url); - - /** - * builds: http://slurl.com/secondlife/Region%20Name/x/y/z/ escaping result url. - */ - static std::string buildSLURL(const std::string& regionname, S32 x, S32 y, S32 z); - - /// Build a SLURL like secondlife:///app/agent//inspect - static std::string buildCommand(const char* noun, const LLUUID& id, const char* verb); - - /** - * builds: http://slurl.com/secondlife/Region Name/x/y/z/ without escaping result url. - */ - static std::string buildUnescapedSLURL(const std::string& regionname, S32 x, S32 y, S32 z); - - /** - * builds SLURL from global position. Returns escaped or unescaped url. - * Returns escaped url by default. - */ - static std::string buildSLURLfromPosGlobal(const std::string& regionname, - const LLVector3d& global_pos, - bool escaped = true); - /** - * Strip protocol part from the URL. - */ - static std::string stripProtocol(const std::string& url); - - /** - * Convert global position to X, Y Z - */ - static void globalPosToXYZ(const LLVector3d& pos, S32& x, S32& y, S32& z); - -private: - static bool matchPrefix(const std::string& url, const std::string& prefix); - + static const char* SLURL_HTTPS_SCHEME; + static const char* SLURL_HTTP_SCHEME; + static const char* SLURL_SL_SCHEME; + static const char* SLURL_SECONDLIFE_SCHEME; + static const char* SLURL_SECONDLIFE_PATH; + static const char* SLURL_COM; + static const char* SLURL_X_GRID_LOCATION_INFO_SCHEME; + static LLSLURL START_LOCATION; + static const char* SIM_LOCATION_HOME; + static const char* SIM_LOCATION_LAST; + static const char* SLURL_APP_PATH; + static const char* SLURL_REGION_PATH; + + enum SLURL_TYPE { + INVALID, + LOCATION, + HOME_LOCATION, + LAST_LOCATION, + APP, + HELP + }; + + + LLSLURL(): mType(LAST_LOCATION) { } + LLSLURL(const std::string& slurl); + LLSLURL(const std::string& grid, const std::string& region); + LLSLURL(const std::string& region, const LLVector3& position); + LLSLURL(const std::string& grid, const std::string& region, const LLVector3& position); + LLSLURL(const std::string& grid, const std::string& region, const LLVector3d& global_position); + LLSLURL(const std::string& region, const LLVector3d& global_position); + LLSLURL(const std::string& command, const LLUUID&id, const std::string& verb); + + SLURL_TYPE getType() const { return mType; } + + std::string getSLURLString() const; + std::string getLoginString() const; + std::string getLocationString() const; + std::string getGrid() const { return mGrid; } + std::string getRegion() const { return mRegion; } + LLVector3 getPosition() const { return mPosition; } + std::string getAppCmd() const { return mAppCmd; } + std::string getAppQuery() const { return mAppQuery; } + LLSD getAppQueryMap() const { return mAppQueryMap; } + LLSD getAppPath() const { return mAppPath; } + + bool isValid() const { return mType != INVALID; } + + bool operator==(const LLSLURL& rhs); + bool operator!=(const LLSLURL&rhs); + + std::string asString() const ; + + +protected: + SLURL_TYPE mType; + + // used for Apps and Help + std::string mAppCmd; + LLSD mAppPath; + LLSD mAppQueryMap; + std::string mAppQuery; + + std::string mGrid; // reference to grid manager grid + std::string mRegion; + LLVector3 mPosition; }; -#endif +#endif // LLSLURL_H diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 6f7a4e2f6a..d1c6fca063 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -144,7 +144,7 @@ #include "lltoolmgr.h" #include "llui.h" #include "llurldispatcher.h" -#include "llurlsimstring.h" +#include "llslurl.h" #include "llurlhistory.h" #include "llurlwhitelist.h" #include "llvieweraudio.h" @@ -229,6 +229,7 @@ static std::string sInitialOutfitGender; // "male" or "female" static bool gUseCircuitCallbackCalled = false; EStartupState LLStartUp::gStartupState = STATE_FIRST; +LLSLURL LLStartUp::sStartSLURL; static LLPointer gUserCredential; static std::string gDisplayName; @@ -419,7 +420,7 @@ bool idle_startup() if ( STATE_FIRST == LLStartUp::getStartupState() ) { - gViewerWindow->showCursor(); + gViewerWindow->showCursor(); gViewerWindow->getWindow()->setCursor(UI_CURSOR_WAIT); ///////////////////////////////////////////////// @@ -719,7 +720,7 @@ bool idle_startup() // if (gUserCredential.isNull()) { - gUserCredential = gLoginHandler.initializeLoginInfo(LLStartUp::sSLURLCommand); + gUserCredential = gLoginHandler.initializeLoginInfo(); } if (gUserCredential.isNull()) { @@ -769,7 +770,7 @@ bool idle_startup() // show the login view until login_show() is called below. if (gUserCredential.isNull()) { - gUserCredential = gLoginHandler.initializeLoginInfo(LLStartUp::sSLURLCommand); + gUserCredential = gLoginHandler.initializeLoginInfo(); } if (gNoRender) { @@ -936,11 +937,7 @@ bool idle_startup() if (show_connect_box) { - std::string location; - LLPanelLogin::getLocation( location ); - LLURLSimString::setString( location ); - - // END TODO + LLStartUp::setStartSLURL(LLPanelLogin::getLocation()); LLPanelLogin::closePanel(); } @@ -961,26 +958,21 @@ bool idle_startup() // their last location, or some URL "-url //sim/x/y[/z]" // All accounts have both a home and a last location, and we don't support // more locations than that. Choose the appropriate one. JC - if (LLURLSimString::parse()) - { - // a startup URL was specified - agent_location_id = START_LOCATION_ID_URL; - - // doesn't really matter what location_which is, since - // gAgentStartLookAt will be overwritten when the - // UserLoginLocationReply arrives - location_which = START_LOCATION_ID_LAST; - } - else if (gSavedSettings.getString("LoginLocation") == "last" ) - { - agent_location_id = START_LOCATION_ID_LAST; // last location - location_which = START_LOCATION_ID_LAST; - } - else - { - agent_location_id = START_LOCATION_ID_HOME; // home - location_which = START_LOCATION_ID_HOME; - } + switch (LLStartUp::getStartSLURL().getType()) + { + case LLSLURL::LOCATION: + agent_location_id = START_LOCATION_ID_URL; + location_which = START_LOCATION_ID_LAST; + break; + case LLSLURL::LAST_LOCATION: + agent_location_id = START_LOCATION_ID_LAST; + location_which = START_LOCATION_ID_LAST; + break; + default: + agent_location_id = START_LOCATION_ID_HOME; + location_which = START_LOCATION_ID_HOME; + break; + } gViewerWindow->getWindow()->setCursor(UI_CURSOR_WAIT); @@ -1851,7 +1843,8 @@ bool idle_startup() // thus, do not show this alert. if (!gAgent.isFirstLogin()) { - bool url_ok = LLURLSimString::sInstance.parse(); + llinfos << "gAgentStartLocation : " << gAgentStartLocation << llendl; + bool url_ok = (LLStartUp::getStartSLURL().getType() == LLSLURL::LOCATION); if ((url_ok && gAgentStartLocation == "url") || (!url_ok && ((gAgentStartLocation == gSavedSettings.getString("LoginLocation"))))) { @@ -2197,7 +2190,7 @@ bool login_alert_status(const LLSD& notification, const LLSD& response) // break; case 2: // Teleport // Restart the login process, starting at our home locaton - LLURLSimString::setString("home"); + LLStartUp::setStartSLURL(LLSLURL(LLSLURL::SIM_LOCATION_HOME)); LLStartUp::setStartupState( STATE_LOGIN_CLEANUP ); break; default: @@ -2619,7 +2612,6 @@ void reset_login() //--------------------------------------------------------------------------- -std::string LLStartUp::sSLURLCommand; bool LLStartUp::canGoFullscreen() { @@ -2652,35 +2644,40 @@ void LLStartUp::fontInit() bool LLStartUp::dispatchURL() { // ok, if we've gotten this far and have a startup URL - if (!sSLURLCommand.empty()) + if (!getStartSLURL().isValid()) { - LLMediaCtrl* web = NULL; - const bool trusted_browser = false; - LLURLDispatcher::dispatch(sSLURLCommand, web, trusted_browser); + return false; } - else if (LLURLSimString::parse()) - { + if(getStartSLURL().getType() != LLSLURL::APP) + { + // If we started with a location, but we're already // at that location, don't pop dialogs open. LLVector3 pos = gAgent.getPositionAgent(); - F32 dx = pos.mV[VX] - (F32)LLURLSimString::sInstance.mX; - F32 dy = pos.mV[VY] - (F32)LLURLSimString::sInstance.mY; + LLVector3 slurlpos = getStartSLURL().getPosition(); + F32 dx = pos.mV[VX] - slurlpos.mV[VX]; + F32 dy = pos.mV[VY] - slurlpos.mV[VY]; const F32 SLOP = 2.f; // meters - if( LLURLSimString::sInstance.mSimName != gAgent.getRegion()->getName() + if( getStartSLURL().getRegion() != gAgent.getRegion()->getName() || (dx*dx > SLOP*SLOP) || (dy*dy > SLOP*SLOP) ) { - std::string url = LLURLSimString::getURL(); - LLMediaCtrl* web = NULL; - const bool trusted_browser = false; - LLURLDispatcher::dispatch(url, web, trusted_browser); + LLURLDispatcher::dispatch(getStartSLURL().getSLURLString(), + NULL, false); } return true; } return false; } +void LLStartUp::setStartSLURL(const LLSLURL& slurl) +{ + sStartSLURL = slurl; + gSavedSettings.setBOOL("LoginLastLocation", + !(slurl.getType() == LLSLURL::HOME_LOCATION)); +} + bool login_alert_done(const LLSD& notification, const LLSD& response) { LLPanelLogin::giveFocus(); diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h index 28bc7fcd2a..c01a6fc8e1 100644 --- a/indra/newview/llstartup.h +++ b/indra/newview/llstartup.h @@ -34,6 +34,7 @@ #define LL_LLSTARTUP_H #include +#include "llslurl.h" class LLViewerTexture ; class LLEventPump; @@ -106,13 +107,13 @@ public: // if we have a SLURL or sim string ("Ahern/123/45") that started // the viewer, dispatch it - static std::string sSLURLCommand; - // *HACK: On startup, if we were passed a secondlife://app/do/foo - // command URL, store it for later processing. - static void postStartupState(); + static void setStartSLURL(const LLSLURL& slurl); + static LLSLURL& getStartSLURL() { return sStartSLURL; } private: + static LLSLURL sStartSLURL; + static std::string startupStateToString(EStartupState state); static EStartupState gStartupState; // Do not set directly, use LLStartup::setStartupState static boost::scoped_ptr sStateWatcher; diff --git a/indra/newview/llstylemap.cpp b/indra/newview/llstylemap.cpp index 2485563cbc..15e02b38d1 100644 --- a/indra/newview/llstylemap.cpp +++ b/indra/newview/llstylemap.cpp @@ -50,7 +50,7 @@ const LLStyle::Params &LLStyleMap::lookupAgent(const LLUUID &source) { style_params.color.control = "HTMLLinkColor"; style_params.link_href = - LLSLURL::buildCommand("agent", source, "inspect"); + LLSLURL("agent", source, "inspect").getSLURLString(); } else { diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp index 0b6bd4b401..5b11f82a45 100644 --- a/indra/newview/llurldispatcher.cpp +++ b/indra/newview/llurldispatcher.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2007&license=viewergpl$ * - * Copyright (c) 2007-2009, Linden Research, Inc. + * Copyright (c) 2010, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab @@ -12,13 +12,12 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * online at http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -45,10 +44,10 @@ #include "llsidetray.h" #include "llslurl.h" #include "llstartup.h" // gStartupState -#include "llurlsimstring.h" #include "llweb.h" #include "llworldmapmessage.h" #include "llurldispatcherlistener.h" +#include "llviewernetwork.h" // library includes #include "llnotificationsutil.h" @@ -59,25 +58,25 @@ static LLURLDispatcherListener sURLDispatcherListener; class LLURLDispatcherImpl { public: - static bool dispatch(const std::string& url, + static bool dispatch(const LLSLURL& slurl, LLMediaCtrl* web, bool trusted_browser); // returns true if handled or explicitly blocked. - static bool dispatchRightClick(const std::string& url); + static bool dispatchRightClick(const LLSLURL& slurl); private: - static bool dispatchCore(const std::string& url, + static bool dispatchCore(const LLSLURL& slurl, bool right_mouse, LLMediaCtrl* web, bool trusted_browser); // handles both left and right click - static bool dispatchHelp(const std::string& url, bool right_mouse); + static bool dispatchHelp(const LLSLURL& slurl, bool right_mouse); // Handles sl://app.floater.html.help by showing Help floater. // Returns true if handled. - static bool dispatchApp(const std::string& url, + static bool dispatchApp(const LLSLURL& slurl, bool right_mouse, LLMediaCtrl* web, bool trusted_browser); @@ -85,16 +84,16 @@ private: // by showing panel in Search floater. // Returns true if handled or explicitly blocked. - static bool dispatchRegion(const std::string& url, bool right_mouse); + static bool dispatchRegion(const LLSLURL& slurl, bool right_mouse); // handles secondlife://Ahern/123/45/67/ // Returns true if handled. - static void regionHandleCallback(U64 handle, const std::string& url, + static void regionHandleCallback(U64 handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport); // Called by LLWorldMap when a location has been resolved to a // region name - static void regionNameCallback(U64 handle, const std::string& url, + static void regionNameCallback(U64 handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport); // Called by LLWorldMap when a region name has been resolved to a // location in-world, used by places-panel display. @@ -103,65 +102,57 @@ private: }; // static -bool LLURLDispatcherImpl::dispatchCore(const std::string& url, +bool LLURLDispatcherImpl::dispatchCore(const LLSLURL& slurl, bool right_mouse, LLMediaCtrl* web, bool trusted_browser) { - if (url.empty()) return false; - //if (dispatchHelp(url, right_mouse)) return true; - if (dispatchApp(url, right_mouse, web, trusted_browser)) return true; - if (dispatchRegion(url, right_mouse)) return true; + //if (dispatchHelp(slurl, right_mouse)) return true; + switch(slurl.getType()) + { + case LLSLURL::APP: + return dispatchApp(slurl, right_mouse, web, trusted_browser); + case LLSLURL::LOCATION: + return dispatchRegion(slurl, right_mouse); + default: + return false; + } /* // Inform the user we can't handle this std::map args; - args["SLURL"] = url; + args["SLURL"] = slurl; r; */ - - return false; } // static -bool LLURLDispatcherImpl::dispatch(const std::string& url, +bool LLURLDispatcherImpl::dispatch(const LLSLURL& slurl, LLMediaCtrl* web, bool trusted_browser) { - llinfos << "url: " << url << llendl; const bool right_click = false; - return dispatchCore(url, right_click, web, trusted_browser); + return dispatchCore(slurl, right_click, web, trusted_browser); } // static -bool LLURLDispatcherImpl::dispatchRightClick(const std::string& url) +bool LLURLDispatcherImpl::dispatchRightClick(const LLSLURL& slurl) { - llinfos << "url: " << url << llendl; const bool right_click = true; LLMediaCtrl* web = NULL; const bool trusted_browser = false; - return dispatchCore(url, right_click, web, trusted_browser); + return dispatchCore(slurl, right_click, web, trusted_browser); } // static -bool LLURLDispatcherImpl::dispatchApp(const std::string& url, +bool LLURLDispatcherImpl::dispatchApp(const LLSLURL& slurl, bool right_mouse, LLMediaCtrl* web, bool trusted_browser) { - // ensure the URL is in the secondlife:///app/ format - if (!LLSLURL::isSLURLCommand(url)) - { - return false; - } - - LLURI uri(url); - LLSD pathArray = uri.pathArray(); - pathArray.erase(0); // erase "app" - std::string cmd = pathArray.get(0); - pathArray.erase(0); // erase "cmd" + llinfos << "cmd: " << slurl.getAppCmd() << " path: " << slurl.getAppPath() << " query: " << slurl.getAppQuery() << llendl; bool handled = LLCommandDispatcher::dispatch( - cmd, pathArray, uri.queryMap(), web, trusted_browser); + slurl.getAppCmd(), slurl.getAppPath(), slurl.getAppQuery(), web, trusted_browser); // alert if we didn't handle this secondlife:///app/ SLURL // (but still return true because it is a valid app SLURL) @@ -173,109 +164,68 @@ bool LLURLDispatcherImpl::dispatchApp(const std::string& url, } // static -bool LLURLDispatcherImpl::dispatchRegion(const std::string& url, bool right_mouse) +bool LLURLDispatcherImpl::dispatchRegion(const LLSLURL& slurl, bool right_mouse) { - if (!LLSLURL::isSLURL(url)) - { - return false; - } - // Before we're logged in, need to update the startup screen // to tell the user where they are going. if (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP) { // Parse it and stash in globals, it will be dispatched in // STATE_CLEANUP. - LLURLSimString::setString(url); + LLStartUp::setStartSLURL(slurl); // We're at the login screen, so make sure user can see // the login location box to know where they are going. - LLPanelLogin::refreshLocation( true ); + LLPanelLogin::updateLocationCombo( true ); return true; } - std::string sim_string = LLSLURL::stripProtocol(url); - std::string region_name; - S32 x = 128; - S32 y = 128; - S32 z = 0; - LLURLSimString::parse(sim_string, ®ion_name, &x, &y, &z); - // LLFloaterURLDisplay functionality moved to LLPanelPlaces in Side Tray. - //LLFloaterURLDisplay* url_displayp = LLFloaterReg::getTypedInstance("preview_url",LLSD()); - //if(url_displayp) url_displayp->setName(region_name); + //LLFloaterURLDisplay* slurl_displayp = LLFloaterReg::getTypedInstance("preview_url",LLSD()); + //if(slurl_displayp) slurl_displayp->setName(region_name); // Request a region handle by name - LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name, + LLWorldMapMessage::getInstance()->sendNamedRegionRequest(slurl.getRegion(), LLURLDispatcherImpl::regionNameCallback, - url, + slurl.getSLURLString(), false); // don't teleport return true; } /*static*/ -void LLURLDispatcherImpl::regionNameCallback(U64 region_handle, const std::string& url, const LLUUID& snapshot_id, bool teleport) +void LLURLDispatcherImpl::regionNameCallback(U64 region_handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport) { - std::string sim_string = LLSLURL::stripProtocol(url); - std::string region_name; - S32 x = 128; - S32 y = 128; - S32 z = 0; - LLURLSimString::parse(sim_string, ®ion_name, &x, &y, &z); - - LLVector3 local_pos; - local_pos.mV[VX] = (F32)x; - local_pos.mV[VY] = (F32)y; - local_pos.mV[VZ] = (F32)z; + regionHandleCallback(region_handle, slurl, snapshot_id, teleport); +} - - // determine whether the point is in this region - if ((x >= 0) && (x < REGION_WIDTH_UNITS) && - (y >= 0) && (y < REGION_WIDTH_UNITS)) - { - // if so, we're done - regionHandleCallback(region_handle, url, snapshot_id, teleport); - } +/* static */ +void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const LLSLURL& slurl, const LLUUID& snapshot_id, bool teleport) +{ - else + // we can't teleport cross grid at this point + if((!LLGridManager::getInstance()->isSystemGrid(slurl.getGrid()) || !LLGridManager::getInstance()->isSystemGrid()) && + (slurl.getGrid() != LLGridManager::getInstance()->getGridName())) { - // otherwise find the new region from the location + LLSD args; + args["SLURL"] = slurl.getLocationString(); + args["CURRENT_GRID"] = LLGridManager::getInstance()->getGridLabel(); + LLSD grid_info = LLGridManager::getInstance()->getGridInfo(slurl.getGrid()); - // add the position to get the new region - LLVector3d global_pos = from_region_handle(region_handle) + LLVector3d(local_pos); - - U64 new_region_handle = to_region_handle(global_pos); - LLWorldMapMessage::getInstance()->sendHandleRegionRequest(new_region_handle, - LLURLDispatcherImpl::regionHandleCallback, - url, teleport); + if(grid_info.has(GRID_LABEL_VALUE)) + { + args["GRID"] = grid_info[GRID_LABEL_VALUE].asString(); + } + else + { + args["GRID"] = slurl.getGrid(); + } + LLNotificationsUtil::add("CantTeleportToGrid", args); + return; } -} - -/* static */ -void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const std::string& url, const LLUUID& snapshot_id, bool teleport) -{ - std::string sim_string = LLSLURL::stripProtocol(url); - std::string region_name; - S32 x = 128; - S32 y = 128; - S32 z = 0; - LLURLSimString::parse(sim_string, ®ion_name, &x, &y, &z); - - // remap x and y to local coordinates - S32 local_x = x % REGION_WIDTH_UNITS; - S32 local_y = y % REGION_WIDTH_UNITS; - if (local_x < 0) - local_x += REGION_WIDTH_UNITS; - if (local_y < 0) - local_y += REGION_WIDTH_UNITS; - LLVector3 local_pos; - local_pos.mV[VX] = (F32)local_x; - local_pos.mV[VY] = (F32)local_y; - local_pos.mV[VZ] = (F32)z; LLVector3d global_pos = from_region_handle(region_handle); - global_pos += LLVector3d(local_pos); + global_pos += LLVector3d(slurl.getPosition()); if (teleport) { @@ -299,8 +249,8 @@ void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const std::str // LLFloaterURLDisplay functionality moved to LLPanelPlaces in Side Tray. // // display informational floater, allow user to click teleport btn -// LLFloaterURLDisplay* url_displayp = LLFloaterReg::getTypedInstance("preview_url",LLSD()); -// if(url_displayp) +// LLFloaterURLDisplay* slurl_displayp = LLFloaterReg::getTypedInstance("preview_url",LLSD()); +// if(slurl_displayp) // { // url_displayp->displayParcelInfo(region_handle, local_pos); // if(snapshot_id.notNull()) @@ -315,7 +265,7 @@ void LLURLDispatcherImpl::regionHandleCallback(U64 region_handle, const std::str //--------------------------------------------------------------------------- // Teleportation links are handled here because they are tightly coupled -// to URL parsing and sim-fragment parsing +// to SLURL parsing and sim-fragment parsing class LLTeleportHandler : public LLCommandHandler { public: @@ -331,18 +281,21 @@ public: // a global position, and teleport to it if (tokens.size() < 1) return false; - // Region names may be %20 escaped. - std::string region_name = LLURLSimString::unescapeRegionName(tokens[0]); - - // build secondlife://De%20Haro/123/45/67 for use in callback - std::string url = LLSLURL::PREFIX_SECONDLIFE; - for (int i = 0; i < tokens.size(); ++i) + LLVector3 coords(128, 128, 0); + if (tokens.size() <= 4) { - url += tokens[i].asString() + "/"; + coords = LLVector3(tokens[1].asReal(), + tokens[2].asReal(), + tokens[3].asReal()); } + + // Region names may be %20 escaped. + + std::string region_name = LLURI::unescape(tokens[0]); + LLWorldMapMessage::getInstance()->sendNamedRegionRequest(region_name, LLURLDispatcherImpl::regionHandleCallback, - url, + LLSLURL(region_name, coords).getSLURLString(), true); // teleport return true; } @@ -352,21 +305,21 @@ LLTeleportHandler gTeleportHandler; //--------------------------------------------------------------------------- // static -bool LLURLDispatcher::dispatch(const std::string& url, +bool LLURLDispatcher::dispatch(const std::string& slurl, LLMediaCtrl* web, bool trusted_browser) { - return LLURLDispatcherImpl::dispatch(url, web, trusted_browser); + return LLURLDispatcherImpl::dispatch(LLSLURL(slurl), web, trusted_browser); } // static -bool LLURLDispatcher::dispatchRightClick(const std::string& url) +bool LLURLDispatcher::dispatchRightClick(const std::string& slurl) { - return LLURLDispatcherImpl::dispatchRightClick(url); + return LLURLDispatcherImpl::dispatchRightClick(LLSLURL(slurl)); } // static -bool LLURLDispatcher::dispatchFromTextEditor(const std::string& url) +bool LLURLDispatcher::dispatchFromTextEditor(const std::string& slurl) { // *NOTE: Text editors are considered sources of trusted URLs // in order to make avatar profile links in chat history work. @@ -376,5 +329,7 @@ bool LLURLDispatcher::dispatchFromTextEditor(const std::string& url) // *TODO: Make this trust model more refined. JC const bool trusted_browser = true; LLMediaCtrl* web = NULL; - return LLURLDispatcherImpl::dispatch(url, web, trusted_browser); + return LLURLDispatcherImpl::dispatch(LLSLURL(slurl), web, trusted_browser); } + + diff --git a/indra/newview/llurldispatcher.h b/indra/newview/llurldispatcher.h index ff8a351253..41c5225e00 100644 --- a/indra/newview/llurldispatcher.h +++ b/indra/newview/llurldispatcher.h @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2007&license=viewergpl$ * - * Copyright (c) 2007-2009, Linden Research, Inc. + * Copyright (c) 2007, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab @@ -12,13 +12,12 @@ * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or - * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or - * online at - * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * online at http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, @@ -31,16 +30,17 @@ */ #ifndef LLURLDISPATCHER_H #define LLURLDISPATCHER_H - +#include "llslurl.h" class LLMediaCtrl; class LLURLDispatcher { public: - static bool dispatch(const std::string& url, + + static bool dispatch(const std::string& slurl, LLMediaCtrl* web, - bool trusted_browser); + bool trusted_browser); // At startup time and on clicks in internal web browsers, // teleport, open map, or run requested command. // @param url @@ -54,9 +54,9 @@ public: // that navigates to trusted (Linden Lab) pages. // Returns true if someone handled the URL. - static bool dispatchRightClick(const std::string& url); + static bool dispatchRightClick(const std::string& slurl); - static bool dispatchFromTextEditor(const std::string& url); + static bool dispatchFromTextEditor(const std::string& slurl); }; #endif diff --git a/indra/newview/llurldispatcherlistener.cpp b/indra/newview/llurldispatcherlistener.cpp index fea6a769c5..8f50f30d6d 100644 --- a/indra/newview/llurldispatcherlistener.cpp +++ b/indra/newview/llurldispatcherlistener.cpp @@ -17,6 +17,7 @@ // std headers // external library headers // other Linden headers +#include "llslurl.h" #include "llurldispatcher.h" LLURLDispatcherListener::LLURLDispatcherListener(/* LLURLDispatcher* instance */): diff --git a/indra/newview/llurllineeditorctrl.cpp b/indra/newview/llurllineeditorctrl.cpp index 258c3ddd75..2a5c7cbea1 100644 --- a/indra/newview/llurllineeditorctrl.cpp +++ b/indra/newview/llurllineeditorctrl.cpp @@ -89,7 +89,7 @@ void LLURLLineEditor::copyEscapedURLToClipboard() const std::string unescaped_text = wstring_to_utf8str(mText.getWString().substr(left_pos, length)); LLWString text_to_copy; - if (LLSLURL::isSLURL(unescaped_text)) + if (LLSLURL(unescaped_text).isValid()) text_to_copy = utf8str_to_wstring(LLWeb::escapeURL(unescaped_text)); else text_to_copy = utf8str_to_wstring(unescaped_text); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 3362142807..6bff78de82 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -1518,9 +1518,9 @@ void inventory_offer_handler(LLOfferInfo* info) payload["give_inventory_notification"] = FALSE; args["OBJECTFROMNAME"] = info->mFromName; args["NAME"] = info->mFromName; - args["NAME_SLURL"] = LLSLURL::buildCommand("agent", info->mFromID, "about"); + args["NAME_SLURL"] = LLSLURL("agent", info->mFromID, "about").getSLURLString(); std::string verb = "select?name=" + LLURI::escape(msg); - args["ITEM_SLURL"] = LLSLURL::buildCommand("inventory", info->mObjectID, verb.c_str()); + args["ITEM_SLURL"] = LLSLURL("inventory", info->mObjectID, verb.c_str()).getSLURLString(); LLNotification::Params p("ObjectGiveItem"); @@ -3037,7 +3037,7 @@ void process_agent_movement_complete(LLMessageSystem* msg, void**) if (avatarp) { // Chat the "back" SLURL. (DEV-4907) - LLChat chat("Teleport completed from " + gAgent.getTeleportSourceSLURL()); + LLChat chat("Teleport completed from " + gAgent.getTeleportSourceSLURL().getSLURLString()); chat.mSourceType = CHAT_SOURCE_SYSTEM; LLFloaterChat::addChatHistory(chat); @@ -5353,7 +5353,7 @@ void send_group_notice(const LLUUID& group_id, bool handle_lure_callback(const LLSD& notification, const LLSD& response) { std::string text = response["message"].asString(); - text.append("\r\n").append(LLAgentUI::buildSLURL()); + text.append("\r\n").append(LLAgentUI::buildSLURL().getSLURLString()); S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if(0 == option) @@ -5774,7 +5774,7 @@ void process_covenant_reply(LLMessageSystem* msg, void**) LLFloaterBuyLand::updateEstateName(estate_name); std::string owner_name = - LLSLURL::buildCommand("agent", estate_owner_id, "inspect"); + LLSLURL("agent", estate_owner_id, "inspect").getSLURLString(); LLPanelEstateCovenant::updateEstateOwnerName(owner_name); LLPanelLandCovenant::updateEstateOwnerName(owner_name); LLFloaterBuyLand::updateEstateOwnerName(owner_name); diff --git a/indra/newview/llviewernetwork.cpp b/indra/newview/llviewernetwork.cpp index 82dc459777..3615c00f37 100644 --- a/indra/newview/llviewernetwork.cpp +++ b/indra/newview/llviewernetwork.cpp @@ -5,7 +5,7 @@ * * $LicenseInfo:firstyear=2006&license=viewergpl$ * - * Copyright (c) 2006-2010, 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 @@ -40,7 +40,8 @@ const char* DEFAULT_LOGIN_PAGE = "http://secondlife.com/app/login/"; -const char* SYSTEM_GRID_SLURL_BASE = "http://slurl.com/secondlife/"; +const char* SYSTEM_GRID_SLURL_BASE = "secondlife://%s/secondlife/"; +const char* MAIN_GRID_SLURL_BASE = "http://slurl.com/secondlife/"; const char* SYSTEM_GRID_APP_SLURL_BASE = "secondlife:///app"; const char* DEFAULT_SLURL_BASE = "https://%s/region/"; @@ -60,6 +61,12 @@ LLGridManager::LLGridManager() } +LLGridManager::LLGridManager(const std::string& grid_file) +{ + // initialize with an explicity grid file for testing. + initialize(grid_file); +} + // // LLGridManager - class for managing the list of known grids, and the current // selection @@ -391,7 +398,7 @@ void LLGridManager::addSystemGrid(const std::string& label, 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()) { @@ -406,8 +413,13 @@ void LLGridManager::addSystemGrid(const std::string& label, // if we're building a debug version. if (name == std::string(MAINGRID)) { + grid[GRID_SLURL_BASE] = MAIN_GRID_SLURL_BASE; grid[GRID_IS_FAVORITE_VALUE] = TRUE; } + else + { + grid[GRID_SLURL_BASE] = llformat(SYSTEM_GRID_SLURL_BASE, label.c_str()); + } addGrid(grid); } @@ -458,6 +470,20 @@ void LLGridManager::setGridChoice(const std::string& grid_name) gSavedSettings.setString("CurrentGrid", grid_name); } +std::string LLGridManager::getGridByLabel( const std::string &grid_label) +{ + for(LLSD::map_iterator grid_iter = mGridList.beginMap(); + grid_iter != mGridList.endMap(); + grid_iter++) + { + if (grid_iter->second.has(GRID_LABEL_VALUE) && (grid_iter->second[GRID_LABEL_VALUE].asString() == grid_label)) + { + return grid_iter->first; + } + } + return std::string(); +} + void LLGridManager::getLoginURIs(std::vector& uris) { uris.clear(); @@ -528,10 +554,10 @@ 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(); + return mGridList[grid_name][GRID_APP_SLURL_BASE].asString(); } else { - return llformat(DEFAULT_APP_SLURL_BASE, grid_name.c_str()); + return llformat(DEFAULT_APP_SLURL_BASE, grid_name.c_str()); } } diff --git a/indra/newview/llviewernetwork.h b/indra/newview/llviewernetwork.h index 0642845d54..bcf0c5a8f2 100644 --- a/indra/newview/llviewernetwork.h +++ b/indra/newview/llviewernetwork.h @@ -43,7 +43,6 @@ extern const char* DEFAULT_LOGIN_PAGE; #define GRID_LOGIN_PAGE_VALUE "login_page" #define GRID_IS_SYSTEM_GRID_VALUE "system_grid" #define GRID_IS_FAVORITE_VALUE "favorite" -#define GRID_IS_VISIBLE_VALUE "visible" #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" @@ -79,6 +78,7 @@ public: // 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(); @@ -112,10 +112,7 @@ public: void setGridChoice(const std::string& grid_name); - std::string getGridLabel() - { - return mGridList[mGridName][GRID_LABEL_VALUE]; - } + 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];} @@ -132,6 +129,8 @@ public: LLSD getGridInfo() { return mGridList[mGridName]; } + std::string getGridByLabel( const std::string &grid_label); + bool isSystemGrid(const std::string& grid) { return mGridList.has(grid) && diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index f8e08dbf7d..ee6fb8120c 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -2033,7 +2033,7 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) gSavedSettings.setBOOL("ForceShowGrid", visible); // Initialize visibility (and don't force visibility - use prefs) - LLPanelLogin::refreshLocation( false ); + LLPanelLogin::updateLocationCombo( false ); } } diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index c4cbcb1dc8..d177cfce7d 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -2371,6 +2371,15 @@ Please choose the male or female avatar. You can change your mind later. notext="Female" yestext="Male"/> + +Could not teleport to [SLURL] as it's on a different grid ([GRID]) than the current grid ([CURRENT_GRID]). Please close your viewer and try again. + + + diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp index 9222882f5f..f4d9cc99cc 100644 --- a/indra/newview/tests/lllogininstance_test.cpp +++ b/indra/newview/tests/lllogininstance_test.cpp @@ -36,7 +36,12 @@ const std::string APPVIEWER_SERIALNUMBER("appviewer_serialno"); //----------------------------------------------------------------------------- static LLEventStream gTestPump("test_pump"); +#include "../llslurl.h" +#include "../llstartup.h" +LLSLURL LLStartUp::sStartSLURL; + #include "lllogin.h" + static std::string gLoginURI; static LLSD gLoginCreds; static bool gDisconnectCalled = false; @@ -141,10 +146,6 @@ BOOL LLControlGroup::declareString(const std::string& name, const std::string &i #include "lluicolortable.h" void LLUIColorTable::saveUserSettings(void)const {} -//----------------------------------------------------------------------------- -#include "../llurlsimstring.h" -LLURLSimString LLURLSimString::sInstance; -bool LLURLSimString::parse() { return true; } //----------------------------------------------------------------------------- #include "llnotifications.h" diff --git a/indra/newview/tests/llslurl_test.cpp b/indra/newview/tests/llslurl_test.cpp new file mode 100644 index 0000000000..90d2526890 --- /dev/null +++ b/indra/newview/tests/llslurl_test.cpp @@ -0,0 +1,258 @@ +/** + * @file llsecapi_test.cpp + * @author Roxie + * @date 2009-02-10 + * @brief Test the sec api functionality + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 2009, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/programs/open_source/licensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ +#include "../llviewerprecompiledheaders.h" +#include "../llviewernetwork.h" +#include "../test/lltut.h" +#include "../llslurl.h" +#include "../../llxml/llcontrol.h" +#include "llsdserialize.h" +//---------------------------------------------------------------------------- +// Mock objects for the dependencies of the code we're testing + +LLControlGroup::LLControlGroup(const std::string& name) +: LLInstanceTracker(name) {} +LLControlGroup::~LLControlGroup() {} +BOOL LLControlGroup::declareString(const std::string& name, + const std::string& initial_val, + const std::string& comment, + BOOL persist) {return TRUE;} +void LLControlGroup::setString(const std::string& name, const std::string& val){} + +std::string gCmdLineLoginURI; +std::string gCmdLineGridChoice; +std::string gCmdLineHelperURI; +std::string gLoginPage; +std::string gCurrentGrid; +std::string LLControlGroup::getString(const std::string& name) +{ + if (name == "CmdLineGridChoice") + return gCmdLineGridChoice; + else if (name == "CmdLineHelperURI") + return gCmdLineHelperURI; + else if (name == "LoginPage") + return gLoginPage; + else if (name == "CurrentGrid") + return gCurrentGrid; + return ""; +} + +LLSD LLControlGroup::getLLSD(const std::string& name) +{ + if (name == "CmdLineLoginURI") + { + if(!gCmdLineLoginURI.empty()) + { + return LLSD(gCmdLineLoginURI); + } + } + return LLSD(); +} + + +LLControlGroup gSavedSettings("test"); + +// ------------------------------------------------------------------------------------------- +// TUT +// ------------------------------------------------------------------------------------------- +namespace tut +{ + // Test wrapper declaration : wrapping nothing for the moment + struct slurlTest + { + slurlTest() + { + LLGridManager::getInstance()->initialize(std::string("")); + } + ~slurlTest() + { + } + }; + + // Tut templating thingamagic: test group, object and test instance + typedef test_group slurlTestFactory; + typedef slurlTestFactory::object slurlTestObject; + tut::slurlTestFactory tut_test("llslurl"); + + // --------------------------------------------------------------------------------------- + // Test functions + // --------------------------------------------------------------------------------------- + // construction from slurl string + template<> template<> + void slurlTestObject::test<1>() + { + LLGridManager::getInstance()->setGridChoice("util.agni.lindenlab.com"); + + LLSLURL slurl = LLSLURL(""); + ensure_equals("null slurl", (int)slurl.getType(), LLSLURL::LAST_LOCATION); + + slurl = LLSLURL("http://slurl.com/secondlife/myregion"); + ensure_equals("slurl.com slurl, region only - type", slurl.getType(), LLSLURL::LOCATION); + ensure_equals("slurl.com slurl, region only", slurl.getSLURLString(), + "http://slurl.com/secondlife/myregion/128/128/0"); + + slurl = LLSLURL("http://slurl.com/secondlife/myregion/1/2/3"); + ensure_equals("slurl.com slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); + ensure_equals("slurl.com slurl, region + coords", slurl.getSLURLString(), + "http://slurl.com/secondlife/myregion/1/2/3"); + + slurl = LLSLURL("secondlife://myregion"); + ensure_equals("secondlife: slurl, region only - type", slurl.getType(), LLSLURL::LOCATION); + ensure_equals("secondlife: slurl, region only", slurl.getSLURLString(), + "http://slurl.com/secondlife/myregion/128/128/0"); + + slurl = LLSLURL("secondlife://myregion/1/2/3"); + ensure_equals("secondlife: slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); + ensure_equals("secondlife slurl, region + coords", slurl.getSLURLString(), + "http://slurl.com/secondlife/myregion/1/2/3"); + + slurl = LLSLURL("/myregion"); + ensure_equals("/region slurl, region- type", slurl.getType(), LLSLURL::LOCATION); + ensure_equals("/region slurl, region ", slurl.getSLURLString(), + "http://slurl.com/secondlife/myregion/128/128/0"); + + slurl = LLSLURL("/myregion/1/2/3"); + ensure_equals("/: slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); + ensure_equals("/ slurl, region + coords", slurl.getSLURLString(), + "http://slurl.com/secondlife/myregion/1/2/3"); + + slurl = LLSLURL("my region/1/2/3"); + ensure_equals(" slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); + ensure_equals(" slurl, region + coords", slurl.getSLURLString(), + "http://slurl.com/secondlife/my%20region/1/2/3"); + + slurl = LLSLURL("https://my.grid.com/region/my%20region/1/2/3"); + ensure_equals("grid slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); + ensure_equals("grid slurl, region + coords", slurl.getSLURLString(), + "https://my.grid.com/region/my%20region/1/2/3"); + + slurl = LLSLURL("https://my.grid.com/region/my region"); + ensure_equals("grid slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); + ensure_equals("grid slurl, region + coords", slurl.getSLURLString(), + "https://my.grid.com/region/my%20region/128/128/0"); + + LLGridManager::getInstance()->setGridChoice("foo.bar.com"); + slurl = LLSLURL("/myregion/1/2/3"); + ensure_equals("/: slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); + ensure_equals("/ slurl, region + coords", slurl.getSLURLString(), + "https://foo.bar.com/region/myregion/1/2/3"); + + slurl = LLSLURL("myregion/1/2/3"); + ensure_equals(": slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); + ensure_equals(" slurl, region + coords", slurl.getSLURLString(), + "https://foo.bar.com/region/myregion/1/2/3"); + + slurl = LLSLURL(LLSLURL::SIM_LOCATION_HOME); + ensure_equals("home", slurl.getType(), LLSLURL::HOME_LOCATION); + + slurl = LLSLURL(LLSLURL::SIM_LOCATION_LAST); + ensure_equals("last", slurl.getType(), LLSLURL::LAST_LOCATION); + + slurl = LLSLURL("secondlife:///app/foo/bar?12345"); + ensure_equals("app", slurl.getType(), LLSLURL::APP); + ensure_equals("appcmd", slurl.getAppCmd(), "foo"); + ensure_equals("apppath", slurl.getAppPath().size(), 1); + ensure_equals("apppath2", slurl.getAppPath()[0].asString(), "bar"); + ensure_equals("appquery", slurl.getAppQuery(), "12345"); + ensure_equals("grid1", "foo.bar.com", slurl.getGrid()); + + slurl = LLSLURL("secondlife://Aditi/app/foo/bar?12345"); + ensure_equals("app", slurl.getType(), LLSLURL::APP); + ensure_equals("appcmd", slurl.getAppCmd(), "foo"); + ensure_equals("apppath", slurl.getAppPath().size(), 1); + ensure_equals("apppath2", slurl.getAppPath()[0].asString(), "bar"); + ensure_equals("appquery", slurl.getAppQuery(), "12345"); + ensure_equals("grid2", "util.aditi.lindenlab.com", slurl.getGrid()); + + LLGridManager::getInstance()->setGridChoice("foo.bar.com"); + slurl = LLSLURL("secondlife:///secondlife/myregion/1/2/3"); + ensure_equals("/: slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); + ensure_equals("location", slurl.getType(), LLSLURL::LOCATION); + ensure_equals("region" , "myregion", slurl.getRegion()); + ensure_equals("grid3", "util.agni.lindenlab.com", slurl.getGrid()); + + slurl = LLSLURL("secondlife://Aditi/secondlife/myregion/1/2/3"); + ensure_equals("/: slurl, region + coords - type", slurl.getType(), LLSLURL::LOCATION); + ensure_equals("location", slurl.getType(), LLSLURL::LOCATION); + ensure_equals("region" , "myregion", slurl.getRegion()); + ensure_equals("grid4", "util.aditi.lindenlab.com", slurl.getGrid()); + + slurl = LLSLURL("https://my.grid.com/app/foo/bar?12345"); + ensure_equals("app", slurl.getType(), LLSLURL::APP); + ensure_equals("appcmd", slurl.getAppCmd(), "foo"); + ensure_equals("apppath", slurl.getAppPath().size(), 1); + ensure_equals("apppath2", slurl.getAppPath()[0].asString(), "bar"); + ensure_equals("appquery", slurl.getAppQuery(), "12345"); + + } + + // construction from grid/region/vector combos + template<> template<> + void slurlTestObject::test<2>() + { + LLSLURL slurl = LLSLURL("mygrid.com", "my region"); + ensure_equals("grid/region - type", slurl.getType(), LLSLURL::LOCATION); + ensure_equals("grid/region", slurl.getSLURLString(), + "https://mygrid.com/region/my%20region/128/128/0"); + + slurl = LLSLURL("mygrid.com", "my region", LLVector3(1,2,3)); + ensure_equals("grid/region/vector - type", slurl.getType(), LLSLURL::LOCATION); + ensure_equals(" grid/region/vector", slurl.getSLURLString(), + "https://mygrid.com/region/my%20region/1/2/3"); + + LLGridManager::getInstance()->setGridChoice("foo.bar.com.bar"); + slurl = LLSLURL("my region", LLVector3(1,2,3)); + ensure_equals("grid/region/vector - type", slurl.getType(), LLSLURL::LOCATION); + ensure_equals(" grid/region/vector", slurl.getSLURLString(), + "https://foo.bar.com.bar/region/my%20region/1/2/3"); + + LLGridManager::getInstance()->setGridChoice("util.agni.lindenlab.com"); + slurl = LLSLURL("my region", LLVector3(1,2,3)); + ensure_equals("default grid/region/vector - type", slurl.getType(), LLSLURL::LOCATION); + ensure_equals(" default grid/region/vector", slurl.getSLURLString(), + "http://slurl.com/secondlife/my%20region/1/2/3"); + + } + // Accessors + template<> template<> + void slurlTestObject::test<3>() + { + LLSLURL slurl = LLSLURL("https://my.grid.com/region/my%20region/1/2/3"); + ensure_equals("login string", slurl.getLoginString(), "uri:my region&1&2&3"); + ensure_equals("location string", slurl.getLocationString(), "my region/1/2/3"); + ensure_equals("grid", slurl.getGrid(), "my.grid.com"); + ensure_equals("region", slurl.getRegion(), "my region"); + ensure_equals("position", slurl.getPosition(), LLVector3(1, 2, 3)); + + } +} -- cgit v1.3