summaryrefslogtreecommitdiff
path: root/indra/llui/llurlentry.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llui/llurlentry.cpp')
-rw-r--r--indra/llui/llurlentry.cpp546
1 files changed, 546 insertions, 0 deletions
diff --git a/indra/llui/llurlentry.cpp b/indra/llui/llurlentry.cpp
new file mode 100644
index 0000000000..85f9064115
--- /dev/null
+++ b/indra/llui/llurlentry.cpp
@@ -0,0 +1,546 @@
+/**
+ * @file llurlentry.cpp
+ * @author Martin Reddy
+ * @brief Describes the Url types that can be registered in LLUrlRegistry
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ *
+ * Copyright (c) 2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llurlentry.h"
+#include "lluri.h"
+#include "llcachename.h"
+#include "lltrans.h"
+
+LLUrlEntryBase::LLUrlEntryBase()
+{
+}
+
+LLUrlEntryBase::~LLUrlEntryBase()
+{
+}
+
+std::string LLUrlEntryBase::getUrl(const std::string &string)
+{
+ return escapeUrl(string);
+}
+
+std::string LLUrlEntryBase::getIDStringFromUrl(const std::string &url) const
+{
+ // return the id from a SLURL in the format /app/{cmd}/{id}/about
+ LLURI uri(url);
+ LLSD path_array = uri.pathArray();
+ if (path_array.size() == 4)
+ {
+ return path_array.get(2).asString();
+ }
+ return "";
+}
+
+std::string LLUrlEntryBase::unescapeUrl(const std::string &url) const
+{
+ return LLURI::unescape(url);
+}
+
+std::string LLUrlEntryBase::escapeUrl(const std::string &url) const
+{
+ static std::string no_escape_chars;
+ static bool initialized = false;
+ if (!initialized)
+ {
+ no_escape_chars =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789"
+ "-._~!$?&()*+,@:;=/%";
+
+ std::sort(no_escape_chars.begin(), no_escape_chars.end());
+ initialized = true;
+ }
+ return LLURI::escape(url, no_escape_chars, true);
+}
+
+std::string LLUrlEntryBase::getLabelFromWikiLink(const std::string &url)
+{
+ // return the label part from [http://www.example.org Label]
+ const char *text = url.c_str();
+ S32 start = 0;
+ while (! isspace(text[start]))
+ {
+ start++;
+ }
+ while (text[start] == ' ' || text[start] == '\t')
+ {
+ start++;
+ }
+ return url.substr(start, url.size()-start-1);
+}
+
+std::string LLUrlEntryBase::getUrlFromWikiLink(const std::string &string)
+{
+ // return the url part from [http://www.example.org Label]
+ const char *text = string.c_str();
+ S32 end = 0;
+ while (! isspace(text[end]))
+ {
+ end++;
+ }
+ return escapeUrl(string.substr(1, end-1));
+}
+
+void LLUrlEntryBase::addObserver(const std::string &id,
+ const std::string &url,
+ const LLUrlLabelCallback &cb)
+{
+ // add a callback to be notified when we have a label for the uuid
+ LLUrlEntryObserver observer;
+ observer.url = url;
+ observer.signal = new LLUrlLabelSignal();
+ if (observer.signal)
+ {
+ observer.signal->connect(cb);
+ mObservers.insert(std::pair<std::string, LLUrlEntryObserver>(id, observer));
+ }
+}
+
+void LLUrlEntryBase::callObservers(const std::string &id, const std::string &label)
+{
+ // notify all callbacks waiting on the given uuid
+ std::multimap<std::string, LLUrlEntryObserver>::iterator it;
+ for (it = mObservers.find(id); it != mObservers.end();)
+ {
+ // call the callback - give it the new label
+ LLUrlEntryObserver &observer = it->second;
+ (*observer.signal)(it->second.url, label);
+ // then remove the signal - we only need to call it once
+ delete observer.signal;
+ mObservers.erase(it++);
+ }
+}
+
+//
+// LLUrlEntryHTTP Describes generic http: and https: Urls
+//
+LLUrlEntryHTTP::LLUrlEntryHTTP()
+{
+ mPattern = boost::regex("https?://([-\\w\\.]+)+(:\\d+)?(:\\w+)?(@\\d+)?(@\\w+)?/?\\S*",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_http.xml";
+ mTooltip = LLTrans::getString("TooltipHttpUrl");
+ //mIcon = "gear.tga";
+}
+
+std::string LLUrlEntryHTTP::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return unescapeUrl(url);
+}
+
+//
+// LLUrlEntryHTTP Describes generic http: and https: Urls with custom label
+// We use the wikipedia syntax of [http://www.example.org Text]
+//
+LLUrlEntryHTTPLabel::LLUrlEntryHTTPLabel()
+{
+ mPattern = boost::regex("\\[https?://\\S+[ \t]+[^\\]]+\\]",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_http.xml";
+ mTooltip = LLTrans::getString("TooltipHttpUrl");
+}
+
+std::string LLUrlEntryHTTPLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return getLabelFromWikiLink(url);
+}
+
+std::string LLUrlEntryHTTPLabel::getUrl(const std::string &string)
+{
+ return getUrlFromWikiLink(string);
+}
+
+//
+// LLUrlEntrySLURL Describes generic http: and https: Urls
+//
+LLUrlEntrySLURL::LLUrlEntrySLURL()
+{
+ // see http://slurl.com/about.php for details on the SLURL format
+ mPattern = boost::regex("http://slurl.com/secondlife/\\S+/?(\\d+)?/?(\\d+)?/?(\\d+)?/?\\S*",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_slurl.xml";
+ mTooltip = LLTrans::getString("TooltipSLURL");
+}
+
+std::string LLUrlEntrySLURL::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ //
+ // we handle SLURLs in the following formats:
+ // - http://slurl.com/secondlife/Place/X/Y/Z
+ // - http://slurl.com/secondlife/Place/X/Y
+ // - http://slurl.com/secondlife/Place/X
+ // - http://slurl.com/secondlife/Place
+ //
+ LLURI uri(url);
+ LLSD path_array = uri.pathArray();
+ S32 path_parts = path_array.size();
+ if (path_parts == 5)
+ {
+ // handle slurl with (X,Y,Z) coordinates
+ std::string location = unescapeUrl(path_array[path_parts-4]);
+ std::string x = path_array[path_parts-3];
+ std::string y = path_array[path_parts-2];
+ std::string z = path_array[path_parts-1];
+ return location + " (" + x + "," + y + "," + z + ")";
+ }
+ else if (path_parts == 4)
+ {
+ // handle slurl with (X,Y) coordinates
+ std::string location = unescapeUrl(path_array[path_parts-3]);
+ std::string x = path_array[path_parts-2];
+ std::string y = path_array[path_parts-1];
+ return location + " (" + x + "," + y + ")";
+ }
+ else if (path_parts == 3)
+ {
+ // handle slurl with (X) coordinate
+ std::string location = unescapeUrl(path_array[path_parts-2]);
+ std::string x = path_array[path_parts-1];
+ return location + " (" + x + ")";
+ }
+ else if (path_parts == 2)
+ {
+ // handle slurl with no coordinates
+ std::string location = unescapeUrl(path_array[path_parts-1]);
+ return location;
+ }
+
+ return url;
+}
+
+std::string LLUrlEntrySLURL::getLocation(const std::string &url) const
+{
+ // return the part of the Url after slurl.com/secondlife/
+ const std::string search_string = "secondlife";
+ size_t pos = url.find(search_string);
+ if (pos == std::string::npos)
+ {
+ return "";
+ }
+
+ pos += search_string.size() + 1;
+ return url.substr(pos, url.size() - pos);
+}
+
+//
+// LLUrlEntryAgent Describes a Second Life agent Url, e.g.,
+// secondlife:///app/agent/0e346d8b-4433-4d66-a6b0-fd37083abc4c/about
+//
+LLUrlEntryAgent::LLUrlEntryAgent()
+{
+ mPattern = boost::regex("secondlife:///app/agent/[\\da-f-]+/about",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_agent.xml";
+ mTooltip = LLTrans::getString("TooltipAgentUrl");
+}
+
+void LLUrlEntryAgent::onAgentNameReceived(const LLUUID& id,
+ const std::string& first,
+ const std::string& last,
+ BOOL is_group)
+{
+ // received the agent name from the server - tell our observers
+ callObservers(id.asString(), first + " " + last);
+}
+
+std::string LLUrlEntryAgent::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ std::string id = getIDStringFromUrl(url);
+ if (gCacheName && ! id.empty())
+ {
+ LLUUID uuid(id);
+ std::string full_name;
+ if (gCacheName->getFullName(uuid, full_name))
+ {
+ return full_name;
+ }
+ else
+ {
+ gCacheName->get(uuid, FALSE, boost::bind(&LLUrlEntryAgent::onAgentNameReceived, this, _1, _2, _3, _4));
+ addObserver(id, url, cb);
+ }
+ }
+
+ return unescapeUrl(url);
+}
+
+//
+// LLUrlEntryGroup Describes a Second Life group Url, e.g.,
+// secondlife:///app/group/00005ff3-4044-c79f-9de8-fb28ae0df991/about
+//
+LLUrlEntryGroup::LLUrlEntryGroup()
+{
+ mPattern = boost::regex("secondlife:///app/group/[\\da-f-]+/about",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_group.xml";
+ mTooltip = LLTrans::getString("TooltipGroupUrl");
+}
+
+void LLUrlEntryGroup::onGroupNameReceived(const LLUUID& id,
+ const std::string& first,
+ const std::string& last,
+ BOOL is_group)
+{
+ // received the group name from the server - tell our observers
+ callObservers(id.asString(), first);
+}
+
+std::string LLUrlEntryGroup::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ std::string id = getIDStringFromUrl(url);
+ if (gCacheName && ! id.empty())
+ {
+ LLUUID uuid(id);
+ std::string group_name;
+ if (gCacheName->getGroupName(uuid, group_name))
+ {
+ return group_name;
+ }
+ else
+ {
+ gCacheName->get(uuid, TRUE, boost::bind(&LLUrlEntryGroup::onGroupNameReceived, this, _1, _2, _3, _4));
+ addObserver(id, url, cb);
+ }
+ }
+
+ return unescapeUrl(url);
+}
+
+///
+/// LLUrlEntryEvent Describes a Second Life event Url, e.g.,
+/// secondlife:///app/event/700727/about
+///
+LLUrlEntryEvent::LLUrlEntryEvent()
+{
+ mPattern = boost::regex("secondlife:///app/event/[\\da-f-]+/about",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_event.xml";
+ mTooltip = LLTrans::getString("TooltipEventUrl");
+}
+
+std::string LLUrlEntryEvent::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()
+{
+ mPattern = boost::regex("secondlife:///app/classified/[\\da-f-]+/about",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_classified.xml";
+ mTooltip = LLTrans::getString("TooltipClassifiedUrl");
+}
+
+std::string LLUrlEntryClassified::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return unescapeUrl(url);
+}
+
+///
+/// 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");
+}
+
+std::string LLUrlEntryParcel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return unescapeUrl(url);
+}
+
+//
+// LLUrlEntryTeleport Describes a Second Life teleport Url, e.g.,
+// secondlife:///app/teleport/Ahern/50/50/50/
+//
+LLUrlEntryTeleport::LLUrlEntryTeleport()
+{
+ mPattern = boost::regex("secondlife:///app/teleport/\\S+(/\\d+)?(/\\d+)?(/\\d+)?/?\\S*",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_teleport.xml";
+ mTooltip = LLTrans::getString("TooltipTeleportUrl");
+}
+
+std::string LLUrlEntryTeleport::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ //
+ // we handle teleport SLURLs in the following formats:
+ // - secondlife:///app/teleport/Place/X/Y/Z
+ // - secondlife:///app/teleport/Place/X/Y
+ // - secondlife:///app/teleport/Place/X
+ // - secondlife:///app/teleport/Place
+ //
+ LLURI uri(url);
+ LLSD path_array = uri.pathArray();
+ S32 path_parts = path_array.size();
+ if (path_parts == 6)
+ {
+ // handle teleport url with (X,Y,Z) coordinates
+ std::string location = unescapeUrl(path_array[path_parts-4]);
+ std::string x = path_array[path_parts-3];
+ std::string y = path_array[path_parts-2];
+ std::string z = path_array[path_parts-1];
+ return "Teleport to " + location + " (" + x + "," + y + "," + z + ")";
+ }
+ else if (path_parts == 5)
+ {
+ // handle teleport url with (X,Y) coordinates
+ std::string location = unescapeUrl(path_array[path_parts-3]);
+ std::string x = path_array[path_parts-2];
+ std::string y = path_array[path_parts-1];
+ return "Teleport to " + location + " (" + x + "," + y + ")";
+ }
+ else if (path_parts == 4)
+ {
+ // handle teleport url with (X) coordinate only
+ std::string location = unescapeUrl(path_array[path_parts-2]);
+ std::string x = path_array[path_parts-1];
+ return "Teleport to " + location + " (" + x + ")";
+ }
+ else if (path_parts == 3)
+ {
+ // handle teleport url with no coordinates
+ std::string location = unescapeUrl(path_array[path_parts-1]);
+ return "Teleport to " + location;
+ }
+
+ return url;
+}
+
+std::string LLUrlEntryTeleport::getLocation(const std::string &url) const
+{
+ // return the part of the Url after ///app/teleport
+ const std::string search_string = "teleport";
+ size_t pos = url.find(search_string);
+ if (pos == std::string::npos)
+ {
+ return "";
+ }
+
+ pos += search_string.size() + 1;
+ return url.substr(pos, url.size() - pos);
+}
+
+///
+/// LLUrlEntryObjectIM Describes a Second Life object instant msg Url, e.g.,
+/// secondlife:///app/objectim/<sessionid>
+///
+LLUrlEntryObjectIM::LLUrlEntryObjectIM()
+{
+ mPattern = boost::regex("secondlife:///app/objectim/[\\da-f-]+\\??\\S*",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_objectim.xml";
+ mTooltip = LLTrans::getString("TooltipObjectIMUrl");
+}
+
+std::string LLUrlEntryObjectIM::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ LLURI uri(url);
+ LLSD params = uri.queryMap();
+ if (params.has("name"))
+ {
+ // look for a ?name=<obj-name> param in the url
+ // and use that as the label if present.
+ std::string name = params.get("name");
+ LLStringUtil::trim(name);
+ if (name.empty())
+ {
+ name = LLTrans::getString("Unnamed");
+ }
+ return name;
+ }
+
+ return unescapeUrl(url);
+}
+
+std::string LLUrlEntryObjectIM::getLocation(const std::string &url) const
+{
+ LLURI uri(url);
+ LLSD params = uri.queryMap();
+ if (params.has("slurl"))
+ {
+ return params.get("slurl");
+ }
+
+ return "";
+}
+
+//
+// LLUrlEntrySL Describes a generic SLURL, e.g., a Url that starts
+// with secondlife:// (used as a catch-all for cases not matched above)
+//
+LLUrlEntrySL::LLUrlEntrySL()
+{
+ mPattern = boost::regex("secondlife://(\\w+)?(:\\d+)?/\\S+",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_slapp.xml";
+ mTooltip = LLTrans::getString("TooltipSLAPP");
+}
+
+std::string LLUrlEntrySL::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return unescapeUrl(url);
+}
+
+//
+// LLUrlEntrySLLabel Describes a generic SLURL, e.g., a Url that starts
+/// with secondlife:// with the ability to specify a custom label.
+//
+LLUrlEntrySLLabel::LLUrlEntrySLLabel()
+{
+ mPattern = boost::regex("\\[secondlife://\\S+[ \t]+[^\\]]+\\]",
+ boost::regex::perl|boost::regex::icase);
+ mMenuName = "menu_url_slapp.xml";
+ mTooltip = LLTrans::getString("TooltipSLAPP");
+}
+
+std::string LLUrlEntrySLLabel::getLabel(const std::string &url, const LLUrlLabelCallback &cb)
+{
+ return getLabelFromWikiLink(url);
+}
+
+std::string LLUrlEntrySLLabel::getUrl(const std::string &string)
+{
+ return getUrlFromWikiLink(string);
+}
+