PageRenderTime 39ms CodeModel.GetById 10ms app.highlight 24ms RepoModel.GetById 2ms app.codeStats 0ms

/indra/llui/llurlregistry.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 263 lines | 181 code | 26 blank | 56 comment | 39 complexity | 55be8ea3ff98b6b93be7fb3137e44cfc MD5 | raw file
  1/** 
  2 * @file llurlregistry.cpp
  3 * @author Martin Reddy
  4 * @brief Contains a set of Url types that can be matched in a string
  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 "llurlregistry.h"
 30
 31#include <boost/regex.hpp>
 32
 33// default dummy callback that ignores any label updates from the server
 34void LLUrlRegistryNullCallback(const std::string &url, const std::string &label, const std::string& icon)
 35{
 36}
 37
 38LLUrlRegistry::LLUrlRegistry()
 39{
 40	mUrlEntry.reserve(20);
 41
 42	// Urls are matched in the order that they were registered
 43	registerUrl(new LLUrlEntryNoLink());
 44	registerUrl(new LLUrlEntryIcon());
 45	registerUrl(new LLUrlEntrySLURL());
 46	registerUrl(new LLUrlEntryHTTP());
 47	registerUrl(new LLUrlEntryHTTPLabel());
 48	registerUrl(new LLUrlEntryAgentCompleteName());
 49	registerUrl(new LLUrlEntryAgentDisplayName());
 50	registerUrl(new LLUrlEntryAgentUserName());
 51	// LLUrlEntryAgent*Name must appear before LLUrlEntryAgent since 
 52	// LLUrlEntryAgent is a less specific (catchall for agent urls)
 53	registerUrl(new LLUrlEntryAgent());
 54	registerUrl(new LLUrlEntryGroup());
 55	registerUrl(new LLUrlEntryParcel());
 56	registerUrl(new LLUrlEntryTeleport());
 57	registerUrl(new LLUrlEntryRegion());
 58	registerUrl(new LLUrlEntryWorldMap());
 59	registerUrl(new LLUrlEntryObjectIM());
 60	registerUrl(new LLUrlEntryPlace());
 61	registerUrl(new LLUrlEntryInventory());
 62	registerUrl(new LLUrlEntryObjectIM());
 63	//LLUrlEntrySL and LLUrlEntrySLLabel have more common pattern, 
 64	//so it should be registered in the end of list
 65	registerUrl(new LLUrlEntrySL());
 66	registerUrl(new LLUrlEntrySLLabel());
 67	// most common pattern is a URL without any protocol,
 68	// e.g., "secondlife.com"
 69	registerUrl(new LLUrlEntryHTTPNoProtocol());	
 70}
 71
 72LLUrlRegistry::~LLUrlRegistry()
 73{
 74	// free all of the LLUrlEntryBase objects we are holding
 75	std::vector<LLUrlEntryBase *>::iterator it;
 76	for (it = mUrlEntry.begin(); it != mUrlEntry.end(); ++it)
 77	{
 78		delete *it;
 79	}
 80}
 81
 82void LLUrlRegistry::registerUrl(LLUrlEntryBase *url, bool force_front)
 83{
 84	if (url)
 85	{
 86		if (force_front)  // IDEVO
 87			mUrlEntry.insert(mUrlEntry.begin(), url);
 88		else
 89		mUrlEntry.push_back(url);
 90	}
 91}
 92
 93static bool matchRegex(const char *text, boost::regex regex, U32 &start, U32 &end)
 94{
 95	boost::cmatch result;
 96	bool found;
 97
 98	// regex_search can potentially throw an exception, so check for it
 99	try
100	{
101		found = boost::regex_search(text, result, regex);
102	}
103	catch (std::runtime_error &)
104	{
105		return false;
106	}
107
108	if (! found)
109	{
110		return false;
111	}
112
113	// return the first/last character offset for the matched substring
114	start = static_cast<U32>(result[0].first - text);
115	end = static_cast<U32>(result[0].second - text) - 1;
116
117	// we allow certain punctuation to terminate a Url but not match it,
118	// e.g., "http://foo.com/." should just match "http://foo.com/"
119	if (text[end] == '.' || text[end] == ',')
120	{
121		end--;
122	}
123	// ignore a terminating ')' when Url contains no matching '('
124	// see DEV-19842 for details
125	else if (text[end] == ')' && std::string(text+start, end-start).find('(') == std::string::npos)
126	{
127		end--;
128	}
129
130	return true;
131}
132
133static bool stringHasUrl(const std::string &text)
134{
135	// fast heuristic test for a URL in a string. This is used
136	// to avoid lots of costly regex calls, BUT it needs to be
137	// kept in sync with the LLUrlEntry regexes we support.
138	return (text.find("://") != std::string::npos ||
139			text.find("www.") != std::string::npos ||
140			text.find(".com") != std::string::npos ||
141			text.find(".net") != std::string::npos ||
142			text.find(".edu") != std::string::npos ||
143			text.find(".org") != std::string::npos ||
144			text.find("<nolink>") != std::string::npos ||
145			text.find("<icon") != std::string::npos);
146}
147
148bool LLUrlRegistry::findUrl(const std::string &text, LLUrlMatch &match, const LLUrlLabelCallback &cb)
149{
150	// avoid costly regexes if there is clearly no URL in the text
151	if (! stringHasUrl(text))
152	{
153		return false;
154	}
155
156	// find the first matching regex from all url entries in the registry
157	U32 match_start = 0, match_end = 0;
158	LLUrlEntryBase *match_entry = NULL;
159
160	std::vector<LLUrlEntryBase *>::iterator it;
161	for (it = mUrlEntry.begin(); it != mUrlEntry.end(); ++it)
162	{
163		LLUrlEntryBase *url_entry = *it;
164
165		U32 start = 0, end = 0;
166		if (matchRegex(text.c_str(), url_entry->getPattern(), start, end))
167		{
168			// does this match occur in the string before any other match
169			if (start < match_start || match_entry == NULL)
170			{
171				match_start = start;
172				match_end = end;
173				match_entry = url_entry;
174			}
175		}
176	}
177	
178	// did we find a match? if so, return its details in the match object
179	if (match_entry)
180	{
181		// fill in the LLUrlMatch object and return it
182		std::string url = text.substr(match_start, match_end - match_start + 1);
183		match.setValues(match_start, match_end,
184						match_entry->getUrl(url),
185						match_entry->getLabel(url, cb),
186						match_entry->getTooltip(url),
187						match_entry->getIcon(url),
188						match_entry->getStyle(),
189						match_entry->getMenuName(),
190						match_entry->getLocation(url),
191						match_entry->getID(url),
192						match_entry->underlineOnHoverOnly(url));
193		return true;
194	}
195
196	return false;
197}
198
199bool LLUrlRegistry::findUrl(const LLWString &text, LLUrlMatch &match, const LLUrlLabelCallback &cb)
200{
201	// boost::regex_search() only works on char or wchar_t
202	// types, but wchar_t is only 2-bytes on Win32 (not 4).
203	// So we use UTF-8 to make this work the same everywhere.
204	std::string utf8_text = wstring_to_utf8str(text);
205	if (findUrl(utf8_text, match, cb))
206	{
207		// we cannot blindly return the start/end offsets from
208		// the UTF-8 string because it is a variable-length
209		// character encoding, so we need to update the start
210		// and end values to be correct for the wide string.
211		LLWString wurl = utf8str_to_wstring(match.getUrl());
212		S32 start = text.find(wurl);
213		if (start == std::string::npos)
214		{
215			return false;
216		}
217		S32 end = start + wurl.size() - 1;
218
219		match.setValues(start, end, match.getUrl(), 
220						match.getLabel(),
221						match.getTooltip(),
222						match.getIcon(),
223						match.getStyle(),
224						match.getMenuName(),
225						match.getLocation(),
226						match.getID(),
227						match.underlineOnHoverOnly());
228		return true;
229	}
230	return false;
231}
232
233bool LLUrlRegistry::hasUrl(const std::string &text)
234{
235	LLUrlMatch match;
236	return findUrl(text, match);
237}
238
239bool LLUrlRegistry::hasUrl(const LLWString &text)
240{
241	LLUrlMatch match;
242	return findUrl(text, match);
243}
244
245bool LLUrlRegistry::isUrl(const std::string &text)
246{
247	LLUrlMatch match;
248	if (findUrl(text, match))
249	{
250		return (match.getStart() == 0 && match.getEnd() >= text.size()-1);
251	}
252	return false;
253}
254
255bool LLUrlRegistry::isUrl(const LLWString &text)
256{
257	LLUrlMatch match;
258	if (findUrl(text, match))
259	{
260		return (match.getStart() == 0 && match.getEnd() >= text.size()-1);
261	}
262	return false;
263}