From 7cd1020f2953b776e1878e7b4e365d4b23e6b07b Mon Sep 17 00:00:00 2001
From: Martin Reddy <lynx@lindenlab.com>
Date: Mon, 14 Sep 2009 20:01:20 +0000
Subject: [PATCH] DEV-39188: Removed the custom support for
 secondlife:///app/events and secondlife:///app/classifieds URLs because we do
 not have handlers for these SLAPPs in 2.0.

Added support for secondlife://<location/<x>/<y>/<z> URLs, in addition
to the http://slurl.com SLURLs.

Also optimized LLUrlRegistry:findUrl() so that it avoids lots of
expensive regex calls when there is no URL in the text.
---
 indra/llui/llurlentry.cpp            | 81 +++++++++++++++++-----------
 indra/llui/llurlentry.h              | 28 ++++------
 indra/llui/llurlregistry.cpp         |  7 ++-
 indra/llui/tests/llurlentry_test.cpp | 72 ++++++++++++-------------
 4 files changed, 97 insertions(+), 91 deletions(-)

diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp
index 85f9064115..c20212c375 100644
--- a/indra/llui/llurlentry.cpp
+++ b/indra/llui/llurlentry.cpp
@@ -339,54 +339,75 @@ std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCa
 }
 
 ///
-/// LLUrlEntryEvent Describes a Second Life event Url, e.g.,
-/// secondlife:///app/event/700727/about
+/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
+/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
 ///
-LLUrlEntryEvent::LLUrlEntryEvent()
+LLUrlEntryParcel::LLUrlEntryParcel()
 {
-	mPattern = boost::regex("secondlife:///app/event/[\\da-f-]+/about",
+	mPattern = boost::regex("secondlife:///app/parcel/[\\da-f-]+/about",
 							boost::regex::perl|boost::regex::icase);
-	mMenuName = "menu_url_event.xml";
-	mTooltip = LLTrans::getString("TooltipEventUrl");
+	mMenuName = "menu_url_parcel.xml";
+	mTooltip = LLTrans::getString("TooltipParcelUrl");
 }
 
-std::string LLUrlEntryEvent::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
 {
 	return unescapeUrl(url);
 }
 
-///
-/// LLUrlEntryClassified Describes a Second Life classified Url, e.g.,
-/// secondlife:///app/classified/00128854-c36a-5649-7ca6-5dfaa7514ab2/about
-///
-LLUrlEntryClassified::LLUrlEntryClassified()
+//
+// LLUrlEntryPlace Describes secondlife:///<location> URLs
+//
+LLUrlEntryPlace::LLUrlEntryPlace()
 {
-	mPattern = boost::regex("secondlife:///app/classified/[\\da-f-]+/about",
+	mPattern = boost::regex("secondlife://\\S+/?(\\d+/\\d+/\\d+|\\d+/\\d+)/?",
 							boost::regex::perl|boost::regex::icase);
-	mMenuName = "menu_url_classified.xml";
-	mTooltip = LLTrans::getString("TooltipClassifiedUrl");
+	mMenuName = "menu_url_slurl.xml";
+	mTooltip = LLTrans::getString("TooltipSLURL");
 }
 
-std::string LLUrlEntryClassified::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+std::string LLUrlEntryPlace::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
 {
-	return unescapeUrl(url);
-}
+	//
+	// we handle SLURLs in the following formats:
+	//   - secondlife://Place/X/Y/Z
+	//   - secondlife://Place/X/Y
+	//
+	LLURI uri(url);
+	std::string location = unescapeUrl(uri.hostName());
+	LLSD path_array = uri.pathArray();
+	S32 path_parts = path_array.size();
+	if (path_parts == 3)
+	{
+		// handle slurl with (X,Y,Z) coordinates
+		std::string x = path_array[0];
+		std::string y = path_array[1];
+		std::string z = path_array[2];
+		return location + " (" + x + "," + y + "," + z + ")";
+	}
+	else if (path_parts == 2)
+	{
+		// handle slurl with (X,Y) coordinates
+		std::string x = path_array[0];
+		std::string y = path_array[1];
+		return location + " (" + x + "," + y + ")";
+	}
 
-///
-/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
-/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
-///
-LLUrlEntryParcel::LLUrlEntryParcel()
-{
-	mPattern = boost::regex("secondlife:///app/parcel/[\\da-f-]+/about",
-							boost::regex::perl|boost::regex::icase);
-	mMenuName = "menu_url_parcel.xml";
-	mTooltip = LLTrans::getString("TooltipParcelUrl");
+	return url;
 }
 
-std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+std::string LLUrlEntryPlace::getLocation(const std::string &url) const
 {
-	return unescapeUrl(url);
+	// return the part of the Url after secondlife:// part
+	const std::string search_string = "://";
+	size_t pos = url.find(search_string);
+	if (pos == std::string::npos)
+	{
+		return "";
+	}
+
+	pos += search_string.size();
+	return url.substr(pos, url.size() - pos);
 }
 
 //
diff --git a/indra/llui/llurlentry.h b/indra/llui/llurlentry.h
index f3e76dbec0..54053872df 100644
--- a/indra/llui/llurlentry.h
+++ b/indra/llui/llurlentry.h
@@ -170,36 +170,26 @@ private:
 };
 
 ///
-/// LLUrlEntryEvent Describes a Second Life event Url, e.g.,
-/// secondlife:///app/event/700727/about
-///
-class LLUrlEntryEvent : public LLUrlEntryBase
-{
-public:
-	LLUrlEntryEvent();
-	/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
-};
-
-///
-/// LLUrlEntryClassified Describes a Second Life classified Url, e.g.,
-/// secondlife:///app/classified/00128854-c36a-5649-7ca6-5dfaa7514ab2/about
+/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
+/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
 ///
-class LLUrlEntryClassified : public LLUrlEntryBase
+class LLUrlEntryParcel : public LLUrlEntryBase
 {
 public:
-	LLUrlEntryClassified();
+	LLUrlEntryParcel();
 	/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
 };
 
 ///
-/// LLUrlEntryParcel Describes a Second Life parcel Url, e.g.,
-/// secondlife:///app/parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/about
+/// LLUrlEntryPlace Describes a Second Life location Url, e.g.,
+/// secondlife:///Ahern/50/50/50
 ///
-class LLUrlEntryParcel : public LLUrlEntryBase
+class LLUrlEntryPlace : public LLUrlEntryBase
 {
 public:
-	LLUrlEntryParcel();
+	LLUrlEntryPlace();
 	/*virtual*/ std::string getLabel(const std::string &url, const LLUrlLabelCallback &cb);
+	/*virtual*/ std::string getLocation(const std::string &url) const;
 };
 
 ///
diff --git a/indra/llui/llurlregistry.cpp b/indra/llui/llurlregistry.cpp
index 938375ad13..f2d340deb7 100644
--- a/indra/llui/llurlregistry.cpp
+++ b/indra/llui/llurlregistry.cpp
@@ -49,11 +49,10 @@ LLUrlRegistry::LLUrlRegistry()
 	registerUrl(new LLUrlEntryHTTPLabel());
 	registerUrl(new LLUrlEntryAgent());
 	registerUrl(new LLUrlEntryGroup());
-	registerUrl(new LLUrlEntryEvent());
-	registerUrl(new LLUrlEntryClassified());
 	registerUrl(new LLUrlEntryParcel());
 	registerUrl(new LLUrlEntryTeleport());
 	registerUrl(new LLUrlEntryObjectIM());
+	registerUrl(new LLUrlEntryPlace());
 	registerUrl(new LLUrlEntrySL());
 	registerUrl(new LLUrlEntrySLLabel());
 }
@@ -118,8 +117,8 @@ static bool matchRegex(const char *text, boost::regex regex, U32 &start, U32 &en
 
 bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LLUrlLabelCallback &cb)
 {
-	// test for the trivial case of no text and get out fast
-	if (text.empty())
+	// avoid costly regexes if there is clearly no URL in the text
+	if (text.find("://") == std::string::npos)
 	{
 		return false;
 	}
diff --git a/indra/llui/tests/llurlentry_test.cpp b/indra/llui/tests/llurlentry_test.cpp
index 610ee3349b..1e7a0f7f2c 100644
--- a/indra/llui/tests/llurlentry_test.cpp
+++ b/indra/llui/tests/llurlentry_test.cpp
@@ -308,56 +308,52 @@ namespace tut
 	void object::test<6>()
 	{
 		//
-		// test LLUrlEntryEvent - secondlife://app/event Urls
+		// test LLUrlEntryPlace - secondlife://<location> URLs
 		//
-		LLUrlEntryEvent url;
+		LLUrlEntryPlace url;
 		boost::regex r = url.getPattern();
 
-		testRegex("Invalid Event Url", r,
-				  "secondlife:///app/event/FOO/about",
+		testRegex("no valid slurl [1]", r,
+				  "secondlife://Ahern/FOO/50/",
 				  "");
 
-		testRegex("Event Url ", r,
-				  "secondlife:///app/event/700727/about",
-				  "secondlife:///app/event/700727/about");
+		testRegex("Ahern (50,50,50) [1]", r,
+				  "secondlife://Ahern/50/50/50/",
+				  "secondlife://Ahern/50/50/50/");
 
-		testRegex("Event Url in text", r,
-				  "XXX secondlife:///app/event/700727/about XXX",
-				  "secondlife:///app/event/700727/about");
+		testRegex("Ahern (50,50,50) [2]", r,
+				  "XXX secondlife://Ahern/50/50/50/ XXX",
+				  "secondlife://Ahern/50/50/50/");
 
-		testRegex("Event Url multicase", r,
-				  "XXX secondlife:///APP/Event/700727/about XXX",
-				  "secondlife:///APP/Event/700727/about");
-	}
+		testRegex("Ahern (50,50,50) [3]", r,
+				  "XXX secondlife://Ahern/50/50/50 XXX",
+				  "secondlife://Ahern/50/50/50");
 
-	template<> template<>
-	void object::test<7>()
-	{
-		//
-		// test LLUrlEntryClassified - secondlife://app/classified Urls
-		//
-		LLUrlEntryClassified url;
-		boost::regex r = url.getPattern();
+		testRegex("Ahern (50,50,50) multicase", r,
+				  "XXX SecondLife://Ahern/50/50/50/ XXX",
+				  "SecondLife://Ahern/50/50/50/");
 
-		testRegex("Invalid Classified Url", r,
-				  "secondlife:///app/classified/00128854-XXXX-5649-7ca6-5dfaa7514ab2/about",
-				  "");
+		testRegex("Ahern (50,50) [1]", r,
+				  "XXX secondlife://Ahern/50/50/ XXX",
+				  "secondlife://Ahern/50/50/");
 
-		testRegex("Classified Url ", r,
-				  "secondlife:///app/classified/00128854-c36a-5649-7ca6-5dfaa7514ab2/about",
-				  "secondlife:///app/classified/00128854-c36a-5649-7ca6-5dfaa7514ab2/about");
+		testRegex("Ahern (50,50) [2]", r,
+				  "XXX secondlife://Ahern/50/50 XXX",
+				  "secondlife://Ahern/50/50");
 
-		testRegex("Classified Url in text", r,
-				  "XXX secondlife:///app/classified/00128854-c36a-5649-7ca6-5dfaa7514ab2/about XXX",
-				  "secondlife:///app/classified/00128854-c36a-5649-7ca6-5dfaa7514ab2/about");
+		// DEV-21577: In-world SLURLs containing "(" or ")" are not treated as a hyperlink in chat
+		testRegex("SLURL with brackets", r,
+				  "XXX secondlife://Burning%20Life%20(Hyper)/27/210/30 XXX",
+				  "secondlife://Burning%20Life%20(Hyper)/27/210/30");
 
-		testRegex("Classified Url multicase", r,
-				  "XXX secondlife:///APP/Classified/00128854-c36a-5649-7ca6-5dfaa7514ab2/About XXX",
-				  "secondlife:///APP/Classified/00128854-c36a-5649-7ca6-5dfaa7514ab2/About");
+		// DEV-35459: SLURLs and teleport Links not parsed properly
+		testRegex("SLURL with quote", r,
+				  "XXX secondlife://A'ksha%20Oasis/41/166/701 XXX",
+				  "secondlife://A'ksha%20Oasis/41/166/701");
 	}
 
 	template<> template<>
-	void object::test<8>()
+	void object::test<7>()
 	{
 		//
 		// test LLUrlEntryParcel - secondlife://app/parcel Urls
@@ -382,7 +378,7 @@ namespace tut
 				  "secondlife:///APP/Parcel/0000060e-4b39-e00b-d0c3-d98b1934e3a8/About");
 	}
 	template<> template<>
-	void object::test<9>()
+	void object::test<8>()
 	{
 		//
 		// test LLUrlEntryTeleport - secondlife://app/teleport URLs
@@ -458,7 +454,7 @@ namespace tut
 	}
 
 	template<> template<>
-	void object::test<10>()
+	void object::test<9>()
 	{
 		//
 		// test LLUrlEntrySL - general secondlife:// URLs
@@ -496,7 +492,7 @@ namespace tut
 	}
 
 	template<> template<>
-	void object::test<11>()
+	void object::test<10>()
 	{
 		//
 		// test LLUrlEntrySLLabel - general secondlife:// URLs with labels
-- 
GitLab