PageRenderTime 93ms CodeModel.GetById 12ms app.highlight 69ms RepoModel.GetById 1ms app.codeStats 0ms

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