llmath::llround->ll_round LL_ICC->LL_INTELC Add llpredicate Add LL_CPP11 macro Remove llhash Update llinitparam, llsd and all relatives of it.
568 lines
17 KiB
C++
568 lines
17 KiB
C++
/**
|
|
* @file llurlsimstring.cpp (was llsimurlstring.cpp)
|
|
* @brief Handles "SLURL fragments" like Ahern/123/45 for
|
|
* startup processing, login screen, prefs, etc.
|
|
*
|
|
* $LicenseInfo:firstyear=2010&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 "llslurl.h"
|
|
|
|
#include "llpanellogin.h"
|
|
#include "llviewercontrol.h"
|
|
#include "llviewernetwork.h"
|
|
#include "llfiltersd2xmlrpc.h"
|
|
#include "curl/curl.h"
|
|
#include "hippogridmanager.h"
|
|
#include "llworldmap.h" // Variable size regions
|
|
|
|
const char* LLSLURL::SLURL_HTTP_SCHEME = "http";
|
|
const char* LLSLURL::SLURL_HTTPS_SCHEME = "https";
|
|
const char* LLSLURL::SLURL_SECONDLIFE_SCHEME = "secondlife";
|
|
const char* LLSLURL::SLURL_SECONDLIFE_PATH = "secondlife";
|
|
const char* LLSLURL::SLURL_COM = "slurl.com";
|
|
// For DnD - even though www.slurl.com redirects to slurl.com in a browser, you can copy and drag
|
|
// text with www.slurl.com or a link explicitly pointing at www.slurl.com so testing for this
|
|
// version is required also.
|
|
|
|
const char* LLSLURL::WWW_SLURL_COM = "www.slurl.com";
|
|
const char* LLSLURL::MAPS_SECONDLIFE_COM = "maps.secondlife.com";
|
|
const char* LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME = "x-grid-location-info";
|
|
const char* LLSLURL::SLURL_APP_PATH = "app";
|
|
const char* LLSLURL::SLURL_REGION_PATH = "region";
|
|
const char* LLSLURL::SIM_LOCATION_HOME = "home";
|
|
const char* LLSLURL::SIM_LOCATION_LAST = "last";
|
|
|
|
const std::string MAIN_GRID_SLURL_BASE = "http://maps.secondlife.com/secondlife/";
|
|
const std::string SYSTEM_GRID_APP_SLURL_BASE = "secondlife:///app";
|
|
|
|
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";
|
|
|
|
#define MAINGRID "secondlife"
|
|
// resolve a simstring from a slurl
|
|
LLSLURL::LLSLURL(const std::string& slurl)
|
|
{
|
|
// by default we go to agni.
|
|
mType = INVALID;
|
|
|
|
if(slurl == SIM_LOCATION_HOME)
|
|
{
|
|
mType = HOME_LOCATION;
|
|
}
|
|
else if(slurl.empty() || (slurl == SIM_LOCATION_LAST))
|
|
{
|
|
mType = LAST_LOCATION;
|
|
}
|
|
else
|
|
{
|
|
LLURI slurl_uri;
|
|
// parse the slurl as a uri
|
|
if(slurl.find(':') == std::string::npos)
|
|
{
|
|
// There may be no scheme ('secondlife:' etc.) passed in. In that case
|
|
// we want to normalize the slurl by putting the appropriate scheme
|
|
// in front of the slurl. So, we grab the appropriate slurl base
|
|
// from the grid manager which may be http://slurl.com/secondlife/ for maingrid, or
|
|
// https://<hostname>/region/ for Standalone grid (the word region, not the region name)
|
|
// these slurls are typically passed in from the 'starting location' box on the login panel,
|
|
// where the user can type in <regionname>/<x>/<y>/<z>
|
|
//std::string fixed_slurl = LLGridManager::getInstance()->getSLURLBase();
|
|
//Singu TODO: Implement LLHippoGridMgr::getSLURLBase some day. For now it's hardcoded.
|
|
std::string fixed_slurl;
|
|
|
|
if(gHippoGridManager->getCurrentGrid()->isSecondLife())
|
|
{
|
|
if(gHippoGridManager->getCurrentGrid()->isInProductionGrid())
|
|
fixed_slurl = MAIN_GRID_SLURL_BASE;
|
|
else
|
|
fixed_slurl = llformat(SYSTEM_GRID_SLURL_BASE, gHippoGridManager->getCurrentGridNick().c_str());
|
|
}
|
|
else
|
|
fixed_slurl = llformat(DEFAULT_SLURL_BASE, gHippoGridManager->getCurrentGridNick().c_str());
|
|
|
|
//std::string fixed_slurl = MAIN_GRID_SLURL_BASE;
|
|
|
|
// the slurl that was passed in might have a prepended /, or not. So,
|
|
// we strip off the prepended '/' so we don't end up with http://slurl.com/secondlife/<region>/<x>/<y>/<z>
|
|
// or some such.
|
|
|
|
if(slurl[0] == '/')
|
|
{
|
|
fixed_slurl += slurl.substr(1);
|
|
}
|
|
else
|
|
{
|
|
fixed_slurl += slurl;
|
|
}
|
|
// We then load the slurl into a LLURI form
|
|
slurl_uri = LLURI(fixed_slurl);
|
|
}
|
|
else
|
|
{
|
|
// as we did have a scheme, implying a URI style slurl, we
|
|
// simply parse it as a URI
|
|
slurl_uri = LLURI(slurl);
|
|
}
|
|
|
|
LLSD path_array = slurl_uri.pathArray();
|
|
|
|
// determine whether it's a maingrid URI or an Standalone/open style URI
|
|
// by looking at the scheme. If it's a 'secondlife:' slurl scheme or
|
|
// 'sl:' scheme, we know it's maingrid
|
|
|
|
// At the end of this if/else block, we'll have determined the grid,
|
|
// and the slurl type (APP or LOCATION)
|
|
if(slurl_uri.scheme() == LLSLURL::SLURL_SECONDLIFE_SCHEME)
|
|
{
|
|
// parse a maingrid style slurl. We know the grid is maingrid
|
|
// so grab it.
|
|
// A location slurl for maingrid (with the special schemes) can be in the form
|
|
// secondlife://<regionname>/<x>/<y>/<z>
|
|
// or
|
|
// secondlife://<Grid>/secondlife/<region>/<x>/<y>/<z>
|
|
// where if grid is empty, it specifies Agni
|
|
|
|
// An app style slurl for maingrid can be
|
|
// secondlife://<Grid>/app/<app parameters>
|
|
// where an empty grid implies Agni
|
|
|
|
// we'll start by checking the top of the 'path' which will be
|
|
// either 'app', 'secondlife', or <x>.
|
|
|
|
// default to maingrid
|
|
|
|
mGrid = MAINGRID;
|
|
|
|
if ((path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH) ||
|
|
(path_array[0].asString() == LLSLURL::SLURL_APP_PATH))
|
|
{
|
|
// it's in the form secondlife://<grid>/(app|secondlife)
|
|
// so parse the grid name to derive the grid ID
|
|
if (!slurl_uri.hostName().empty())
|
|
{
|
|
if(slurl_uri.hostName() == "util.agni.lindenlab.com")
|
|
mGrid = MAINGRID;
|
|
else if(slurl_uri.hostName() == "util.aditi.lindenlab.com")
|
|
mGrid = "secondlife_beta";
|
|
else
|
|
{
|
|
HippoGridInfo* grid = gHippoGridManager->getGrid(slurl_uri.hostName());
|
|
mGrid = grid ? grid->getGridNick() : gHippoGridManager->getDefaultGridNick();
|
|
}
|
|
}
|
|
else if(path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH)
|
|
{
|
|
// If the slurl is in the form secondlife:///secondlife/<region> form,
|
|
// then we are in fact on maingrid.
|
|
mGrid = MAINGRID;
|
|
}
|
|
else if(path_array[0].asString() == LLSLURL::SLURL_APP_PATH)
|
|
{
|
|
// for app style slurls, where no grid name is specified, assume the currently
|
|
// selected or logged in grid.
|
|
mGrid = gHippoGridManager->getCurrentGridNick();
|
|
}
|
|
|
|
if(mGrid.empty())
|
|
{
|
|
// we couldn't find the grid in the grid manager, so bail
|
|
LL_WARNS("AppInit")<<"unable to find grid"<<LL_ENDL;
|
|
return;
|
|
}
|
|
// set the type as appropriate.
|
|
if(path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH)
|
|
{
|
|
mType = LOCATION;
|
|
}
|
|
else
|
|
{
|
|
mType = APP;
|
|
}
|
|
path_array.erase(0);
|
|
}
|
|
else
|
|
{
|
|
// it wasn't a /secondlife/<region> or /app/<params>, so it must be secondlife://<region>
|
|
// therefore the hostname will be the region name, and it's a location type
|
|
mType = LOCATION;
|
|
// 'normalize' it so the region name is in fact the head of the path_array
|
|
path_array.insert(0, slurl_uri.hostName());
|
|
}
|
|
}
|
|
else if((slurl_uri.scheme() == LLSLURL::SLURL_HTTP_SCHEME) ||
|
|
(slurl_uri.scheme() == LLSLURL::SLURL_HTTPS_SCHEME) ||
|
|
(slurl_uri.scheme() == LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME))
|
|
{
|
|
// We're dealing with either a Standalone style slurl or slurl.com slurl
|
|
if ((slurl_uri.hostName() == LLSLURL::SLURL_COM) ||
|
|
(slurl_uri.hostName() == LLSLURL::WWW_SLURL_COM) ||
|
|
(slurl_uri.hostName() == LLSLURL::MAPS_SECONDLIFE_COM))
|
|
{
|
|
// slurl.com implies maingrid
|
|
mGrid = MAINGRID;
|
|
}
|
|
else
|
|
{
|
|
// Don't try to match any old http://<host>/ URL as a SLurl.
|
|
// SLE SLurls will have the grid hostname in the URL, so only
|
|
// match http URLs if the hostname matches the grid hostname
|
|
// (or its a slurl.com or maps.secondlife.com URL).
|
|
if ((slurl_uri.scheme() == LLSLURL::SLURL_HTTP_SCHEME ||
|
|
slurl_uri.scheme() == LLSLURL::SLURL_HTTPS_SCHEME) &&
|
|
slurl_uri.hostName() != gHippoGridManager->getCurrentGridNick())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// As it's a Standalone grid/open, we will always have a hostname, as Standalone/open style
|
|
// urls are properly formed, unlike the stinky maingrid style
|
|
mGrid = slurl_uri.hostName();
|
|
}
|
|
if (path_array.size() == 0)
|
|
{
|
|
// um, we need a path...
|
|
return;
|
|
}
|
|
|
|
// we need to normalize the urls so
|
|
// the path portion starts with the 'command' that we want to do
|
|
// it can either be region or app.
|
|
if ((path_array[0].asString() == LLSLURL::SLURL_REGION_PATH) ||
|
|
(path_array[0].asString() == LLSLURL::SLURL_SECONDLIFE_PATH))
|
|
{
|
|
// strip off 'region' or 'secondlife'
|
|
path_array.erase(0);
|
|
// it's a location
|
|
mType = LOCATION;
|
|
}
|
|
else if (path_array[0].asString() == LLSLURL::SLURL_APP_PATH)
|
|
{
|
|
mType = APP;
|
|
path_array.erase(0);
|
|
// leave app appended.
|
|
}
|
|
else
|
|
{
|
|
// not a valid https/http/x-grid-location-info slurl, so it'll likely just be a URL
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// invalid scheme, so bail
|
|
return;
|
|
}
|
|
|
|
|
|
if(path_array.size() == 0)
|
|
{
|
|
// we gotta have some stuff after the specifier as to whether it's a region or command
|
|
return;
|
|
}
|
|
|
|
// now that we know whether it's an app slurl or a location slurl,
|
|
// parse the slurl into the proper data structures.
|
|
if(mType == APP)
|
|
{
|
|
// grab the app command type and strip it (could be a command to jump somewhere,
|
|
// or whatever )
|
|
mAppCmd = path_array[0].asString();
|
|
path_array.erase(0);
|
|
|
|
// Grab the parameters
|
|
mAppPath = path_array;
|
|
// and the query
|
|
mAppQuery = slurl_uri.query();
|
|
mAppQueryMap = slurl_uri.queryMap();
|
|
return;
|
|
}
|
|
else if(mType == LOCATION)
|
|
{
|
|
// at this point, head of the path array should be [ <region>, <x>, <y>, <z> ] where x, y and z
|
|
// are collectively optional
|
|
// are optional
|
|
mRegion = LLURI::unescape(path_array[0].asString());
|
|
path_array.erase(0);
|
|
|
|
// parse the x, y, and optionally z
|
|
if(path_array.size() >= 2)
|
|
{
|
|
|
|
mPosition = LLVector3(path_array); // this construction handles LLSD without all components (values default to 0.f)
|
|
if((F32(mPosition[VX]) < 0.f) ||
|
|
(mPosition[VX] > 8192.f/*REGION_WIDTH_METERS*/) ||
|
|
(F32(mPosition[VY]) < 0.f) ||
|
|
(mPosition[VY] > 8192.f/*REGION_WIDTH_METERS*/) ||
|
|
(F32(mPosition[VZ]) < 0.f) ||
|
|
(mPosition[VZ] > 8192.f/*REGION_HEIGHT_METERS*/))
|
|
{
|
|
mType = INVALID;
|
|
return;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// if x, y and z were not fully passed in, go to the middle of the region.
|
|
// teleport will adjust the actual location to make sure you're on the ground
|
|
// and such
|
|
mPosition = LLVector3(REGION_WIDTH_METERS/2, REGION_WIDTH_METERS/2, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Create a slurl for the middle of the region
|
|
LLSLURL::LLSLURL(const std::string& grid,
|
|
const std::string& region)
|
|
{
|
|
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 = ll_round( (F32)position[VX] );
|
|
S32 y = ll_round( (F32)position[VY] );
|
|
S32 z = ll_round( (F32)position[VZ] );
|
|
mType = LOCATION;
|
|
mPosition = LLVector3(x, y, z);
|
|
}
|
|
|
|
|
|
// create a simstring
|
|
LLSLURL::LLSLURL(const std::string& region,
|
|
const LLVector3& position)
|
|
{
|
|
*this = LLSLURL(gHippoGridManager->getCurrentGridNick(),
|
|
region, position);
|
|
}
|
|
|
|
// create a slurl from a global position
|
|
LLSLURL::LLSLURL(const std::string& grid,
|
|
const std::string& region,
|
|
const LLVector3d& global_position)
|
|
{
|
|
HippoGridInfo* gridp = gHippoGridManager->getGrid(grid);
|
|
LLVector3 pos(global_position);
|
|
if (LLSimInfo* sim = LLWorldMap::getInstance()->simInfoFromPosGlobal(global_position)) // Variable size regions, we need to fmod against their proper dimensions, not 256
|
|
{
|
|
pos[VX] = fmod(pos[VX], sim->getSizeX());
|
|
pos[VY] = fmod(pos[VY], sim->getSizeY());
|
|
}
|
|
*this = LLSLURL(gridp ? gridp->getGridNick() : gHippoGridManager->getDefaultGridNick(),
|
|
region, pos);
|
|
}
|
|
|
|
// create a slurl from a global position
|
|
LLSLURL::LLSLURL(const std::string& region,
|
|
const LLVector3d& global_position)
|
|
{
|
|
*this = LLSLURL(gHippoGridManager->getCurrentGridNick(),
|
|
region, global_position);
|
|
}
|
|
|
|
LLSLURL::LLSLURL(const std::string& command, const LLUUID&id, const std::string& verb)
|
|
{
|
|
mType = APP;
|
|
mAppCmd = command;
|
|
mAppPath = LLSD::emptyArray();
|
|
mAppPath.append(LLSD(id));
|
|
mAppPath.append(LLSD(verb));
|
|
}
|
|
|
|
|
|
std::string LLSLURL::getSLURLString() const
|
|
{
|
|
switch(mType)
|
|
{
|
|
case HOME_LOCATION:
|
|
return SIM_LOCATION_HOME;
|
|
case LAST_LOCATION:
|
|
return SIM_LOCATION_LAST;
|
|
case LOCATION:
|
|
{
|
|
// lookup the grid
|
|
S32 x = ll_round( (F32)mPosition[VX] );
|
|
S32 y = ll_round( (F32)mPosition[VY] );
|
|
S32 z = ll_round( (F32)mPosition[VZ] );
|
|
//return LLGridManager::getInstance()->getSLURLBase(mGrid) +
|
|
//Singu TODO: Implement LLHippoGridMgr::getSLURLBase some day. For now it's hardcoded.
|
|
std::string fixed_slurl;
|
|
if(gHippoGridManager->getCurrentGrid()->isSecondLife())
|
|
{
|
|
if(gHippoGridManager->getCurrentGrid()->isInProductionGrid())
|
|
fixed_slurl = MAIN_GRID_SLURL_BASE;
|
|
else
|
|
fixed_slurl = llformat(SYSTEM_GRID_SLURL_BASE, gHippoGridManager->getCurrentGridNick().c_str());
|
|
}
|
|
else
|
|
fixed_slurl = llformat(DEFAULT_SLURL_BASE, gHippoGridManager->getCurrentGridNick().c_str());
|
|
return fixed_slurl +
|
|
LLURI::escape(mRegion) + llformat("/%d/%d/%d",x,y,z);
|
|
}
|
|
case APP:
|
|
{
|
|
std::ostringstream app_url;
|
|
//app_url << LLGridManager::getInstance()->getAppSLURLBase() << "/" << mAppCmd;
|
|
//Singu TODO: Implement LLHippoGridMgr::getAppSLURLBase some day. For now it's hardcoded.
|
|
if(gHippoGridManager->getCurrentGrid()->isSecondLife())
|
|
app_url << SYSTEM_GRID_APP_SLURL_BASE << "/" << mAppCmd;
|
|
else
|
|
app_url << llformat(DEFAULT_APP_SLURL_BASE, gHippoGridManager->getCurrentGridNick().c_str()) << "/" << 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();
|
|
}
|
|
}
|
|
|
|
std::string LLSLURL::getLoginString() const
|
|
{
|
|
|
|
std::stringstream unescaped_start;
|
|
switch(mType)
|
|
{
|
|
case LOCATION:
|
|
unescaped_start << "uri:"
|
|
<< mRegion << "&"
|
|
<< ll_round(mPosition[0]) << "&"
|
|
<< ll_round(mPosition[1]) << "&"
|
|
<< ll_round(mPosition[2]);
|
|
break;
|
|
case HOME_LOCATION:
|
|
unescaped_start << "home";
|
|
break;
|
|
case LAST_LOCATION:
|
|
unescaped_start << "last";
|
|
break;
|
|
default:
|
|
LL_WARNS("AppInit") << "Unexpected SLURL type ("<<(int)mType <<")for login string"<< LL_ENDL;
|
|
break;
|
|
}
|
|
return xml_escape_string(unescaped_start.str());
|
|
}
|
|
|
|
bool LLSLURL::operator==(const LLSLURL& rhs)
|
|
{
|
|
if(rhs.mType != mType) return false;
|
|
switch(mType)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
bool LLSLURL::operator !=(const LLSLURL& rhs)
|
|
{
|
|
return !(*this == rhs);
|
|
}
|
|
|
|
std::string LLSLURL::getLocationString() const
|
|
{
|
|
return llformat("%s/%d/%d/%d",
|
|
mRegion.c_str(),
|
|
(int)ll_round(mPosition[0]),
|
|
(int)ll_round(mPosition[1]),
|
|
(int)ll_round(mPosition[2]));
|
|
}
|
|
|
|
// static
|
|
const std::string LLSLURL::typeName[NUM_SLURL_TYPES] =
|
|
{
|
|
"INVALID",
|
|
"LOCATION",
|
|
"HOME_LOCATION",
|
|
"LAST_LOCATION",
|
|
"APP",
|
|
"HELP"
|
|
};
|
|
|
|
std::string LLSLURL::getTypeString(SLURL_TYPE type)
|
|
{
|
|
std::string name;
|
|
if ( type >= INVALID && type < NUM_SLURL_TYPES )
|
|
{
|
|
name = LLSLURL::typeName[type];
|
|
}
|
|
else
|
|
{
|
|
name = llformat("Out of Range (%d)",type);
|
|
}
|
|
return name;
|
|
}
|
|
|
|
|
|
std::string LLSLURL::asString() const
|
|
{
|
|
std::ostringstream result;
|
|
result
|
|
<< " mType: " << LLSLURL::getTypeString(mType)
|
|
<< " mGrid: " + getGrid()
|
|
<< " mRegion: " + getRegion()
|
|
<< " mPosition: " << mPosition
|
|
<< " mAppCmd:" << getAppCmd()
|
|
<< " mAppPath:" + getAppPath().asString()
|
|
<< " mAppQueryMap:" + getAppQueryMap().asString()
|
|
<< " mAppQuery: " + getAppQuery()
|
|
;
|
|
|
|
return result.str();
|
|
}
|
|
|