/** * @file llviewernetwork.cpp * @author James Cook, Richard Nelson * @brief Networking constants and globals for viewer. * * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" #include "llviewernetwork.h" #include "llviewercontrol.h" #include "llsdserialize.h" #include "llsecapi.h" #include "lltrans.h" #include "llweb.h" /// key used to store the grid, and the name attribute in the grid data const std::string GRID_VALUE = "keyname"; /// the value displayed in the grid selector menu, and other human-oriented text const std::string GRID_LABEL_VALUE = "label"; /// the value used on the --grid command line argument const std::string GRID_ID_VALUE = "grid_login_id"; /// the url for the login cgi script const std::string GRID_LOGIN_URI_VALUE = "login_uri"; /// url base for update queries const std::string GRID_UPDATE_SERVICE_URL = "update_query_url_base"; /// const std::string GRID_HELPER_URI_VALUE = "helper_uri"; /// the splash page url const std::string GRID_LOGIN_PAGE_VALUE = "login_page"; /// url for the web profile site const std::string GRID_WEB_PROFILE_VALUE = "web_profile_url"; /// internal data on system grids const std::string GRID_IS_SYSTEM_GRID_VALUE = "system_grid"; /// whether this is single or double names const std::string GRID_LOGIN_IDENTIFIER_TYPES = "login_identifier_types"; // defines slurl formats associated with various grids. // we need to continue to support existing forms, as slurls // are shared between viewers that may not understand newer // forms. const std::string GRID_SLURL_BASE = "slurl_base"; const std::string GRID_APP_SLURL_BASE = "app_slurl_base"; const std::string DEFAULT_LOGIN_PAGE = "https://viewer-splash.secondlife.com/"; const std::string MAIN_GRID_LOGIN_URI = "https://login.agni.lindenlab.com/cgi-bin/login.cgi"; const std::string SL_UPDATE_QUERY_URL = "https://update.secondlife.com/update"; const std::string MAIN_GRID_SLURL_BASE = "http://maps.secondlife.com/secondlife/"; const std::string SYSTEM_GRID_APP_SLURL_BASE = "secondlife:///app"; const std::string MAIN_GRID_WEB_PROFILE_URL = "https://my.secondlife.com/"; const char* SYSTEM_GRID_SLURL_BASE = "secondlife://%s/secondlife/"; const char* DEFAULT_SLURL_BASE = "https://%s/region/"; const char* DEFAULT_APP_SLURL_BASE = "x-grid-location-info://%s/app"; LLGridManager::LLGridManager() : mIsInProductionGrid(false) { // 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"); LL_DEBUGS("GridManager")<<LL_ENDL; initialize(grid_file); } // // 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) // and the grids.xml file void LLGridManager::initialize(const std::string& grid_file) { // default grid list. // Don't move to a modifiable file for security reasons, mGrid.clear() ; // set to undefined mGridList = LLSD(); mGridFile = grid_file; // as we don't want an attacker to override our grid list // to point the default grid to an invalid grid addSystemGrid(LLTrans::getString("AgniGridLabel"), MAINGRID, MAIN_GRID_LOGIN_URI, "https://secondlife.com/helpers/", DEFAULT_LOGIN_PAGE, SL_UPDATE_QUERY_URL, MAIN_GRID_WEB_PROFILE_URL, "Agni"); addSystemGrid(LLTrans::getString("AditiGridLabel"), "util.aditi.lindenlab.com", "https://login.aditi.lindenlab.com/cgi-bin/login.cgi", "https://secondlife.aditi.lindenlab.com/helpers/", DEFAULT_LOGIN_PAGE, SL_UPDATE_QUERY_URL, "https://my.secondlife-beta.com/", "Aditi"); LLSD other_grids; llifstream llsd_xml; if (!grid_file.empty()) { LL_INFOS("GridManager")<<"Grid configuration file '"<<grid_file<<"'"<<LL_ENDL; llsd_xml.open( grid_file.c_str(), std::ios::in | std::ios::binary ); // parse through the gridfile, inserting grids into the list unless // they overwrite an existing 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; std::string existingGrid = getGrid(grid); if (mGridList.has(key_name) || !existingGrid.empty()) { LL_WARNS("GridManager") << "Cannot override existing grid '" << key_name << "'; ignoring definition from '"<<grid_file<<"'" << LL_ENDL; } else if ( addGrid(grid) ) { LL_INFOS("GridManager") << "added grid '"<<key_name<<"'"<<LL_ENDL; } else { LL_WARNS("GridManager") << "failed to add invalid grid '"<<key_name<<"'"<<LL_ENDL; } } llsd_xml.close(); } else { LL_WARNS("GridManager")<<"Failed to parse grid configuration '"<<grid_file<<"'"<<LL_ENDL; } } else { LL_WARNS("GridManager")<<"Failed to open grid configuration '"<<grid_file<<"'"<<LL_ENDL; } } else { LL_DEBUGS("GridManager")<<"no grid file specified"<<LL_ENDL; } // load a grid from the command line. // if the actual grid name is specified from the command line, // set it as the 'selected' grid. std::string cmd_line_grid = gSavedSettings.getString("CmdLineGridChoice"); if(!cmd_line_grid.empty()) { // try to find the grid assuming the command line parameter is // the case-insensitive 'label' of the grid. ie 'Agni' mGrid = getGrid(cmd_line_grid); if(mGrid.empty()) { LL_WARNS("GridManager")<<"Unknown grid '"<<cmd_line_grid<<"'"<<LL_ENDL; } else { LL_INFOS("GridManager")<<"Command line specified '"<<cmd_line_grid<<"': "<<mGrid<<LL_ENDL; } } else { // if a grid was not passed in via the command line, grab it from the CurrentGrid setting. // if there's no current grid, that's ok as it'll be either set by the value passed // in via the login uri if that's specified, or will default to maingrid std::string last_grid = gSavedSettings.getString("CurrentGrid"); if ( ! getGrid(last_grid).empty() ) { LL_INFOS("GridManager")<<"Using last grid: "<<last_grid<<LL_ENDL; mGrid = last_grid; } else { LL_INFOS("GridManager")<<"Last grid '"<<last_grid<<"' not configured"<<LL_ENDL; } } if(mGrid.empty()) { // no grid was specified so default to maingrid LL_INFOS("GridManager") << "Default grid to "<<MAINGRID<< LL_ENDL; mGrid = MAINGRID; } LLControlVariablePtr grid_control = gSavedSettings.getControl("CurrentGrid"); if (grid_control.notNull()) { grid_control->getSignal()->connect(boost::bind(&LLGridManager::updateIsInProductionGrid, this)); } // since above only triggers on changes, trigger the callback manually to initialize state updateIsInProductionGrid(); setGridChoice(mGrid); } LLGridManager::~LLGridManager() { } // // LLGridManager::addGrid - add a grid to the grid list, populating the needed values // if they're not populated yet. // bool LLGridManager::addGrid(LLSD& grid_data) { bool added = false; if (grid_data.isMap() && grid_data.has(GRID_VALUE)) { std::string grid = utf8str_tolower(grid_data[GRID_VALUE].asString()); if ( getGrid(grid_data[GRID_VALUE]).empty() && getGrid(grid).empty() ) { std::string grid_id = grid_data.has(GRID_ID_VALUE) ? grid_data[GRID_ID_VALUE].asString() : ""; if ( getGrid(grid_id).empty() ) { // populate the other values if they don't exist if (!grid_data.has(GRID_LABEL_VALUE)) { grid_data[GRID_LABEL_VALUE] = grid; } if (!grid_data.has(GRID_ID_VALUE)) { grid_data[GRID_ID_VALUE] = grid; } // if the grid data doesn't include any of the URIs, then // generate them from the grid, which should be a dns address if (!grid_data.has(GRID_LOGIN_URI_VALUE)) { grid_data[GRID_LOGIN_URI_VALUE] = LLSD::emptyArray(); grid_data[GRID_LOGIN_URI_VALUE].append(std::string("https://") + grid + "/cgi-bin/login.cgi"); } // Populate to the default values if (!grid_data.has(GRID_LOGIN_PAGE_VALUE)) { grid_data[GRID_LOGIN_PAGE_VALUE] = std::string("http://") + grid + "/app/login/"; } if (!grid_data.has(GRID_HELPER_URI_VALUE)) { grid_data[GRID_HELPER_URI_VALUE] = std::string("https://") + grid + "/helpers/"; } if (!grid_data.has(GRID_WEB_PROFILE_VALUE)) { grid_data[GRID_WEB_PROFILE_VALUE] = std::string("https://") + grid + "/"; } if (!grid_data.has(GRID_LOGIN_IDENTIFIER_TYPES)) { // non system grids and grids that haven't already been configured with values // get both types of credentials. grid_data[GRID_LOGIN_IDENTIFIER_TYPES] = LLSD::emptyArray(); grid_data[GRID_LOGIN_IDENTIFIER_TYPES].append(CRED_IDENTIFIER_TYPE_AGENT); grid_data[GRID_LOGIN_IDENTIFIER_TYPES].append(CRED_IDENTIFIER_TYPE_ACCOUNT); } LL_DEBUGS("GridManager") <<grid<<"\n" <<" id: "<<grid_data[GRID_ID_VALUE].asString()<<"\n" <<" label: "<<grid_data[GRID_LABEL_VALUE].asString()<<"\n" <<" login page: "<<grid_data[GRID_LOGIN_PAGE_VALUE].asString()<<"\n" <<" helper page: "<<grid_data[GRID_HELPER_URI_VALUE].asString()<<"\n" <<" web profile: "<<grid_data[GRID_WEB_PROFILE_VALUE].asString()<<"\n"; /* still in LL_DEBUGS */ for (LLSD::array_const_iterator login_uris = grid_data[GRID_LOGIN_URI_VALUE].beginArray(); login_uris != grid_data[GRID_LOGIN_URI_VALUE].endArray(); login_uris++) { LL_CONT << " login uri: "<<login_uris->asString()<<"\n"; } LL_CONT << LL_ENDL; mGridList[grid] = grid_data; added = true; } else { LL_WARNS("GridManager")<<"duplicate grid id'"<<grid_id<<"' ignored"<<LL_ENDL; } } else { LL_WARNS("GridManager")<<"duplicate grid name '"<<grid<<"' ignored"<<LL_ENDL; } } else { LL_WARNS("GridManager")<<"invalid grid definition ignored"<<LL_ENDL; } return added; } // // LLGridManager::addSystemGrid - helper for adding a system grid. void LLGridManager::addSystemGrid(const std::string& label, const std::string& name, const std::string& login_uri, const std::string& helper, const std::string& login_page, const std::string& update_url_base, const std::string& web_profile_url, const std::string& login_id) { LLSD grid = LLSD::emptyMap(); grid[GRID_VALUE] = name; grid[GRID_LABEL_VALUE] = label; grid[GRID_HELPER_URI_VALUE] = helper; grid[GRID_LOGIN_URI_VALUE] = LLSD::emptyArray(); grid[GRID_LOGIN_URI_VALUE].append(login_uri); grid[GRID_LOGIN_PAGE_VALUE] = login_page; grid[GRID_UPDATE_SERVICE_URL] = update_url_base; grid[GRID_WEB_PROFILE_VALUE] = web_profile_url; grid[GRID_IS_SYSTEM_GRID_VALUE] = true; grid[GRID_LOGIN_IDENTIFIER_TYPES] = LLSD::emptyArray(); grid[GRID_LOGIN_IDENTIFIER_TYPES].append(CRED_IDENTIFIER_TYPE_AGENT); grid[GRID_APP_SLURL_BASE] = SYSTEM_GRID_APP_SLURL_BASE; if (login_id.empty()) { grid[GRID_ID_VALUE] = name; } else { grid[GRID_ID_VALUE] = login_id; } if (name == std::string(MAINGRID)) { grid[GRID_SLURL_BASE] = MAIN_GRID_SLURL_BASE; } else { grid[GRID_SLURL_BASE] = llformat(SYSTEM_GRID_SLURL_BASE, grid[GRID_ID_VALUE].asString().c_str()); } addGrid(grid); } // return a list of grid name -> grid label mappings for UI purposes std::map<std::string, std::string> LLGridManager::getKnownGrids() { std::map<std::string, std::string> result; for(LLSD::map_iterator grid_iter = mGridList.beginMap(); grid_iter != mGridList.endMap(); grid_iter++) { result[grid_iter->first] = grid_iter->second[GRID_LABEL_VALUE].asString(); } return result; } void LLGridManager::setGridChoice(const std::string& grid) { // Set the grid choice based on a string. LL_DEBUGS("GridManager")<<"requested "<<grid<<LL_ENDL; std::string grid_name = getGrid(grid); // resolved either the name or the id to the name if(!grid_name.empty()) { LL_INFOS("GridManager")<<"setting "<<grid_name<<LL_ENDL; mGrid = grid_name; gSavedSettings.setString("CurrentGrid", grid_name); updateIsInProductionGrid(); } else { // the grid was not in the list of grids. LL_WARNS("GridManager")<<"unknown grid "<<grid<<LL_ENDL; } } std::string LLGridManager::getGrid( const std::string &grid ) { std::string grid_name; if (mGridList.has(grid)) { // the grid was the long name, so we're good, return it grid_name = grid; } else { // search the grid list for a grid with a matching id for(LLSD::map_iterator grid_iter = mGridList.beginMap(); grid_name.empty() && grid_iter != mGridList.endMap(); grid_iter++) { if (grid_iter->second.has(GRID_ID_VALUE)) { if (0 == (LLStringUtil::compareInsensitive(grid, grid_iter->second[GRID_ID_VALUE].asString()))) { // found a matching label, return this name grid_name = grid_iter->first; } } } } return grid_name; } std::string LLGridManager::getGridLabel(const std::string& grid) { std::string grid_label; std::string grid_name = getGrid(grid); if (!grid.empty()) { grid_label = mGridList[grid_name][GRID_LABEL_VALUE].asString(); } else { LL_WARNS("GridManager")<<"invalid grid '"<<grid<<"'"<<LL_ENDL; } LL_DEBUGS("GridManager")<<"returning "<<grid_label<<LL_ENDL; return grid_label; } std::string LLGridManager::getGridId(const std::string& grid) { std::string grid_id; std::string grid_name = getGrid(grid); if (!grid.empty()) { grid_id = mGridList[grid_name][GRID_ID_VALUE].asString(); } else { LL_WARNS("GridManager")<<"invalid grid '"<<grid<<"'"<<LL_ENDL; } LL_DEBUGS("GridManager")<<"returning "<<grid_id<<LL_ENDL; return grid_id; } void LLGridManager::getLoginURIs(const std::string& grid, std::vector<std::string>& uris) { uris.clear(); std::string grid_name = getGrid(grid); if (!grid_name.empty()) { if (mGridList[grid_name][GRID_LOGIN_URI_VALUE].isArray()) { for (LLSD::array_iterator llsd_uri = mGridList[grid_name][GRID_LOGIN_URI_VALUE].beginArray(); llsd_uri != mGridList[grid_name][GRID_LOGIN_URI_VALUE].endArray(); llsd_uri++) { uris.push_back(llsd_uri->asString()); } } else { uris.push_back(mGridList[grid_name][GRID_LOGIN_URI_VALUE].asString()); } } else { LL_WARNS("GridManager")<<"invalid grid '"<<grid<<"'"<<LL_ENDL; } } void LLGridManager::getLoginURIs(std::vector<std::string>& uris) { getLoginURIs(mGrid, uris); } std::string LLGridManager::getHelperURI(const std::string& grid) { std::string helper_uri; std::string grid_name = getGrid(grid); if (!grid_name.empty()) { helper_uri = mGridList[grid_name][GRID_HELPER_URI_VALUE].asString(); } else { LL_WARNS("GridManager")<<"invalid grid '"<<grid<<"'"<<LL_ENDL; } LL_DEBUGS("GridManager")<<"returning "<<helper_uri<<LL_ENDL; return helper_uri; } std::string LLGridManager::getLoginPage(const std::string& grid) { std::string grid_login_page; std::string grid_name = getGrid(grid); if (!grid_name.empty()) { grid_login_page = mGridList[grid_name][GRID_LOGIN_PAGE_VALUE].asString(); } else { LL_WARNS("GridManager")<<"invalid grid '"<<grid<<"'"<<LL_ENDL; } return grid_login_page; } std::string LLGridManager::getLoginPage() { std::string login_page = mGridList[mGrid][GRID_LOGIN_PAGE_VALUE].asString(); LL_DEBUGS("GridManager")<<"returning "<<login_page<<LL_ENDL; return login_page; } std::string LLGridManager::getWebProfileURL(const std::string& grid) { std::string web_profile_url; std::string grid_name = getGrid(grid); if (!grid_name.empty()) { web_profile_url = mGridList[grid_name][GRID_WEB_PROFILE_VALUE].asString(); } else { LL_WARNS("GridManager")<<"invalid grid '"<<grid<<"'"<<LL_ENDL; } return web_profile_url; } void LLGridManager::getLoginIdentifierTypes(LLSD& idTypes) { idTypes = mGridList[mGrid][GRID_LOGIN_IDENTIFIER_TYPES]; } std::string LLGridManager::getGridLoginID() { return mGridList[mGrid][GRID_ID_VALUE]; } std::string LLGridManager::getUpdateServiceURL() { std::string update_url_base = gSavedSettings.getString("CmdLineUpdateService");; if ( !update_url_base.empty() ) { LL_INFOS("UpdaterService","GridManager") << "Update URL base overridden from command line: " << update_url_base << LL_ENDL; } else if ( mGridList[mGrid].has(GRID_UPDATE_SERVICE_URL) ) { update_url_base = mGridList[mGrid][GRID_UPDATE_SERVICE_URL].asString(); } else { LL_WARNS("UpdaterService","GridManager") << "The grid property '" << GRID_UPDATE_SERVICE_URL << "' is not defined for the grid '" << mGrid << "'" << LL_ENDL; } return update_url_base; } void LLGridManager::updateIsInProductionGrid() { mIsInProductionGrid = false; // *NOTE:Mani This used to compare GRID_INFO_AGNI to gGridChoice, // but it seems that loginURI trumps that. std::vector<std::string> uris; getLoginURIs(uris); if (uris.empty()) { mIsInProductionGrid = true; } else { for ( std::vector<std::string>::iterator uri_it = uris.begin(); ! mIsInProductionGrid && uri_it != uris.end(); uri_it++ ) { if( MAIN_GRID_LOGIN_URI == *uri_it ) { mIsInProductionGrid = true; } } } } bool LLGridManager::isInProductionGrid() { return mIsInProductionGrid; } bool LLGridManager::isSystemGrid(const std::string& grid) { std::string grid_name = getGrid(grid); return ( !grid_name.empty() && mGridList.has(grid) && mGridList[grid].has(GRID_IS_SYSTEM_GRID_VALUE) && mGridList[grid][GRID_IS_SYSTEM_GRID_VALUE].asBoolean() ); } // build a slurl for the given region within the selected grid std::string LLGridManager::getSLURLBase(const std::string& grid) { std::string grid_base = ""; std::string grid_name = getGrid(grid); if( ! grid_name.empty() && mGridList.has(grid_name) ) { if (mGridList[grid_name].has(GRID_SLURL_BASE)) { grid_base = mGridList[grid_name][GRID_SLURL_BASE].asString(); } else { grid_base = llformat(DEFAULT_SLURL_BASE, grid_name.c_str()); } } LL_DEBUGS("GridManager")<<"returning '"<<grid_base<<"'"<<LL_ENDL; return grid_base; } // build a slurl for the given region within the selected grid std::string LLGridManager::getAppSLURLBase(const std::string& grid) { std::string grid_base = ""; std::string grid_name = getGrid(grid); if(!grid_name.empty() && mGridList.has(grid)) { if (mGridList[grid].has(GRID_APP_SLURL_BASE)) { grid_base = mGridList[grid][GRID_APP_SLURL_BASE].asString(); } else { grid_base = llformat(DEFAULT_APP_SLURL_BASE, grid_name.c_str()); } } LL_DEBUGS("GridManager")<<"returning '"<<grid_base<<"'"<<LL_ENDL; return grid_base; }