PageRenderTime 161ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 1ms

/indra/llui/llurlentry.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1188 lines | 842 code | 141 blank | 205 comment | 95 complexity | 45c44d08f6160e9cd7d591a2b01419cb MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llurlentry.cpp
  3. * @author Martin Reddy
  4. * @brief Describes the Url types that can be registered in LLUrlRegistry
  5. *
  6. * $LicenseInfo:firstyear=2009&license=viewerlgpl$
  7. * Second Life Viewer Source Code
  8. * Copyright (C) 2010, Linden Research, Inc.
  9. *
  10. * This library is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU Lesser General Public
  12. * License as published by the Free Software Foundation;
  13. * version 2.1 of the License only.
  14. *
  15. * This library is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  18. * Lesser General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Lesser General Public
  21. * License along with this library; if not, write to the Free Software
  22. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  23. *
  24. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  25. * $/LicenseInfo$
  26. */
  27. #include "linden_common.h"
  28. #include "llurlentry.h"
  29. #include "lluictrl.h"
  30. #include "lluri.h"
  31. #include "llurlmatch.h"
  32. #include "llurlregistry.h"
  33. #include "llavatarnamecache.h"
  34. #include "llcachename.h"
  35. #include "lltrans.h"
  36. #include "lluicolortable.h"
  37. #include "message.h"
  38. #define APP_HEADER_REGEX "((x-grid-location-info://[-\\w\\.]+/app)|(secondlife:///app))"
  39. // Utility functions
  40. std::string localize_slapp_label(const std::string& url, const std::string& full_name);
  41. LLUrlEntryBase::LLUrlEntryBase()
  42. {}
  43. LLUrlEntryBase::~LLUrlEntryBase()
  44. {
  45. }
  46. std::string LLUrlEntryBase::getUrl(const std::string &string) const
  47. {
  48. return escapeUrl(string);
  49. }
  50. //virtual
  51. std::string LLUrlEntryBase::getIcon(const std::string &url)
  52. {
  53. return mIcon;
  54. }
  55. LLStyle::Params LLUrlEntryBase::getStyle() const
  56. {
  57. LLStyle::Params style_params;
  58. style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor");
  59. style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor");
  60. style_params.font.style = "UNDERLINE";
  61. return style_params;
  62. }
  63. std::string LLUrlEntryBase::getIDStringFromUrl(const std::string &url) const
  64. {
  65. // return the id from a SLURL in the format /app/{cmd}/{id}/about
  66. LLURI uri(url);
  67. LLSD path_array = uri.pathArray();
  68. if (path_array.size() == 4)
  69. {
  70. return path_array.get(2).asString();
  71. }
  72. return "";
  73. }
  74. std::string LLUrlEntryBase::unescapeUrl(const std::string &url) const
  75. {
  76. return LLURI::unescape(url);
  77. }
  78. std::string LLUrlEntryBase::escapeUrl(const std::string &url) const
  79. {
  80. static std::string no_escape_chars;
  81. static bool initialized = false;
  82. if (!initialized)
  83. {
  84. no_escape_chars =
  85. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  86. "abcdefghijklmnopqrstuvwxyz"
  87. "0123456789"
  88. "-._~!$?&()*+,@:;=/%#";
  89. std::sort(no_escape_chars.begin(), no_escape_chars.end());
  90. initialized = true;
  91. }
  92. return LLURI::escape(url, no_escape_chars, true);
  93. }
  94. std::string LLUrlEntryBase::getLabelFromWikiLink(const std::string &url) const
  95. {
  96. // return the label part from [http://www.example.org Label]
  97. const char *text = url.c_str();
  98. S32 start = 0;
  99. while (! isspace(text[start]))
  100. {
  101. start++;
  102. }
  103. while (text[start] == ' ' || text[start] == '\t')
  104. {
  105. start++;
  106. }
  107. return unescapeUrl(url.substr(start, url.size()-start-1));
  108. }
  109. std::string LLUrlEntryBase::getUrlFromWikiLink(const std::string &string) const
  110. {
  111. // return the url part from [http://www.example.org Label]
  112. const char *text = string.c_str();
  113. S32 end = 0;
  114. while (! isspace(text[end]))
  115. {
  116. end++;
  117. }
  118. return escapeUrl(string.substr(1, end-1));
  119. }
  120. void LLUrlEntryBase::addObserver(const std::string &id,
  121. const std::string &url,
  122. const LLUrlLabelCallback &cb)
  123. {
  124. // add a callback to be notified when we have a label for the uuid
  125. LLUrlEntryObserver observer;
  126. observer.url = url;
  127. observer.signal = new LLUrlLabelSignal();
  128. if (observer.signal)
  129. {
  130. observer.signal->connect(cb);
  131. mObservers.insert(std::pair<std::string, LLUrlEntryObserver>(id, observer));
  132. }
  133. }
  134. // *NOTE: See also LLUrlEntryAgent::callObservers()
  135. void LLUrlEntryBase::callObservers(const std::string &id,
  136. const std::string &label,
  137. const std::string &icon)
  138. {
  139. // notify all callbacks waiting on the given uuid
  140. typedef std::multimap<std::string, LLUrlEntryObserver>::iterator observer_it;
  141. std::pair<observer_it, observer_it> matching_range = mObservers.equal_range(id);
  142. for (observer_it it = matching_range.first; it != matching_range.second;)
  143. {
  144. // call the callback - give it the new label
  145. LLUrlEntryObserver &observer = it->second;
  146. (*observer.signal)(it->second.url, label, icon);
  147. // then remove the signal - we only need to call it once
  148. delete observer.signal;
  149. mObservers.erase(it++);
  150. }
  151. }
  152. /// is this a match for a URL that should not be hyperlinked?
  153. bool LLUrlEntryBase::isLinkDisabled() const
  154. {
  155. // this allows us to have a global setting to turn off text hyperlink highlighting/action
  156. bool globally_disabled = LLUI::sSettingGroups["config"]->getBOOL("DisableTextHyperlinkActions");
  157. return globally_disabled;
  158. }
  159. static std::string getStringAfterToken(const std::string str, const std::string token)
  160. {
  161. size_t pos = str.find(token);
  162. if (pos == std::string::npos)
  163. {
  164. return "";
  165. }
  166. pos += token.size();
  167. return str.substr(pos, str.size() - pos);
  168. }
  169. //
  170. // LLUrlEntryHTTP Describes generic http: and https: Urls
  171. //
  172. LLUrlEntryHTTP::LLUrlEntryHTTP()
  173. {
  174. mPattern = boost::regex("https?://([-\\w\\.]+)+(:\\d+)?(:\\w+)?(@\\d+)?(@\\w+)?/?\\S*",
  175. boost::regex::perl|boost::regex::icase);
  176. mMenuName = "menu_url_http.xml";
  177. mTooltip = LLTrans::getString("TooltipHttpUrl");
  178. }
  179. std::string LLUrlEntryHTTP::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
  180. {
  181. return unescapeUrl(url);
  182. }
  183. //
  184. // LLUrlEntryHTTP Describes generic http: and https: Urls with custom label
  185. // We use the wikipedia syntax of [http://www.example.org Text]
  186. //
  187. LLUrlEntryHTTPLabel::LLUrlEntryHTTPLabel()
  188. {
  189. mPattern = boost::regex("\\[https?://\\S+[ \t]+[^\\]]+\\]",
  190. boost::regex::perl|boost::regex::icase);
  191. mMenuName = "menu_url_http.xml";
  192. mTooltip = LLTrans::getString("TooltipHttpUrl");
  193. }
  194. std::string LLUrlEntryHTTPLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
  195. {
  196. std::string label = getLabelFromWikiLink(url);
  197. return (!LLUrlRegistry::instance().hasUrl(label)) ? label : getUrl(url);
  198. }
  199. std::string LLUrlEntryHTTPLabel::getTooltip(const std::string &string) const
  200. {
  201. return getUrl(string);
  202. }
  203. std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string) const
  204. {
  205. return getUrlFromWikiLink(string);
  206. }
  207. //
  208. // LLUrlEntryHTTPNoProtocol Describes generic Urls like www.google.com
  209. //
  210. LLUrlEntryHTTPNoProtocol::LLUrlEntryHTTPNoProtocol()
  211. {
  212. mPattern = boost::regex("("
  213. "\\bwww\\.\\S+\\.\\S+" // i.e. www.FOO.BAR
  214. "|" // or
  215. "(?<!@)\\b[^[:space:]:@/>]+\\.(?:com|net|edu|org)([/:][^[:space:]<]*)?\\b" // i.e. FOO.net
  216. ")",
  217. boost::regex::perl|boost::regex::icase);
  218. mMenuName = "menu_url_http.xml";
  219. mTooltip = LLTrans::getString("TooltipHttpUrl");
  220. }
  221. std::string LLUrlEntryHTTPNoProtocol::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
  222. {
  223. return unescapeUrl(url);
  224. }
  225. std::string LLUrlEntryHTTPNoProtocol::getUrl(const std::string &string) const
  226. {
  227. if (string.find("://") == std::string::npos)
  228. {
  229. return "http://" + escapeUrl(string);
  230. }
  231. return escapeUrl(string);
  232. }
  233. //
  234. // LLUrlEntrySLURL Describes generic http: and https: Urls
  235. //
  236. LLUrlEntrySLURL::LLUrlEntrySLURL()
  237. {
  238. // see http://slurl.com/about.php for details on the SLURL format
  239. mPattern = boost::regex("http://(maps.secondlife.com|slurl.com)/secondlife/[^ /]+(/\\d+){0,3}(/?(\\?title|\\?img|\\?msg)=\\S*)?/?",
  240. boost::regex::perl|boost::regex::icase);
  241. mMenuName = "menu_url_slurl.xml";
  242. mTooltip = LLTrans::getString("TooltipSLURL");
  243. }
  244. std::string LLUrlEntrySLURL::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
  245. {
  246. //
  247. // we handle SLURLs in the following formats:
  248. // - http://slurl.com/secondlife/Place/X/Y/Z
  249. // - http://slurl.com/secondlife/Place/X/Y
  250. // - http://slurl.com/secondlife/Place/X
  251. // - http://slurl.com/secondlife/Place
  252. //
  253. LLURI uri(url);
  254. LLSD path_array = uri.pathArray();
  255. S32 path_parts = path_array.size();
  256. if (path_parts == 5)
  257. {
  258. // handle slurl with (X,Y,Z) coordinates
  259. std::string location = unescapeUrl(path_array[path_parts-4]);
  260. std::string x = path_array[path_parts-3];
  261. std::string y = path_array[path_parts-2];
  262. std::string z = path_array[path_parts-1];
  263. return location + " (" + x + "," + y + "," + z + ")";
  264. }
  265. else if (path_parts == 4)
  266. {
  267. // handle slurl with (X,Y) coordinates
  268. std::string location = unescapeUrl(path_array[path_parts-3]);
  269. std::string x = path_array[path_parts-2];
  270. std::string y = path_array[path_parts-1];
  271. return location + " (" + x + "," + y + ")";
  272. }
  273. else if (path_parts == 3)
  274. {
  275. // handle slurl with (X) coordinate
  276. std::string location = unescapeUrl(path_array[path_parts-2]);
  277. std::string x = path_array[path_parts-1];
  278. return location + " (" + x + ")";
  279. }
  280. else if (path_parts == 2)
  281. {
  282. // handle slurl with no coordinates
  283. std::string location = unescapeUrl(path_array[path_parts-1]);
  284. return location;
  285. }
  286. return url;
  287. }
  288. std::string LLUrlEntrySLURL::getLocation(const std::string &url) const
  289. {
  290. // return the part of the Url after slurl.com/secondlife/
  291. const std::string search_string = "/secondlife";
  292. size_t pos = url.find(search_string);
  293. if (pos == std::string::npos)
  294. {
  295. return "";
  296. }
  297. pos += search_string.size() + 1;
  298. return url.substr(pos, url.size() - pos);
  299. }
  300. //
  301. // LLUrlEntryAgent Describes a Second Life agent Url, e.g.,
  302. // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about
  303. // x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about
  304. //
  305. LLUrlEntryAgent::LLUrlEntryAgent()
  306. {
  307. mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/\\w+",
  308. boost::regex::perl|boost::regex::icase);
  309. mMenuName = "menu_url_agent.xml";
  310. mIcon = "Generic_Person";
  311. }
  312. // virtual
  313. void LLUrlEntryAgent::callObservers(const std::string &id,
  314. const std::string &label,
  315. const std::string &icon)
  316. {
  317. // notify all callbacks waiting on the given uuid
  318. typedef std::multimap<std::string, LLUrlEntryObserver>::iterator observer_it;
  319. std::pair<observer_it, observer_it> matching_range = mObservers.equal_range(id);
  320. for (observer_it it = matching_range.first; it != matching_range.second;)
  321. {
  322. // call the callback - give it the new label
  323. LLUrlEntryObserver &observer = it->second;
  324. std::string final_label = localize_slapp_label(observer.url, label);
  325. (*observer.signal)(observer.url, final_label, icon);
  326. // then remove the signal - we only need to call it once
  327. delete observer.signal;
  328. mObservers.erase(it++);
  329. }
  330. }
  331. void LLUrlEntryAgent::onAvatarNameCache(const LLUUID& id,
  332. const LLAvatarName& av_name)
  333. {
  334. std::string label = av_name.getCompleteName();
  335. // received the agent name from the server - tell our observers
  336. callObservers(id.asString(), label, mIcon);
  337. }
  338. LLUUID LLUrlEntryAgent::getID(const std::string &string) const
  339. {
  340. return LLUUID(getIDStringFromUrl(string));
  341. }
  342. std::string LLUrlEntryAgent::getTooltip(const std::string &string) const
  343. {
  344. // return a tooltip corresponding to the URL type instead of the generic one
  345. std::string url = getUrl(string);
  346. if (LLStringUtil::endsWith(url, "/inspect"))
  347. {
  348. return LLTrans::getString("TooltipAgentInspect");
  349. }
  350. if (LLStringUtil::endsWith(url, "/mute"))
  351. {
  352. return LLTrans::getString("TooltipAgentMute");
  353. }
  354. if (LLStringUtil::endsWith(url, "/unmute"))
  355. {
  356. return LLTrans::getString("TooltipAgentUnmute");
  357. }
  358. if (LLStringUtil::endsWith(url, "/im"))
  359. {
  360. return LLTrans::getString("TooltipAgentIM");
  361. }
  362. if (LLStringUtil::endsWith(url, "/pay"))
  363. {
  364. return LLTrans::getString("TooltipAgentPay");
  365. }
  366. if (LLStringUtil::endsWith(url, "/offerteleport"))
  367. {
  368. return LLTrans::getString("TooltipAgentOfferTeleport");
  369. }
  370. if (LLStringUtil::endsWith(url, "/requestfriend"))
  371. {
  372. return LLTrans::getString("TooltipAgentRequestFriend");
  373. }
  374. return LLTrans::getString("TooltipAgentUrl");
  375. }
  376. bool LLUrlEntryAgent::underlineOnHoverOnly(const std::string &string) const
  377. {
  378. std::string url = getUrl(string);
  379. return LLStringUtil::endsWith(url, "/about") || LLStringUtil::endsWith(url, "/inspect");
  380. }
  381. std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
  382. {
  383. if (!gCacheName)
  384. {
  385. // probably at the login screen, use short string for layout
  386. return LLTrans::getString("LoadingData");
  387. }
  388. std::string agent_id_string = getIDStringFromUrl(url);
  389. if (agent_id_string.empty())
  390. {
  391. // something went wrong, just give raw url
  392. return unescapeUrl(url);
  393. }
  394. LLUUID agent_id(agent_id_string);
  395. if (agent_id.isNull())
  396. {
  397. return LLTrans::getString("AvatarNameNobody");
  398. }
  399. LLAvatarName av_name;
  400. if (LLAvatarNameCache::get(agent_id, &av_name))
  401. {
  402. std::string label = av_name.getCompleteName();
  403. // handle suffixes like /mute or /offerteleport
  404. label = localize_slapp_label(url, label);
  405. return label;
  406. }
  407. else
  408. {
  409. LLAvatarNameCache::get(agent_id,
  410. boost::bind(&LLUrlEntryAgent::onAvatarNameCache,
  411. this, _1, _2));
  412. addObserver(agent_id_string, url, cb);
  413. return LLTrans::getString("LoadingData");
  414. }
  415. }
  416. LLStyle::Params LLUrlEntryAgent::getStyle() const
  417. {
  418. LLStyle::Params style_params = LLUrlEntryBase::getStyle();
  419. style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor");
  420. style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor");
  421. return style_params;
  422. }
  423. std::string localize_slapp_label(const std::string& url, const std::string& full_name)
  424. {
  425. // customize label string based on agent SLapp suffix
  426. if (LLStringUtil::endsWith(url, "/mute"))
  427. {
  428. return LLTrans::getString("SLappAgentMute") + " " + full_name;
  429. }
  430. if (LLStringUtil::endsWith(url, "/unmute"))
  431. {
  432. return LLTrans::getString("SLappAgentUnmute") + " " + full_name;
  433. }
  434. if (LLStringUtil::endsWith(url, "/im"))
  435. {
  436. return LLTrans::getString("SLappAgentIM") + " " + full_name;
  437. }
  438. if (LLStringUtil::endsWith(url, "/pay"))
  439. {
  440. return LLTrans::getString("SLappAgentPay") + " " + full_name;
  441. }
  442. if (LLStringUtil::endsWith(url, "/offerteleport"))
  443. {
  444. return LLTrans::getString("SLappAgentOfferTeleport") + " " + full_name;
  445. }
  446. if (LLStringUtil::endsWith(url, "/requestfriend"))
  447. {
  448. return LLTrans::getString("SLappAgentRequestFriend") + " " + full_name;
  449. }
  450. return full_name;
  451. }
  452. std::string LLUrlEntryAgent::getIcon(const std::string &url)
  453. {
  454. // *NOTE: Could look up a badge here by calling getIDStringFromUrl()
  455. // and looking up the badge for the agent.
  456. return mIcon;
  457. }
  458. //
  459. // LLUrlEntryAgentName describes a Second Life agent name Url, e.g.,
  460. // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username)
  461. // x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/(completename|displayname|username)
  462. //
  463. LLUrlEntryAgentName::LLUrlEntryAgentName()
  464. {}
  465. void LLUrlEntryAgentName::onAvatarNameCache(const LLUUID& id,
  466. const LLAvatarName& av_name)
  467. {
  468. std::string label = getName(av_name);
  469. // received the agent name from the server - tell our observers
  470. callObservers(id.asString(), label, mIcon);
  471. }
  472. std::string LLUrlEntryAgentName::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
  473. {
  474. if (!gCacheName)
  475. {
  476. // probably at the login screen, use short string for layout
  477. return LLTrans::getString("LoadingData");
  478. }
  479. std::string agent_id_string = getIDStringFromUrl(url);
  480. if (agent_id_string.empty())
  481. {
  482. // something went wrong, just give raw url
  483. return unescapeUrl(url);
  484. }
  485. LLUUID agent_id(agent_id_string);
  486. if (agent_id.isNull())
  487. {
  488. return LLTrans::getString("AvatarNameNobody");
  489. }
  490. LLAvatarName av_name;
  491. if (LLAvatarNameCache::get(agent_id, &av_name))
  492. {
  493. return getName(av_name);
  494. }
  495. else
  496. {
  497. LLAvatarNameCache::get(agent_id,
  498. boost::bind(&LLUrlEntryAgentCompleteName::onAvatarNameCache,
  499. this, _1, _2));
  500. addObserver(agent_id_string, url, cb);
  501. return LLTrans::getString("LoadingData");
  502. }
  503. }
  504. LLStyle::Params LLUrlEntryAgentName::getStyle() const
  505. {
  506. // don't override default colors
  507. return LLStyle::Params().is_link(false);
  508. }
  509. //
  510. // LLUrlEntryAgentCompleteName describes a Second Life agent complete name Url, e.g.,
  511. // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename
  512. // x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/completename
  513. //
  514. LLUrlEntryAgentCompleteName::LLUrlEntryAgentCompleteName()
  515. {
  516. mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/completename",
  517. boost::regex::perl|boost::regex::icase);
  518. }
  519. std::string LLUrlEntryAgentCompleteName::getName(const LLAvatarName& avatar_name)
  520. {
  521. return avatar_name.getCompleteName();
  522. }
  523. //
  524. // LLUrlEntryAgentDisplayName describes a Second Life agent display name Url, e.g.,
  525. // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname
  526. // x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/displayname
  527. //
  528. LLUrlEntryAgentDisplayName::LLUrlEntryAgentDisplayName()
  529. {
  530. mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/displayname",
  531. boost::regex::perl|boost::regex::icase);
  532. }
  533. std::string LLUrlEntryAgentDisplayName::getName(const LLAvatarName& avatar_name)
  534. {
  535. return avatar_name.mDisplayName;
  536. }
  537. //
  538. // LLUrlEntryAgentUserName describes a Second Life agent user name Url, e.g.,
  539. // secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username
  540. // x-grid-location-info://lincoln.lindenlab.com/app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/username
  541. //
  542. LLUrlEntryAgentUserName::LLUrlEntryAgentUserName()
  543. {
  544. mPattern = boost::regex(APP_HEADER_REGEX "/agent/[\\da-f-]+/username",
  545. boost::regex::perl|boost::regex::icase);
  546. }
  547. std::string LLUrlEntryAgentUserName::getName(const LLAvatarName& avatar_name)
  548. {
  549. return avatar_name.mUsername.empty() ? avatar_name.getLegacyName() : avatar_name.mUsername;
  550. }
  551. //
  552. // LLUrlEntryGroup Describes a Second Life group Url, e.g.,
  553. // secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about
  554. // secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect
  555. // x-grid-location-info://lincoln.lindenlab.com/app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/inspect
  556. //
  557. LLUrlEntryGroup::LLUrlEntryGroup()
  558. {
  559. mPattern = boost::regex(APP_HEADER_REGEX "/group/[\\da-f-]+/\\w+",
  560. boost::regex::perl|boost::regex::icase);
  561. mMenuName = "menu_url_group.xml";
  562. mIcon = "Generic_Group";
  563. mTooltip = LLTrans::getString("TooltipGroupUrl");
  564. }
  565. void LLUrlEntryGroup::onGroupNameReceived(const LLUUID& id,
  566. const std::string& name,
  567. bool is_group)
  568. {
  569. // received the group name from the server - tell our observers
  570. callObservers(id.asString(), name, mIcon);
  571. }
  572. LLUUID LLUrlEntryGroup::getID(const std::string &string) const
  573. {
  574. return LLUUID(getIDStringFromUrl(string));
  575. }
  576. std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
  577. {
  578. if (!gCacheName)
  579. {
  580. // probably at login screen, give something short for layout
  581. return LLTrans::getString("LoadingData");
  582. }
  583. std::string group_id_string = getIDStringFromUrl(url);
  584. if (group_id_string.empty())
  585. {
  586. // something went wrong, give raw url
  587. return unescapeUrl(url);
  588. }
  589. LLUUID group_id(group_id_string);
  590. std::string group_name;
  591. if (group_id.isNull())
  592. {
  593. return LLTrans::getString("GroupNameNone");
  594. }
  595. else if (gCacheName->getGroupName(group_id, group_name))
  596. {
  597. return group_name;
  598. }
  599. else
  600. {
  601. gCacheName->getGroup(group_id,
  602. boost::bind(&LLUrlEntryGroup::onGroupNameReceived,
  603. this, _1, _2, _3));
  604. addObserver(group_id_string, url, cb);
  605. return LLTrans::getString("LoadingData");
  606. }
  607. }
  608. LLStyle::Params LLUrlEntryGroup::getStyle() const
  609. {
  610. LLStyle::Params style_params = LLUrlEntryBase::getStyle();
  611. style_params.color = LLUIColorTable::instance().getColor("HTMLLinkColor");
  612. style_params.readonly_color = LLUIColorTable::instance().getColor("HTMLLinkColor");
  613. return style_params;
  614. }
  615. //
  616. // LLUrlEntryInventory Describes a Second Life inventory Url, e.g.,
  617. // secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select
  618. //
  619. LLUrlEntryInventory::LLUrlEntryInventory()
  620. {
  621. //*TODO: add supporting of inventory item names with whitespaces
  622. //this pattern cann't parse for example
  623. //secondlife:///app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces&param2=value
  624. //x-grid-location-info://lincoln.lindenlab.com/app/inventory/0e346d8b-4433-4d66-a6b0-fd37083abc4c/select?name=name with spaces&param2=value
  625. mPattern = boost::regex(APP_HEADER_REGEX "/inventory/[\\da-f-]+/\\w+\\S*",
  626. boost::regex::perl|boost::regex::icase);
  627. mMenuName = "menu_url_inventory.xml";
  628. }
  629. std::string LLUrlEntryInventory::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
  630. {
  631. std::string label = getStringAfterToken(url, "name=");
  632. return LLURI::unescape(label.empty() ? url : label);
  633. }
  634. //
  635. // LLUrlEntryObjectIM Describes a Second Life inspector for the object Url, e.g.,
  636. // secondlife:///app/objectim/7bcd7864-da6b-e43f-4486-91d28a28d95b?name=Object&owner=3de548e1-57be-cfea-2b78-83ae3ad95998&slurl=Danger!%20Danger!/200/200/30/&groupowned=1
  637. //
  638. LLUrlEntryObjectIM::LLUrlEntryObjectIM()
  639. {
  640. mPattern = boost::regex("secondlife:///app/objectim/[\\da-f-]+\?.*",
  641. boost::regex::perl|boost::regex::icase);
  642. mMenuName = "menu_url_objectim.xml";
  643. }
  644. std::string LLUrlEntryObjectIM::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
  645. {
  646. LLURI uri(url);
  647. LLSD query_map = uri.queryMap();
  648. if (query_map.has("name"))
  649. return query_map["name"];
  650. return unescapeUrl(url);
  651. }
  652. std::string LLUrlEntryObjectIM::getLocation(const std::string &url) const
  653. {
  654. LLURI uri(url);
  655. LLSD query_map = uri.queryMap();
  656. if (query_map.has("slurl"))
  657. return query_map["slurl"];
  658. return LLUrlEntryBase::getLocation(url);
  659. }
  660. // LLUrlEntryParcel statics.
  661. LLUUID LLUrlEntryParcel::sAgentID(LLUUID::null);
  662. LLUUID LLUrlEntryParcel::sSessionID(LLUUID::null);
  663. LLHost LLUrlEntryParcel::sRegionHost(LLHost::invalid);
  664. bool LLUrlEntryParcel::sDisconnected(false);
  665. std::set<LLUrlEntryParcel*> LLUrlEntryParcel::sParcelInfoObservers;
  666. ///
  667. /// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
  668. /// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
  669. /// x-grid-location-info://lincoln.lindenlab.com/app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
  670. ///
  671. LLUrlEntryParcel::LLUrlEntryParcel()
  672. {
  673. mPattern = boost::regex(APP_HEADER_REGEX "/parcel/[\\da-f-]+/about",
  674. boost::regex::perl|boost::regex::icase);
  675. mMenuName = "menu_url_parcel.xml";
  676. mTooltip = LLTrans::getString("TooltipParcelUrl");
  677. sParcelInfoObservers.insert(this);
  678. }
  679. LLUrlEntryParcel::~LLUrlEntryParcel()
  680. {
  681. sParcelInfoObservers.erase(this);
  682. }
  683. std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
  684. {
  685. LLSD path_array = LLURI(url).pathArray();
  686. S32 path_parts = path_array.size();
  687. if (path_parts < 3) // no parcel id
  688. {
  689. llwarns << "Failed to parse url [" << url << "]" << llendl;
  690. return url;
  691. }
  692. std::string parcel_id_string = unescapeUrl(path_array[2]); // parcel id
  693. // Add an observer to call LLUrlLabelCallback when we have parcel name.
  694. addObserver(parcel_id_string, url, cb);
  695. LLUUID parcel_id(parcel_id_string);
  696. sendParcelInfoRequest(parcel_id);
  697. return unescapeUrl(url);
  698. }
  699. void LLUrlEntryParcel::sendParcelInfoRequest(const LLUUID& parcel_id)
  700. {
  701. if (sRegionHost == LLHost::invalid || sDisconnected) return;
  702. LLMessageSystem *msg = gMessageSystem;
  703. msg->newMessage("ParcelInfoRequest");
  704. msg->nextBlockFast(_PREHASH_AgentData);
  705. msg->addUUIDFast(_PREHASH_AgentID, sAgentID );
  706. msg->addUUID("SessionID", sSessionID);
  707. msg->nextBlock("Data");
  708. msg->addUUID("ParcelID", parcel_id);
  709. msg->sendReliable(sRegionHost);
  710. }
  711. void LLUrlEntryParcel::onParcelInfoReceived(const std::string &id, const std::string &label)
  712. {
  713. callObservers(id, label.empty() ? LLTrans::getString("RegionInfoError") : label, mIcon);
  714. }
  715. // static
  716. void LLUrlEntryParcel::processParcelInfo(const LLParcelData& parcel_data)
  717. {
  718. std::string label(LLStringUtil::null);
  719. if (!parcel_data.name.empty())
  720. {
  721. label = parcel_data.name;
  722. }
  723. // If parcel name is empty use Sim_name (x, y, z) for parcel label.
  724. else if (!parcel_data.sim_name.empty())
  725. {
  726. S32 region_x = llround(parcel_data.global_x) % REGION_WIDTH_UNITS;
  727. S32 region_y = llround(parcel_data.global_y) % REGION_WIDTH_UNITS;
  728. S32 region_z = llround(parcel_data.global_z);
  729. label = llformat("%s (%d, %d, %d)",
  730. parcel_data.sim_name.c_str(), region_x, region_y, region_z);
  731. }
  732. for (std::set<LLUrlEntryParcel*>::iterator iter = sParcelInfoObservers.begin();
  733. iter != sParcelInfoObservers.end();
  734. ++iter)
  735. {
  736. LLUrlEntryParcel* url_entry = *iter;
  737. if (url_entry)
  738. {
  739. url_entry->onParcelInfoReceived(parcel_data.parcel_id.asString(), label);
  740. }
  741. }
  742. }
  743. //
  744. // LLUrlEntryPlace Describes secondlife://<location> URLs
  745. //
  746. LLUrlEntryPlace::LLUrlEntryPlace()
  747. {
  748. mPattern = boost::regex("((x-grid-location-info://[-\\w\\.]+/region/)|(secondlife://))\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?",
  749. boost::regex::perl|boost::regex::icase);
  750. mMenuName = "menu_url_slurl.xml";
  751. mTooltip = LLTrans::getString("TooltipSLURL");
  752. }
  753. std::string LLUrlEntryPlace::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
  754. {
  755. //
  756. // we handle SLURLs in the following formats:
  757. // - secondlife://Place/X/Y/Z
  758. // - secondlife://Place/X/Y
  759. //
  760. LLURI uri(url);
  761. std::string location = unescapeUrl(uri.hostName());
  762. LLSD path_array = uri.pathArray();
  763. S32 path_parts = path_array.size();
  764. if (path_parts == 3)
  765. {
  766. // handle slurl with (X,Y,Z) coordinates
  767. std::string x = path_array[0];
  768. std::string y = path_array[1];
  769. std::string z = path_array[2];
  770. return location + " (" + x + "," + y + "," + z + ")";
  771. }
  772. else if (path_parts == 2)
  773. {
  774. // handle slurl with (X,Y) coordinates
  775. std::string x = path_array[0];
  776. std::string y = path_array[1];
  777. return location + " (" + x + "," + y + ")";
  778. }
  779. return url;
  780. }
  781. std::string LLUrlEntryPlace::getLocation(const std::string &url) const
  782. {
  783. // return the part of the Url after secondlife:// part
  784. return ::getStringAfterToken(url, "://");
  785. }
  786. //
  787. // LLUrlEntryRegion Describes secondlife:///app/region/REGION_NAME/X/Y/Z URLs, e.g.
  788. // secondlife:///app/region/Ahern/128/128/0
  789. //
  790. LLUrlEntryRegion::LLUrlEntryRegion()
  791. {
  792. mPattern = boost::regex("secondlife:///app/region/[^/\\s]+(/\\d+)?(/\\d+)?(/\\d+)?/?",
  793. boost::regex::perl|boost::regex::icase);
  794. mMenuName = "menu_url_slurl.xml";
  795. mTooltip = LLTrans::getString("TooltipSLURL");
  796. }
  797. std::string LLUrlEntryRegion::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
  798. {
  799. //
  800. // we handle SLURLs in the following formats:
  801. // - secondlife:///app/region/Place/X/Y/Z
  802. // - secondlife:///app/region/Place/X/Y
  803. // - secondlife:///app/region/Place/X
  804. // - secondlife:///app/region/Place
  805. //
  806. LLSD path_array = LLURI(url).pathArray();
  807. S32 path_parts = path_array.size();
  808. if (path_parts < 3) // no region name
  809. {
  810. llwarns << "Failed to parse url [" << url << "]" << llendl;
  811. return url;
  812. }
  813. std::string label = unescapeUrl(path_array[2]); // region name
  814. if (path_parts > 3) // secondlife:///app/region/Place/X
  815. {
  816. std::string x = path_array[3];
  817. label += " (" + x;
  818. if (path_parts > 4) // secondlife:///app/region/Place/X/Y
  819. {
  820. std::string y = path_array[4];
  821. label += "," + y;
  822. if (path_parts > 5) // secondlife:///app/region/Place/X/Y/Z
  823. {
  824. std::string z = path_array[5];
  825. label = label + "," + z;
  826. }
  827. }
  828. label += ")";
  829. }
  830. return label;
  831. }
  832. std::string LLUrlEntryRegion::getLocation(const std::string &url) const
  833. {
  834. LLSD path_array = LLURI(url).pathArray();
  835. std::string region_name = unescapeUrl(path_array[2]);
  836. return region_name;
  837. }
  838. //
  839. // LLUrlEntryTeleport Describes a Second Life teleport Url, e.g.,
  840. // secondlife:///app/teleport/Ahern/50/50/50/
  841. // x-grid-location-info://lincoln.lindenlab.com/app/teleport/Ahern/50/50/50/
  842. //
  843. LLUrlEntryTeleport::LLUrlEntryTeleport()
  844. {
  845. mPattern = boost::regex(APP_HEADER_REGEX "/teleport/\\S+(/\\d+)?(/\\d+)?(/\\d+)?/?\\S*",
  846. boost::regex::perl|boost::regex::icase);
  847. mMenuName = "menu_url_teleport.xml";
  848. mTooltip = LLTrans::getString("TooltipTeleportUrl");
  849. }
  850. std::string LLUrlEntryTeleport::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
  851. {
  852. //
  853. // we handle teleport SLURLs in the following formats:
  854. // - secondlife:///app/teleport/Place/X/Y/Z
  855. // - secondlife:///app/teleport/Place/X/Y
  856. // - secondlife:///app/teleport/Place/X
  857. // - secondlife:///app/teleport/Place
  858. //
  859. LLURI uri(url);
  860. LLSD path_array = uri.pathArray();
  861. S32 path_parts = path_array.size();
  862. std::string host = uri.hostName();
  863. std::string label = LLTrans::getString("SLurlLabelTeleport");
  864. if (!host.empty())
  865. {
  866. label += " " + host;
  867. }
  868. if (path_parts == 6)
  869. {
  870. // handle teleport url with (X,Y,Z) coordinates
  871. std::string location = unescapeUrl(path_array[path_parts-4]);
  872. std::string x = path_array[path_parts-3];
  873. std::string y = path_array[path_parts-2];
  874. std::string z = path_array[path_parts-1];
  875. return label + " " + location + " (" + x + "," + y + "," + z + ")";
  876. }
  877. else if (path_parts == 5)
  878. {
  879. // handle teleport url with (X,Y) coordinates
  880. std::string location = unescapeUrl(path_array[path_parts-3]);
  881. std::string x = path_array[path_parts-2];
  882. std::string y = path_array[path_parts-1];
  883. return label + " " + location + " (" + x + "," + y + ")";
  884. }
  885. else if (path_parts == 4)
  886. {
  887. // handle teleport url with (X) coordinate only
  888. std::string location = unescapeUrl(path_array[path_parts-2]);
  889. std::string x = path_array[path_parts-1];
  890. return label + " " + location + " (" + x + ")";
  891. }
  892. else if (path_parts == 3)
  893. {
  894. // handle teleport url with no coordinates
  895. std::string location = unescapeUrl(path_array[path_parts-1]);
  896. return label + " " + location;
  897. }
  898. return url;
  899. }
  900. std::string LLUrlEntryTeleport::getLocation(const std::string &url) const
  901. {
  902. // return the part of the Url after ///app/teleport
  903. return ::getStringAfterToken(url, "app/teleport/");
  904. }
  905. //
  906. // LLUrlEntrySL Describes a generic SLURL, e.g., a Url that starts
  907. // with secondlife:// (used as a catch-all for cases not matched above)
  908. //
  909. LLUrlEntrySL::LLUrlEntrySL()
  910. {
  911. mPattern = boost::regex("secondlife://(\\w+)?(:\\d+)?/\\S+",
  912. boost::regex::perl|boost::regex::icase);
  913. mMenuName = "menu_url_slapp.xml";
  914. mTooltip = LLTrans::getString("TooltipSLAPP");
  915. }
  916. std::string LLUrlEntrySL::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
  917. {
  918. return unescapeUrl(url);
  919. }
  920. //
  921. // LLUrlEntrySLLabel Describes a generic SLURL, e.g., a Url that starts
  922. /// with secondlife:// with the ability to specify a custom label.
  923. //
  924. LLUrlEntrySLLabel::LLUrlEntrySLLabel()
  925. {
  926. mPattern = boost::regex("\\[secondlife://\\S+[ \t]+[^\\]]+\\]",
  927. boost::regex::perl|boost::regex::icase);
  928. mMenuName = "menu_url_slapp.xml";
  929. mTooltip = LLTrans::getString("TooltipSLAPP");
  930. }
  931. std::string LLUrlEntrySLLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
  932. {
  933. return getLabelFromWikiLink(url);
  934. }
  935. std::string LLUrlEntrySLLabel::getUrl(const std::string &string) const
  936. {
  937. return getUrlFromWikiLink(string);
  938. }
  939. std::string LLUrlEntrySLLabel::getTooltip(const std::string &string) const
  940. {
  941. // return a tooltip corresponding to the URL type instead of the generic one (EXT-4574)
  942. std::string url = getUrl(string);
  943. LLUrlMatch match;
  944. if (LLUrlRegistry::instance().findUrl(url, match))
  945. {
  946. return match.getTooltip();
  947. }
  948. // unrecognized URL? should not happen
  949. return LLUrlEntryBase::getTooltip(string);
  950. }
  951. bool LLUrlEntrySLLabel::underlineOnHoverOnly(const std::string &string) const
  952. {
  953. std::string url = getUrl(string);
  954. LLUrlMatch match;
  955. if (LLUrlRegistry::instance().findUrl(url, match))
  956. {
  957. return match.underlineOnHoverOnly();
  958. }
  959. // unrecognized URL? should not happen
  960. return LLUrlEntryBase::underlineOnHoverOnly(string);
  961. }
  962. //
  963. // LLUrlEntryWorldMap Describes secondlife:///<location> URLs
  964. //
  965. LLUrlEntryWorldMap::LLUrlEntryWorldMap()
  966. {
  967. mPattern = boost::regex(APP_HEADER_REGEX "/worldmap/\\S+/?(\\d+)?/?(\\d+)?/?(\\d+)?/?\\S*",
  968. boost::regex::perl|boost::regex::icase);
  969. mMenuName = "menu_url_map.xml";
  970. mTooltip = LLTrans::getString("TooltipMapUrl");
  971. }
  972. std::string LLUrlEntryWorldMap::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
  973. {
  974. //
  975. // we handle SLURLs in the following formats:
  976. // - secondlife:///app/worldmap/PLACE/X/Y/Z
  977. // - secondlife:///app/worldmap/PLACE/X/Y
  978. // - secondlife:///app/worldmap/PLACE/X
  979. //
  980. LLURI uri(url);
  981. LLSD path_array = uri.pathArray();
  982. S32 path_parts = path_array.size();
  983. if (path_parts < 3)
  984. {
  985. return url;
  986. }
  987. const std::string label = LLTrans::getString("SLurlLabelShowOnMap");
  988. std::string location = unescapeUrl(path_array[2]);
  989. std::string x = (path_parts > 3) ? path_array[3] : "128";
  990. std::string y = (path_parts > 4) ? path_array[4] : "128";
  991. std::string z = (path_parts > 5) ? path_array[5] : "0";
  992. return label + " " + location + " (" + x + "," + y + "," + z + ")";
  993. }
  994. std::string LLUrlEntryWorldMap::getLocation(const std::string &url) const
  995. {
  996. // return the part of the Url after secondlife:///app/worldmap/ part
  997. return ::getStringAfterToken(url, "app/worldmap/");
  998. }
  999. //
  1000. // LLUrlEntryNoLink lets us turn of URL detection with <nolink>...</nolink> tags
  1001. //
  1002. LLUrlEntryNoLink::LLUrlEntryNoLink()
  1003. {
  1004. mPattern = boost::regex("<nolink>.*?</nolink>",
  1005. boost::regex::perl|boost::regex::icase);
  1006. }
  1007. std::string LLUrlEntryNoLink::getUrl(const std::string &url) const
  1008. {
  1009. // return the text between the <nolink> and </nolink> tags
  1010. return url.substr(8, url.size()-8-9);
  1011. }
  1012. std::string LLUrlEntryNoLink::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
  1013. {
  1014. return getUrl(url);
  1015. }
  1016. LLStyle::Params LLUrlEntryNoLink::getStyle() const
  1017. {
  1018. // Don't render as URL (i.e. no context menu or hand cursor).
  1019. return LLStyle::Params().is_link(false);
  1020. }
  1021. //
  1022. // LLUrlEntryIcon describes an icon with <icon>...</icon> tags
  1023. //
  1024. LLUrlEntryIcon::LLUrlEntryIcon()
  1025. {
  1026. mPattern = boost::regex("<icon\\s*>\\s*([^<]*)?\\s*</icon\\s*>",
  1027. boost::regex::perl|boost::regex::icase);
  1028. }
  1029. std::string LLUrlEntryIcon::getUrl(const std::string &url) const
  1030. {
  1031. return LLStringUtil::null;
  1032. }
  1033. std::string LLUrlEntryIcon::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
  1034. {
  1035. return LLStringUtil::null;
  1036. }
  1037. std::string LLUrlEntryIcon::getIcon(const std::string &url)
  1038. {
  1039. // Grep icon info between <icon>...</icon> tags
  1040. // matches[1] contains the icon name/path
  1041. boost::match_results<std::string::const_iterator> matches;
  1042. mIcon = (boost::regex_match(url, matches, mPattern) && matches[1].matched)
  1043. ? matches[1]
  1044. : LLStringUtil::null;
  1045. LLStringUtil::trim(mIcon);
  1046. return mIcon;
  1047. }