llslurl.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954
  1. /**
  2. * @file llslurl.cpp
  3. * @brief Handles "SLURL fragments" like Ahern/123/45 for startup processing,
  4. * login screen, prefs, etc.
  5. *
  6. * $LicenseInfo:firstyear=2010&license=viewergpl$
  7. *
  8. * Copyright (c) 2010, Linden Research, Inc.
  9. *
  10. * Second Life Viewer Source Code
  11. * The source code in this file ("Source Code") is provided by Linden Lab
  12. * to you under the terms of the GNU General Public License, version 2.0
  13. * ("GPL"), unless you have obtained a separate licensing agreement
  14. * ("Other License"), formally executed by you and Linden Lab. Terms of
  15. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  16. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  17. *
  18. * There are special exceptions to the terms and conditions of the GPL as
  19. * it is applied to this Source Code. View the full text of the exception
  20. * in the file doc/FLOSS-exception.txt in this software distribution, or
  21. * online at
  22. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  23. *
  24. * By copying, modifying or distributing this software, you acknowledge
  25. * that you have read and understood your obligations described above,
  26. * and agree to abide by those obligations.
  27. *
  28. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  29. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  30. * COMPLETENESS OR PERFORMANCE.
  31. * $/LicenseInfo$
  32. */
  33. #include "llviewerprecompiledheaders.h"
  34. #include <regex>
  35. #include "curl/curl.h"
  36. #include "llslurl.h"
  37. #include "llcachename.h"
  38. #include "llexperiencecache.h"
  39. #include "llgridmanager.h"
  40. #include "llfloaterchat.h"
  41. #include "llnotify.h"
  42. //MK
  43. #include "mkrlinterface.h"
  44. //mk
  45. #include "llviewercontrol.h"
  46. #include "llworldmap.h" // Variable region size support
  47. const char* LLSLURL::SLURL_SECONDLIFE_SCHEME = "secondlife";
  48. const char* LLSLURL::SLURL_HOP_SCHEME = "hop";
  49. const char* LLSLURL::SLURL_X_GRID_INFO_SCHEME = "x-grid-info";
  50. const char* LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME = "x-grid-location-info";
  51. const char* LLSLURL::SLURL_HTTP_SCHEME = "http";
  52. const char* LLSLURL::SLURL_HTTPS_SCHEME = "https";
  53. const char* LLSLURL::SLURL_SECONDLIFE_PATH = "secondlife";
  54. const char* LLSLURL::SLURL_COM = "slurl.com";
  55. // For DnD - even though www.slurl.com redirects to slurl.com in a browser, you
  56. // can copy and drag text with www.slurl.com or a link explicitly pointing at
  57. // www.slurl.com so testing for this version is required also:
  58. const char* LLSLURL::WWW_SLURL_COM = "www.slurl.com";
  59. const char* LLSLURL::MAPS_SECONDLIFE_COM = "maps.secondlife.com";
  60. const char* LLSLURL::SLURL_APP_PATH = "app";
  61. const char* LLSLURL::SLURL_REGION_PATH = "region";
  62. const char* LLSLURL::SIM_LOCATION_HOME = "home";
  63. const char* LLSLURL::SIM_LOCATION_LAST = "last";
  64. const std::string MAIN_GRID_SLURL_BASE = "http://maps.secondlife.com/secondlife/";
  65. uuid_list_t LLSLURL::sAvatarUUIDs;
  66. uuid_list_t LLSLURL::sGroupUUIDs;
  67. uuid_list_t LLSLURL::sExperienceUUIDs;
  68. uuid_list_t LLSLURL::sObjectsUUIDs;
  69. LLSLURL::slurls_map_t LLSLURL::sPendingSLURLs;
  70. // Regular expression used to match app/agent SLURLs
  71. #define APP_AGENT_REGEX "(((x-grid-info|x-grid-location-info)://[-\\w\\.]+/app/agent/)|((secondlife|hop):///app/agent/))[\\da-f-]+/"
  72. static std::regex sAgentPattern =
  73. std::regex(APP_AGENT_REGEX, std::regex::ECMAScript | std::regex::icase);
  74. // Regular expression used to match app/group SLURLs
  75. #define APP_GROUP_REGEX "(((x-grid-info|x-grid-location-info)://[-\\w\\.]+/app/group/)|((secondlife|hop):///app/group/))[\\da-f-]+/about"
  76. static std::regex sGroupPattern =
  77. std::regex(APP_GROUP_REGEX, std::regex::ECMAScript | std::regex::icase);
  78. // Regular expression used to match app/experience SLURLs
  79. #define APP_EXP_REGEX "(((x-grid-info|x-grid-location-info)://[-\\w\\.]+/app/experience/)|((secondlife|hop):///app/experience/))[\\da-f-]+/profile"
  80. static std::regex sExperiencePattern =
  81. std::regex(APP_EXP_REGEX, std::regex::ECMAScript | std::regex::icase);
  82. // Regular expression used to match app/objectim SLURLs
  83. #define APP_OBJ_REGEX "(((x-grid-info|x-grid-location-info)://[-\\w\\.]+/app/objectim/)|((secondlife|hop):///app/objectim/))[\\da-f-]+[/\?]"
  84. static std::regex sObjectIMPattern =
  85. std::regex(APP_OBJ_REGEX, std::regex::ECMAScript | std::regex::icase);
  86. // Helper function
  87. bool match_regex(const char* text, std::regex regex, size_t& start,
  88. size_t& end)
  89. {
  90. std::cmatch result;
  91. try
  92. {
  93. if (!std::regex_search(text, result, regex))
  94. {
  95. return false;
  96. }
  97. }
  98. catch (std::regex_error& e)
  99. {
  100. llwarns << "Regex error: " << e.what() << llendl;
  101. return false;
  102. }
  103. // Return the first/last character offset for the matched substring
  104. start = result[0].first - text;
  105. end = result[0].second - text - 1;
  106. // We allow certain punctuation to terminate a Url but not match it,
  107. // e.g., "http://foo.com/." should just match "http://foo.com/"
  108. if (text[end] == '.' || text[end] == ',')
  109. {
  110. --end;
  111. }
  112. // Ignore a terminating ')' when Url contains no matching '('
  113. else if (text[end] == ')' &&
  114. std::string(text + start,
  115. end - start).find('(') == std::string::npos)
  116. {
  117. --end;
  118. }
  119. return end > start;
  120. }
  121. //static
  122. uuid_list_t LLSLURL::findSLURLs(const std::string& txt)
  123. {
  124. uuid_list_t result;
  125. if (txt.size() < 66 || txt.find("://") == std::string::npos)
  126. {
  127. // If no chance of an SLURL in the text, return right now
  128. return result;
  129. }
  130. std::string temp;
  131. size_t start = 0;
  132. size_t end = 0;
  133. LLUUID slurl_id;
  134. // Search for avatar name SLURLs
  135. std::string text = txt;
  136. while (match_regex(text.c_str(), sAgentPattern, start, end))
  137. {
  138. S32 uuid_start = end - 36;
  139. if (uuid_start >= 0)
  140. {
  141. slurl_id.set(text.substr(uuid_start, 36));
  142. if (slurl_id.notNull())
  143. {
  144. bool translate = true;
  145. temp = text.substr(end);
  146. if (temp.find("/completename") == 0)
  147. {
  148. end += 12; //strlen("completename");
  149. }
  150. else if (temp.find("/displayname") == 0)
  151. {
  152. end += 11; // strlen("displayname");
  153. }
  154. else if (temp.find("/username") == 0)
  155. {
  156. end += 8; // strlen("username");
  157. }
  158. else if (temp.find("/inspect") == 0)
  159. {
  160. end += 7; // strlen("inspect");
  161. }
  162. else if (temp.find("/about") == 0)
  163. {
  164. end += 5; // strlen("about");
  165. }
  166. else
  167. {
  168. // Non-translatable SLURL
  169. translate = false;
  170. }
  171. if (translate)
  172. {
  173. temp = text.substr(start, end - start + 1);
  174. result.emplace(slurl_id);
  175. sAvatarUUIDs.emplace(slurl_id);
  176. sPendingSLURLs.emplace(temp, slurl_id);
  177. }
  178. }
  179. }
  180. if (++end >= text.size())
  181. {
  182. break;
  183. }
  184. text = text.substr(end);
  185. }
  186. // Search for group name SLURLs
  187. text = txt;
  188. while (match_regex(text.c_str(), sGroupPattern, start, end))
  189. {
  190. S32 uuid_start = end - 41;
  191. if (uuid_start >= 0)
  192. {
  193. slurl_id.set(text.substr(uuid_start, 36));
  194. if (slurl_id.notNull())
  195. {
  196. temp = text.substr(start, end - start + 1);
  197. result.emplace(slurl_id);
  198. sGroupUUIDs.emplace(slurl_id);
  199. sPendingSLURLs.emplace(temp, slurl_id);
  200. }
  201. }
  202. if (++end >= text.size())
  203. {
  204. break;
  205. }
  206. text = text.substr(end);
  207. }
  208. // Search for experience name SLURLs
  209. text = txt;
  210. while (match_regex(text.c_str(), sExperiencePattern, start, end))
  211. {
  212. S32 uuid_start = end - 43;
  213. if (uuid_start >= 0)
  214. {
  215. slurl_id.set(text.substr(uuid_start, 36));
  216. if (slurl_id.notNull())
  217. {
  218. temp = text.substr(start, end - start + 1);
  219. result.emplace(slurl_id);
  220. sExperienceUUIDs.emplace(slurl_id);
  221. sPendingSLURLs.emplace(temp, slurl_id);
  222. }
  223. }
  224. if (++end >= text.size())
  225. {
  226. break;
  227. }
  228. text = text.substr(end);
  229. }
  230. // Search for objects name SLURLs
  231. text = txt;
  232. static const std::string valid_in_url("/?&=$%-_.+!*'(),");
  233. while (match_regex(text.c_str(), sObjectIMPattern, start, end))
  234. {
  235. S32 uuid_start = end - 36;
  236. if (uuid_start >= 0)
  237. {
  238. slurl_id.set(text.substr(uuid_start, 36));
  239. if (slurl_id.notNull())
  240. {
  241. for (size_t i = end, l = text.size(); i < l; ++i)
  242. {
  243. char c = text[i];
  244. if (c == ' ') // A non-escaped space is an URL end
  245. {
  246. break;
  247. }
  248. if (!isalnum(c) &&
  249. valid_in_url.find(c) == std::string::npos)
  250. {
  251. break;
  252. }
  253. ++end;
  254. }
  255. temp = text.substr(start, end - start);
  256. result.emplace(slurl_id);
  257. sObjectsUUIDs.emplace(slurl_id);
  258. sPendingSLURLs.emplace(temp, slurl_id);
  259. }
  260. }
  261. if (++end >= text.size())
  262. {
  263. break;
  264. }
  265. text = text.substr(end);
  266. }
  267. return result;
  268. }
  269. //static
  270. void LLSLURL::avatarNameCallback(const LLUUID& id,
  271. const LLAvatarName& avatar_name)
  272. {
  273. //MK
  274. bool censor_names = gRLenabled &&
  275. (gRLInterface.mContainsShownames ||
  276. gRLInterface.mContainsShownametags);
  277. //mk
  278. std::string substitute;
  279. for (slurls_map_t::iterator it = sPendingSLURLs.begin(),
  280. end = sPendingSLURLs.end();
  281. it != end; )
  282. {
  283. if (it->second == id)
  284. {
  285. const std::string& slurl = it->first;
  286. if (slurl.find("/username") != std::string::npos)
  287. {
  288. // Note: we purposely display the legacy name instead of the
  289. // user name (the Cool VL Viewer doesn't use the user name
  290. // anywhere)
  291. substitute = avatar_name.getLegacyName();
  292. }
  293. else if (slurl.find("/displayname") != std::string::npos)
  294. {
  295. substitute = avatar_name.mDisplayName;
  296. }
  297. else
  298. {
  299. substitute = avatar_name.getNames();
  300. }
  301. //MK
  302. if (censor_names)
  303. {
  304. substitute = gRLInterface.getCensoredMessage(substitute);
  305. }
  306. //mk
  307. LLFloaterChat::substituteSLURL(id, slurl, substitute);
  308. LLNotifyBox::substituteSLURL(id, slurl, substitute);
  309. sPendingSLURLs.erase(it++);
  310. }
  311. else
  312. {
  313. ++it;
  314. }
  315. }
  316. LLFloaterChat::substitutionDone(id);
  317. LLNotifyBox::substitutionDone(id);
  318. }
  319. //static
  320. void LLSLURL::cacheNameCallback(const LLUUID& id,
  321. const std::string& name, bool is_group)
  322. {
  323. std::string substitute = name;
  324. //MK
  325. if (!is_group && gRLenabled &&
  326. (gRLInterface.mContainsShownames ||
  327. gRLInterface.mContainsShownametags))
  328. {
  329. substitute = gRLInterface.getCensoredMessage(name);
  330. }
  331. //mk
  332. for (slurls_map_t::iterator it = sPendingSLURLs.begin(),
  333. end = sPendingSLURLs.end();
  334. it != end; )
  335. {
  336. if (it->second == id)
  337. {
  338. const std::string& slurl = it->first;
  339. LLFloaterChat::substituteSLURL(id, slurl, substitute);
  340. LLNotifyBox::substituteSLURL(id, slurl, substitute);
  341. sPendingSLURLs.erase(it++);
  342. }
  343. else
  344. {
  345. ++it;
  346. }
  347. }
  348. LLFloaterChat::substitutionDone(id);
  349. LLNotifyBox::substitutionDone(id);
  350. }
  351. //static
  352. void LLSLURL::experienceNameCallback(const LLSD& experience_details)
  353. {
  354. LLUUID id = experience_details[LLExperienceCache::EXPERIENCE_ID].asUUID();
  355. std::string name = experience_details[LLExperienceCache::NAME].asString();
  356. for (slurls_map_t::iterator it = sPendingSLURLs.begin(),
  357. end = sPendingSLURLs.end();
  358. it != end; )
  359. {
  360. if (it->second == id)
  361. {
  362. const std::string& slurl = it->first;
  363. LLFloaterChat::substituteSLURL(id, slurl, name);
  364. LLNotifyBox::substituteSLURL(id, slurl, name);
  365. sPendingSLURLs.erase(it++);
  366. }
  367. else
  368. {
  369. ++it;
  370. }
  371. }
  372. LLFloaterChat::substitutionDone(id);
  373. LLNotifyBox::substitutionDone(id);
  374. }
  375. //static
  376. void LLSLURL::resolveSLURLs()
  377. {
  378. if (!gCacheNamep) return; // Paranoia
  379. bool use_display_names = LLAvatarNameCache::useDisplayNames();
  380. for (uuid_list_t::iterator it = sAvatarUUIDs.begin(),
  381. end = sAvatarUUIDs.end();
  382. it != end; ++it)
  383. {
  384. if (use_display_names)
  385. {
  386. LLAvatarNameCache::get(*it, avatarNameCallback);
  387. }
  388. else
  389. {
  390. gCacheNamep->get(*it, false, cacheNameCallback);
  391. }
  392. }
  393. sAvatarUUIDs.clear();
  394. for (uuid_list_t::iterator it = sGroupUUIDs.begin(),
  395. end = sGroupUUIDs.end();
  396. it != end; ++it)
  397. {
  398. gCacheNamep->get(*it, true, cacheNameCallback);
  399. }
  400. sGroupUUIDs.clear();
  401. LLExperienceCache* expcache = LLExperienceCache::getInstance();
  402. for (uuid_list_t::iterator it = sExperienceUUIDs.begin(),
  403. end = sExperienceUUIDs.end();
  404. it != end; ++it)
  405. {
  406. expcache->get(*it, experienceNameCallback);
  407. }
  408. sExperienceUUIDs.clear();
  409. // No need for an asynchronous query to servers for objects: we subtsitute
  410. // their SLURL with any name found behind the "name=" query field, when it
  411. // exists.
  412. std::string substitute;
  413. for (uuid_list_t::iterator it = sObjectsUUIDs.begin(),
  414. end = sObjectsUUIDs.end();
  415. it != end; ++it)
  416. {
  417. const LLUUID& id = *it;
  418. for (slurls_map_t::iterator it2 = sPendingSLURLs.begin(),
  419. end2 = sPendingSLURLs.end();
  420. it2 != end2; )
  421. {
  422. if (it2->second != id)
  423. {
  424. ++it2;
  425. continue;
  426. }
  427. const std::string& slurl = it2->first;
  428. size_t i = slurl.find("name=");
  429. if (i == std::string::npos)
  430. {
  431. ++it2;
  432. continue;
  433. }
  434. substitute = slurl.substr(i + 5); // strlen("name=")
  435. i = substitute.find('&');
  436. if (i != std::string::npos)
  437. {
  438. substitute.erase(i);
  439. }
  440. substitute = LLURI::unescape(substitute);
  441. LLFloaterChat::substituteSLURL(id, slurl, substitute);
  442. LLNotifyBox::substituteSLURL(id, slurl, substitute);
  443. sPendingSLURLs.erase(it2++);
  444. }
  445. LLFloaterChat::substitutionDone(id);
  446. LLNotifyBox::substitutionDone(id);
  447. }
  448. sObjectsUUIDs.clear();
  449. }
  450. // Resolves a simstring from a slurl
  451. LLSLURL::LLSLURL(const std::string& slurl)
  452. {
  453. // By default we go to agni.
  454. mType = INVALID;
  455. if (slurl == SIM_LOCATION_HOME)
  456. {
  457. mType = HOME_LOCATION;
  458. }
  459. else if (slurl.empty() || slurl == SIM_LOCATION_LAST)
  460. {
  461. mType = LAST_LOCATION;
  462. }
  463. else
  464. {
  465. LLGridManager* gm = LLGridManager::getInstance();
  466. LLURI slurl_uri;
  467. // Parse the slurl as a uri
  468. if (slurl.find(':') == std::string::npos)
  469. {
  470. // There may be no scheme ('secondlife:' etc.) passed in. In that
  471. // case we want to normalize the slurl by putting the appropriate
  472. // scheme in front of the slurl. So, we grab the appropriate slurl
  473. // base from the grid manager which may be
  474. // http://slurl.com/secondlife/ for maingrid, or
  475. // https://<hostname>/region/ for Standalone grid (the word region,
  476. // not the region name); these slurls are typically passed in from
  477. // the 'starting location' box on the login panel, where the user
  478. // can type in <regionname>/<x>/<y>/<z>
  479. std::string fixed_slurl = gm->getSLURLBase();
  480. // The slurl that was passed in might have a prepended / or not.
  481. // So, we strip off the prepended '/' so we don't end up with
  482. // http://slurl.com/secondlife/<region>/<x>/<y>/<z> or some such.
  483. if (slurl[0] == '/')
  484. {
  485. fixed_slurl += slurl.substr(1);
  486. }
  487. else
  488. {
  489. fixed_slurl += slurl;
  490. }
  491. // We then load the slurl into a LLURI form
  492. slurl_uri = LLURI(fixed_slurl);
  493. }
  494. else
  495. {
  496. // As we did have a scheme, implying a URI style slurl, we
  497. // simply parse it as a URI
  498. slurl_uri = LLURI(slurl);
  499. }
  500. LLSD path_array = slurl_uri.pathArray();
  501. // Determine whether it is a maingrid URI or a standalone/open style
  502. // URI/ by looking at the scheme. If it's a 'secondlife:' slurl scheme
  503. // or 'sl:' scheme, we know it's maingrid
  504. // At the end of this if/else block, we will have determined the grid,
  505. // and the slurl type (APP or LOCATION)
  506. const std::string& scheme = slurl_uri.scheme();
  507. if (scheme == LLSLURL::SLURL_SECONDLIFE_SCHEME)
  508. {
  509. // Parse a maingrid style slurl. We know the grid is maingrid, so
  510. // grab it. A location slurl for maingrid (with the special
  511. // schemes) can be in the form
  512. // secondlife://<regionname>/<x>/<y>/<z>
  513. // or
  514. // secondlife://<Grid>/secondlife/<region>/<x>/<y>/<z>
  515. // where if grid is empty, it specifies Agni
  516. // An app style slurl for maingrid can be
  517. // secondlife://<Grid>/app/<app parameters>
  518. // where an empty grid implies Agni
  519. // We will start by checking the top of the 'path' which will be
  520. // either 'app', 'secondlife', or <x>.
  521. // Default to maingrid.
  522. mGrid = "secondlife";
  523. std::string path = path_array[0].asString();
  524. if (path == LLSLURL::SLURL_SECONDLIFE_PATH ||
  525. path == LLSLURL::SLURL_APP_PATH)
  526. {
  527. // Set the type as appropriate.
  528. mType = path == LLSLURL::SLURL_APP_PATH ? APP : LOCATION;
  529. // It is in the form secondlife://<grid>/(app|secondlife), so
  530. // parse the grid name to derive the grid ID
  531. std::string hostname = slurl_uri.hostName();
  532. if (!hostname.empty())
  533. {
  534. mGrid = gm->getGridId(hostname);
  535. }
  536. else if (mType == LOCATION)
  537. {
  538. // If the slurl is in the form secondlife:///secondlife/<region>
  539. // form, then we are in fact on maingrid.
  540. mGrid = "secondlife";
  541. }
  542. else if (mType == APP)
  543. {
  544. // For app style slurls, where no grid name is specified,
  545. // assume the currently selected or logged in grid.
  546. mGrid = gm->getGridId();
  547. }
  548. if (mType != APP && mGrid.empty())
  549. {
  550. mType = INVALID;
  551. // We could not find the grid in the grid manager, so bail
  552. llwarns << "Unable to find grid for: " << slurl << llendl;
  553. return;
  554. }
  555. path_array.erase(0);
  556. }
  557. else
  558. {
  559. // It was not a /secondlife/<region> or /app/<params>, so it
  560. // must be secondlife://<region>. Therefore the hostname will
  561. // be the region name, and it's a location type
  562. mType = LOCATION;
  563. // 'normalize' it so the region name is in fact the head of the
  564. // path_array
  565. path_array.insert(0, slurl_uri.hostName());
  566. }
  567. }
  568. else if (scheme == LLSLURL::SLURL_HTTP_SCHEME ||
  569. scheme == LLSLURL::SLURL_HTTPS_SCHEME ||
  570. scheme == LLSLURL::SLURL_HOP_SCHEME ||
  571. scheme == LLSLURL::SLURL_X_GRID_INFO_SCHEME ||
  572. scheme == LLSLURL::SLURL_X_GRID_LOCATION_INFO_SCHEME)
  573. {
  574. // We are dealing with either a standalone style slurl or slurl.com
  575. // slurl
  576. std::string hostname = slurl_uri.hostName();
  577. if (hostname == LLSLURL::SLURL_COM ||
  578. hostname == LLSLURL::WWW_SLURL_COM ||
  579. hostname == LLSLURL::MAPS_SECONDLIFE_COM)
  580. {
  581. // slurl.com implies maingrid
  582. mGrid = "secondlife";
  583. }
  584. else
  585. {
  586. // Do not try to match any old http://<host>/ URL as a SLurl.
  587. // SL SLURLs will have the grid hostname in the URL, so only
  588. // match http URLs if the hostname matches the grid hostname
  589. // (or it is a slurl.com or maps.secondlife.com URL).
  590. if ((scheme == LLSLURL::SLURL_HTTP_SCHEME ||
  591. scheme == LLSLURL::SLURL_HTTPS_SCHEME) &&
  592. hostname != gm->getGridHost())
  593. {
  594. return;
  595. }
  596. // As it is a standalone grid/open, we will always have a
  597. // hostname, as Standalone/open style urls are properly
  598. // formed, unlike the stinky maingrid style
  599. mGrid = hostname;
  600. }
  601. if (path_array.size() == 0)
  602. {
  603. // We would need a path...
  604. return;
  605. }
  606. // We need to normalize the urls so the path portion starts with
  607. // the 'command' that we want to do. It can either be region or
  608. // app.
  609. std::string path = path_array[0].asString();
  610. if (path == LLSLURL::SLURL_REGION_PATH ||
  611. path == LLSLURL::SLURL_SECONDLIFE_PATH)
  612. {
  613. // Strip off 'region' or 'secondlife'
  614. path_array.erase(0);
  615. // It is a location
  616. mType = LOCATION;
  617. }
  618. else if (path == LLSLURL::SLURL_APP_PATH)
  619. {
  620. mType = APP;
  621. path_array.erase(0);
  622. // Leave app appended.
  623. }
  624. else if (scheme == LLSLURL::SLURL_HOP_SCHEME)
  625. {
  626. mGrid = hostname;
  627. mType = LOCATION;
  628. }
  629. else
  630. {
  631. // Not a valid https/http/x-grid-*info slurl...
  632. return;
  633. }
  634. }
  635. else
  636. {
  637. // Invalid scheme, so bail
  638. return;
  639. }
  640. if (path_array.size() == 0)
  641. {
  642. // We must have some stuff after the specifier as to whether it is
  643. // a region or command
  644. return;
  645. }
  646. // Now that we know whether it is an app slurl or a location slurl,
  647. // parse the slurl into the proper data structures.
  648. if (mType == APP)
  649. {
  650. // Grab the app command type and strip it (could be a command to
  651. // jump somewhere, or whatever)
  652. mAppCmd = path_array[0].asString();
  653. path_array.erase(0);
  654. // Grab the parameters
  655. mAppPath = path_array;
  656. // And the query
  657. mAppQuery = slurl_uri.query();
  658. mAppQueryMap = slurl_uri.queryMap();
  659. return;
  660. }
  661. else if (mType == LOCATION)
  662. {
  663. // At this point, head of the path array should be
  664. // [ <region>, <x>, <y>, <z> ] where x, y and z are collectively
  665. // optional.
  666. mRegion = LLURI::unescape(path_array[0].asString());
  667. if (LLStringUtil::containsNonprintable(mRegion))
  668. {
  669. LLStringUtil::stripNonprintable(mRegion);
  670. }
  671. path_array.erase(0);
  672. // Parse the x, y, and optionally z
  673. if (path_array.size() >= 2)
  674. {
  675. // This construction handles LLSD without all components
  676. // (values default to 0.f)
  677. mPosition = LLVector3(path_array);
  678. // Variable region size support: using 8192 instead of
  679. // REGION_WIDTH_METERS and REGION_HEIGHT_METERS as limits.
  680. if (mPosition.mV[VX] < 0.f || mPosition.mV[VX] > 8192.f ||
  681. mPosition.mV[VY] < 0.f || mPosition.mV[VY] > 8192.f ||
  682. mPosition.mV[VZ] < 0.f || mPosition.mV[VZ] > 8192.f)
  683. {
  684. mType = INVALID;
  685. return;
  686. }
  687. }
  688. else
  689. {
  690. // If x, y and z were not fully passed in, go to the middle of
  691. // the region. Teleport will adjust the actual location to make
  692. // sure you are on the ground and such
  693. mPosition = LLVector3(REGION_WIDTH_METERS * 0.5f,
  694. REGION_WIDTH_METERS * 0.5f, 0.f);
  695. }
  696. }
  697. }
  698. }
  699. // Creates a slurl for the middle of the region
  700. LLSLURL::LLSLURL(const std::string& grid, const std::string& region)
  701. : mType(LOCATION),
  702. mGrid(grid),
  703. mRegion(region)
  704. {
  705. mPosition = LLVector3(REGION_WIDTH_METERS * 0.5f,
  706. REGION_WIDTH_METERS * 0.5f, 0.f);
  707. }
  708. // Creates a slurl given the position. The position will be modded with the
  709. // region width handling global positions as well
  710. LLSLURL::LLSLURL(const std::string& grid, const std::string& region,
  711. const LLVector3& position)
  712. : mType(LOCATION),
  713. mGrid(grid),
  714. mRegion(region)
  715. {
  716. #if 1 // Variable region size support (part 1, see below for part 2)
  717. S32 x = ll_roundp(position.mV[VX]);
  718. S32 y = ll_roundp(position.mV[VY]);
  719. #else
  720. S32 x = ll_roundp((F32)fmod(position.mV[VX], REGION_WIDTH_METERS));
  721. S32 y = ll_roundp((F32)fmod(position.mV[VY], REGION_WIDTH_METERS));
  722. #endif
  723. S32 z = ll_roundp(position.mV[VZ]);
  724. mPosition = LLVector3(x, y, z);
  725. }
  726. // Creates a simstring
  727. LLSLURL::LLSLURL(const std::string& region, const LLVector3& position)
  728. {
  729. *this = LLSLURL(LLGridManager::getInstance()->getGridId(), region,
  730. position);
  731. }
  732. // Creates a slurl from a global position
  733. LLSLURL::LLSLURL(const std::string& grid, const std::string& region,
  734. const LLVector3d& global_position)
  735. {
  736. LLVector3 pos(global_position);
  737. std::string grid_id = LLGridManager::getInstance()->getGridId(grid);
  738. // Variable region size support (part 2, see above for part 1)
  739. bool adjusted = false;
  740. if (grid.empty() || grid_id == LLGridManager::getInstance()->getGridId())
  741. {
  742. // If we build a SLURL for the current grid, then we can use the data
  743. // of this grid to find the region size.
  744. LLSimInfo* sim;
  745. sim = gWorldMap.simInfoFromPosGlobal(global_position);
  746. if (sim)
  747. {
  748. pos.mV[VX] = fmod(pos.mV[VX], sim->getSizeX());
  749. pos.mV[VY] = fmod(pos.mV[VY], sim->getSizeY());
  750. adjusted = true;
  751. }
  752. else if (!gIsInSecondLife)
  753. {
  754. llwarns << "Sim info unavailable for: " << region
  755. << ". The SLURL is created with the default region width (may cause issues if the grid supports VAR REGIONs)"
  756. << llendl;
  757. }
  758. }
  759. if (!adjusted)
  760. {
  761. // Use the default region size as a fallback
  762. pos.mV[VX] = fmod(pos.mV[VX], REGION_WIDTH_METERS);
  763. pos.mV[VY] = fmod(pos.mV[VY], REGION_WIDTH_METERS);
  764. }
  765. *this = LLSLURL(grid_id, region, pos);
  766. }
  767. // Creates a slurl from a global position
  768. LLSLURL::LLSLURL(const std::string& region, const LLVector3d& global_position)
  769. {
  770. *this = LLSLURL(LLGridManager::getInstance()->getGridHost(), region,
  771. global_position);
  772. }
  773. LLSLURL::LLSLURL(const std::string& command, const LLUUID& id,
  774. const std::string& verb)
  775. : mType(APP),
  776. mAppCmd(command)
  777. {
  778. mAppPath = LLSD::emptyArray();
  779. mAppPath.append(LLSD(id));
  780. mAppPath.append(LLSD(verb));
  781. }
  782. std::string LLSLURL::getSLURLString() const
  783. {
  784. switch (mType)
  785. {
  786. case HOME_LOCATION:
  787. return SIM_LOCATION_HOME;
  788. case LAST_LOCATION:
  789. return SIM_LOCATION_LAST;
  790. case LOCATION:
  791. {
  792. // Lookup the grid
  793. S32 x = ll_roundp(mPosition.mV[VX]);
  794. S32 y = ll_roundp(mPosition.mV[VY]);
  795. S32 z = ll_roundp(mPosition.mV[VZ]);
  796. return LLGridManager::getInstance()->getSLURLBase(mGrid) +
  797. LLURI::escape(mRegion) + llformat("/%d/%d/%d", x, y, z);
  798. }
  799. case APP:
  800. {
  801. std::ostringstream app_url;
  802. app_url << LLGridManager::getInstance()->getAppSLURLBase()
  803. << "/" << mAppCmd;
  804. for (LLSD::array_const_iterator i = mAppPath.beginArray();
  805. i != mAppPath.endArray(); ++i)
  806. {
  807. app_url << "/" << i->asString();
  808. }
  809. if (mAppQuery.length() > 0)
  810. {
  811. app_url << "?" << mAppQuery;
  812. }
  813. return app_url.str();
  814. }
  815. default:
  816. llwarns << "Unexpected SLURL type for SLURL string: " << (S32)mType
  817. << llendl;
  818. return LLStringUtil::null;
  819. }
  820. }
  821. bool LLSLURL::operator==(const LLSLURL& rhs)
  822. {
  823. if (rhs.mType != mType) return false;
  824. switch (mType)
  825. {
  826. case LOCATION:
  827. return mGrid == rhs.mGrid && mRegion == rhs.mRegion &&
  828. mPosition == rhs.mPosition;
  829. case APP:
  830. return getSLURLString() == rhs.getSLURLString();
  831. case HOME_LOCATION:
  832. case LAST_LOCATION:
  833. return true;
  834. default:
  835. return false;
  836. }
  837. }
  838. std::string LLSLURL::getLocationString() const
  839. {
  840. return llformat("%s/%d/%d/%d", mRegion.c_str(),
  841. ll_roundp(mPosition.mV[VX]), ll_roundp(mPosition.mV[VY]),
  842. ll_roundp(mPosition.mV[VZ]));
  843. }
  844. // static
  845. const std::string LLSLURL::typeName[NUM_SLURL_TYPES] =
  846. {
  847. "INVALID",
  848. "LOCATION",
  849. "HOME_LOCATION",
  850. "LAST_LOCATION",
  851. "APP",
  852. "HELP"
  853. };
  854. std::string LLSLURL::getTypeString(eType type)
  855. {
  856. if (type >= INVALID && type < NUM_SLURL_TYPES)
  857. {
  858. return LLSLURL::typeName[type];
  859. }
  860. return llformat("Out of Range (%d)", type);
  861. }
  862. std::string LLSLURL::asString() const
  863. {
  864. std::ostringstream result;
  865. result << " mType: " << LLSLURL::getTypeString(mType)
  866. << " mGrid: " << getGrid()
  867. << " mRegion: " << getRegion()
  868. << " mPosition: " << mPosition
  869. << " mAppCmd:" << getAppCmd()
  870. << " mAppPath:" << getAppPath().asString()
  871. << " mAppQueryMap:" << getAppQueryMap().asString()
  872. << " mAppQuery: " << getAppQuery();
  873. return result.str();
  874. }