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