PageRenderTime 37ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/indra/llcommon/lluri.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 612 lines | 497 code | 56 blank | 59 comment | 96 complexity | afa1174d9100594d8c4cc9766f00fa74 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file lluri.cpp
  3. * @author Phoenix
  4. * @date 2006-02-08
  5. * @brief Implementation of the LLURI class.
  6. *
  7. * $LicenseInfo:firstyear=2006&license=viewerlgpl$
  8. * Second Life Viewer Source Code
  9. * Copyright (C) 2010, Linden Research, Inc.
  10. *
  11. * This library is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU Lesser General Public
  13. * License as published by the Free Software Foundation;
  14. * version 2.1 of the License only.
  15. *
  16. * This library is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. * Lesser General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Lesser General Public
  22. * License along with this library; if not, write to the Free Software
  23. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  24. *
  25. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  26. * $/LicenseInfo$
  27. */
  28. #include "linden_common.h"
  29. #include "llapp.h"
  30. #include "lluri.h"
  31. #include "llsd.h"
  32. #include <iomanip>
  33. #include "lluuid.h"
  34. // system includes
  35. #include <boost/tokenizer.hpp>
  36. void encode_character(std::ostream& ostr, std::string::value_type val)
  37. {
  38. ostr << "%"
  39. << std::uppercase
  40. << std::hex
  41. << std::setw(2)
  42. << std::setfill('0')
  43. // VWR-4010 Cannot cast to U32 because sign-extension on
  44. // chars > 128 will result in FFFFFFC3 instead of F3.
  45. << static_cast<S32>(static_cast<U8>(val))
  46. // reset stream state
  47. << std::nouppercase
  48. << std::dec
  49. << std::setfill(' ');
  50. }
  51. // static
  52. std::string LLURI::escape(
  53. const std::string& str,
  54. const std::string& allowed,
  55. bool is_allowed_sorted)
  56. {
  57. // *NOTE: This size determination feels like a good value to
  58. // me. If someone wante to come up with a more precise heuristic
  59. // with some data to back up the assertion that 'sort is good'
  60. // then feel free to change this test a bit.
  61. if(!is_allowed_sorted && (str.size() > 2 * allowed.size()))
  62. {
  63. // if it's already sorted, or if the url is quite long, we
  64. // want to optimize this process.
  65. std::string sorted_allowed(allowed);
  66. std::sort(sorted_allowed.begin(), sorted_allowed.end());
  67. return escape(str, sorted_allowed, true);
  68. }
  69. std::ostringstream ostr;
  70. std::string::const_iterator it = str.begin();
  71. std::string::const_iterator end = str.end();
  72. std::string::value_type c;
  73. if(is_allowed_sorted)
  74. {
  75. std::string::const_iterator allowed_begin(allowed.begin());
  76. std::string::const_iterator allowed_end(allowed.end());
  77. for(; it != end; ++it)
  78. {
  79. c = *it;
  80. if(std::binary_search(allowed_begin, allowed_end, c))
  81. {
  82. ostr << c;
  83. }
  84. else
  85. {
  86. encode_character(ostr, c);
  87. }
  88. }
  89. }
  90. else
  91. {
  92. for(; it != end; ++it)
  93. {
  94. c = *it;
  95. if(allowed.find(c) == std::string::npos)
  96. {
  97. encode_character(ostr, c);
  98. }
  99. else
  100. {
  101. ostr << c;
  102. }
  103. }
  104. }
  105. return ostr.str();
  106. }
  107. // static
  108. std::string LLURI::unescape(const std::string& str)
  109. {
  110. std::ostringstream ostr;
  111. std::string::const_iterator it = str.begin();
  112. std::string::const_iterator end = str.end();
  113. for(; it != end; ++it)
  114. {
  115. if((*it) == '%')
  116. {
  117. ++it;
  118. if(it == end) break;
  119. U8 c = hex_as_nybble(*it++);
  120. c = c << 4;
  121. if (it == end) break;
  122. c |= hex_as_nybble(*it);
  123. ostr.put((char)c);
  124. }
  125. else
  126. {
  127. ostr.put(*it);
  128. }
  129. }
  130. return ostr.str();
  131. }
  132. namespace
  133. {
  134. const std::string unreserved()
  135. {
  136. static const std::string s =
  137. "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
  138. "0123456789"
  139. "-._~";
  140. return s;
  141. }
  142. const std::string sub_delims()
  143. {
  144. static const std::string s = "!$&'()*+,;=";
  145. return s;
  146. }
  147. std::string escapeHostAndPort(const std::string& s)
  148. { return LLURI::escape(s, unreserved() + sub_delims() +":"); }
  149. std::string escapePathComponent(const std::string& s)
  150. { return LLURI::escape(s, unreserved() + sub_delims() + ":@"); }
  151. std::string escapeQueryVariable(const std::string& s)
  152. { return LLURI::escape(s, unreserved() + ":@!$'()*+,"); } // sub_delims - "&;=" + ":@"
  153. std::string escapeQueryValue(const std::string& s)
  154. { return LLURI::escape(s, unreserved() + ":@!$'()*+,="); } // sub_delims - "&;" + ":@"
  155. }
  156. //static
  157. std::string LLURI::escape(const std::string& str)
  158. {
  159. static std::string default_allowed = unreserved();
  160. static bool initialized = false;
  161. if(!initialized)
  162. {
  163. std::sort(default_allowed.begin(), default_allowed.end());
  164. initialized = true;
  165. }
  166. return escape(str, default_allowed, true);
  167. }
  168. LLURI::LLURI()
  169. {
  170. }
  171. LLURI::LLURI(const std::string& escaped_str)
  172. {
  173. std::string::size_type delim_pos;
  174. delim_pos = escaped_str.find(':');
  175. std::string temp;
  176. if (delim_pos == std::string::npos)
  177. {
  178. mScheme = "";
  179. mEscapedOpaque = escaped_str;
  180. }
  181. else
  182. {
  183. mScheme = escaped_str.substr(0, delim_pos);
  184. mEscapedOpaque = escaped_str.substr(delim_pos+1);
  185. }
  186. parseAuthorityAndPathUsingOpaque();
  187. delim_pos = mEscapedPath.find('?');
  188. if (delim_pos != std::string::npos)
  189. {
  190. mEscapedQuery = mEscapedPath.substr(delim_pos+1);
  191. mEscapedPath = mEscapedPath.substr(0,delim_pos);
  192. }
  193. }
  194. static BOOL isDefault(const std::string& scheme, U16 port)
  195. {
  196. if (scheme == "http")
  197. return port == 80;
  198. if (scheme == "https")
  199. return port == 443;
  200. if (scheme == "ftp")
  201. return port == 21;
  202. return FALSE;
  203. }
  204. void LLURI::parseAuthorityAndPathUsingOpaque()
  205. {
  206. if (mScheme == "http" || mScheme == "https" ||
  207. mScheme == "ftp" || mScheme == "secondlife" ||
  208. mScheme == "x-grid-location-info")
  209. {
  210. if (mEscapedOpaque.substr(0,2) != "//")
  211. {
  212. return;
  213. }
  214. std::string::size_type delim_pos, delim_pos2;
  215. delim_pos = mEscapedOpaque.find('/', 2);
  216. delim_pos2 = mEscapedOpaque.find('?', 2);
  217. // no path, no query
  218. if (delim_pos == std::string::npos &&
  219. delim_pos2 == std::string::npos)
  220. {
  221. mEscapedAuthority = mEscapedOpaque.substr(2);
  222. mEscapedPath = "";
  223. }
  224. // path exist, no query
  225. else if (delim_pos2 == std::string::npos)
  226. {
  227. mEscapedAuthority = mEscapedOpaque.substr(2,delim_pos-2);
  228. mEscapedPath = mEscapedOpaque.substr(delim_pos);
  229. }
  230. // no path, only query
  231. else if (delim_pos == std::string::npos ||
  232. delim_pos2 < delim_pos)
  233. {
  234. mEscapedAuthority = mEscapedOpaque.substr(2,delim_pos2-2);
  235. // query part will be broken out later
  236. mEscapedPath = mEscapedOpaque.substr(delim_pos2);
  237. }
  238. // path and query
  239. else
  240. {
  241. mEscapedAuthority = mEscapedOpaque.substr(2,delim_pos-2);
  242. // query part will be broken out later
  243. mEscapedPath = mEscapedOpaque.substr(delim_pos);
  244. }
  245. }
  246. else if (mScheme == "about")
  247. {
  248. mEscapedPath = mEscapedOpaque;
  249. }
  250. }
  251. LLURI::LLURI(const std::string& scheme,
  252. const std::string& userName,
  253. const std::string& password,
  254. const std::string& hostName,
  255. U16 port,
  256. const std::string& escapedPath,
  257. const std::string& escapedQuery)
  258. : mScheme(scheme),
  259. mEscapedPath(escapedPath),
  260. mEscapedQuery(escapedQuery)
  261. {
  262. std::ostringstream auth;
  263. std::ostringstream opaque;
  264. opaque << "//";
  265. if (!userName.empty())
  266. {
  267. auth << escape(userName);
  268. if (!password.empty())
  269. {
  270. auth << ':' << escape(password);
  271. }
  272. auth << '@';
  273. }
  274. auth << hostName;
  275. if (!isDefault(scheme, port))
  276. {
  277. auth << ':' << port;
  278. }
  279. mEscapedAuthority = auth.str();
  280. opaque << mEscapedAuthority << escapedPath << escapedQuery;
  281. mEscapedOpaque = opaque.str();
  282. }
  283. LLURI::~LLURI()
  284. {
  285. }
  286. // static
  287. LLURI LLURI::buildHTTP(const std::string& prefix,
  288. const LLSD& path)
  289. {
  290. LLURI result;
  291. // TODO: deal with '/' '?' '#' in host_port
  292. if (prefix.find("://") != prefix.npos)
  293. {
  294. // it is a prefix
  295. result = LLURI(prefix);
  296. }
  297. else
  298. {
  299. // it is just a host and optional port
  300. result.mScheme = "http";
  301. result.mEscapedAuthority = escapeHostAndPort(prefix);
  302. }
  303. if (path.isArray())
  304. {
  305. // break out and escape each path component
  306. for (LLSD::array_const_iterator it = path.beginArray();
  307. it != path.endArray();
  308. ++it)
  309. {
  310. lldebugs << "PATH: inserting " << it->asString() << llendl;
  311. result.mEscapedPath += "/" + escapePathComponent(it->asString());
  312. }
  313. }
  314. else if(path.isString())
  315. {
  316. result.mEscapedPath += "/" + escapePathComponent(path.asString());
  317. }
  318. else if(path.isUndefined())
  319. {
  320. // do nothing
  321. }
  322. else
  323. {
  324. llwarns << "Valid path arguments to buildHTTP are array, string, or undef, you passed type"
  325. << path.type() << llendl;
  326. }
  327. result.mEscapedOpaque = "//" + result.mEscapedAuthority +
  328. result.mEscapedPath;
  329. return result;
  330. }
  331. // static
  332. LLURI LLURI::buildHTTP(const std::string& prefix,
  333. const LLSD& path,
  334. const LLSD& query)
  335. {
  336. LLURI uri = buildHTTP(prefix, path);
  337. // break out and escape each query component
  338. uri.mEscapedQuery = mapToQueryString(query);
  339. uri.mEscapedOpaque += uri.mEscapedQuery ;
  340. uri.mEscapedQuery.erase(0,1); // trim the leading '?'
  341. return uri;
  342. }
  343. // static
  344. LLURI LLURI::buildHTTP(const std::string& host,
  345. const U32& port,
  346. const LLSD& path)
  347. {
  348. return LLURI::buildHTTP(llformat("%s:%u", host.c_str(), port), path);
  349. }
  350. // static
  351. LLURI LLURI::buildHTTP(const std::string& host,
  352. const U32& port,
  353. const LLSD& path,
  354. const LLSD& query)
  355. {
  356. return LLURI::buildHTTP(llformat("%s:%u", host.c_str(), port), path, query);
  357. }
  358. std::string LLURI::asString() const
  359. {
  360. if (mScheme.empty())
  361. {
  362. return mEscapedOpaque;
  363. }
  364. else
  365. {
  366. return mScheme + ":" + mEscapedOpaque;
  367. }
  368. }
  369. std::string LLURI::scheme() const
  370. {
  371. return mScheme;
  372. }
  373. std::string LLURI::opaque() const
  374. {
  375. return unescape(mEscapedOpaque);
  376. }
  377. std::string LLURI::authority() const
  378. {
  379. return unescape(mEscapedAuthority);
  380. }
  381. namespace {
  382. void findAuthorityParts(const std::string& authority,
  383. std::string& user,
  384. std::string& host,
  385. std::string& port)
  386. {
  387. std::string::size_type start_pos = authority.find('@');
  388. if (start_pos == std::string::npos)
  389. {
  390. user = "";
  391. start_pos = 0;
  392. }
  393. else
  394. {
  395. user = authority.substr(0, start_pos);
  396. start_pos += 1;
  397. }
  398. std::string::size_type end_pos = authority.find(':', start_pos);
  399. if (end_pos == std::string::npos)
  400. {
  401. host = authority.substr(start_pos);
  402. port = "";
  403. }
  404. else
  405. {
  406. host = authority.substr(start_pos, end_pos - start_pos);
  407. port = authority.substr(end_pos + 1);
  408. }
  409. }
  410. }
  411. std::string LLURI::hostName() const
  412. {
  413. std::string user, host, port;
  414. findAuthorityParts(mEscapedAuthority, user, host, port);
  415. return unescape(host);
  416. }
  417. std::string LLURI::userName() const
  418. {
  419. std::string user, userPass, host, port;
  420. findAuthorityParts(mEscapedAuthority, userPass, host, port);
  421. std::string::size_type pos = userPass.find(':');
  422. if (pos != std::string::npos)
  423. {
  424. user = userPass.substr(0, pos);
  425. }
  426. return unescape(user);
  427. }
  428. std::string LLURI::password() const
  429. {
  430. std::string pass, userPass, host, port;
  431. findAuthorityParts(mEscapedAuthority, userPass, host, port);
  432. std::string::size_type pos = userPass.find(':');
  433. if (pos != std::string::npos)
  434. {
  435. pass = userPass.substr(pos + 1);
  436. }
  437. return unescape(pass);
  438. }
  439. BOOL LLURI::defaultPort() const
  440. {
  441. return isDefault(mScheme, hostPort());
  442. }
  443. U16 LLURI::hostPort() const
  444. {
  445. std::string user, host, port;
  446. findAuthorityParts(mEscapedAuthority, user, host, port);
  447. if (port.empty())
  448. {
  449. if (mScheme == "http")
  450. return 80;
  451. if (mScheme == "https")
  452. return 443;
  453. if (mScheme == "ftp")
  454. return 21;
  455. return 0;
  456. }
  457. return atoi(port.c_str());
  458. }
  459. std::string LLURI::path() const
  460. {
  461. return unescape(mEscapedPath);
  462. }
  463. LLSD LLURI::pathArray() const
  464. {
  465. typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
  466. boost::char_separator<char> sep("/", "", boost::drop_empty_tokens);
  467. tokenizer tokens(mEscapedPath, sep);
  468. tokenizer::iterator it = tokens.begin();
  469. tokenizer::iterator end = tokens.end();
  470. LLSD params;
  471. for ( ; it != end; ++it)
  472. {
  473. params.append(*it);
  474. }
  475. return params;
  476. }
  477. std::string LLURI::query() const
  478. {
  479. return unescape(mEscapedQuery);
  480. }
  481. LLSD LLURI::queryMap() const
  482. {
  483. return queryMap(mEscapedQuery);
  484. }
  485. // static
  486. LLSD LLURI::queryMap(std::string escaped_query_string)
  487. {
  488. lldebugs << "LLURI::queryMap query params: " << escaped_query_string << llendl;
  489. LLSD result = LLSD::emptyArray();
  490. while(!escaped_query_string.empty())
  491. {
  492. // get tuple first
  493. std::string tuple;
  494. std::string::size_type tuple_begin = escaped_query_string.find('&');
  495. if (tuple_begin != std::string::npos)
  496. {
  497. tuple = escaped_query_string.substr(0, tuple_begin);
  498. escaped_query_string = escaped_query_string.substr(tuple_begin+1);
  499. }
  500. else
  501. {
  502. tuple = escaped_query_string;
  503. escaped_query_string = "";
  504. }
  505. if (tuple.empty()) continue;
  506. // parse tuple
  507. std::string::size_type key_end = tuple.find('=');
  508. if (key_end != std::string::npos)
  509. {
  510. std::string key = unescape(tuple.substr(0,key_end));
  511. std::string value = unescape(tuple.substr(key_end+1));
  512. lldebugs << "inserting key " << key << " value " << value << llendl;
  513. result[key] = value;
  514. }
  515. else
  516. {
  517. lldebugs << "inserting key " << unescape(tuple) << " value true" << llendl;
  518. result[unescape(tuple)] = true;
  519. }
  520. }
  521. return result;
  522. }
  523. std::string LLURI::mapToQueryString(const LLSD& queryMap)
  524. {
  525. std::string query_string;
  526. if (queryMap.isMap())
  527. {
  528. bool first_element = true;
  529. LLSD::map_const_iterator iter = queryMap.beginMap();
  530. LLSD::map_const_iterator end = queryMap.endMap();
  531. std::ostringstream ostr;
  532. for (; iter != end; ++iter)
  533. {
  534. if(first_element)
  535. {
  536. ostr << "?";
  537. first_element = false;
  538. }
  539. else
  540. {
  541. ostr << "&";
  542. }
  543. ostr << escapeQueryVariable(iter->first);
  544. if(iter->second.isDefined())
  545. {
  546. ostr << "=" << escapeQueryValue(iter->second.asString());
  547. }
  548. }
  549. query_string = ostr.str();
  550. }
  551. return query_string;
  552. }
  553. bool operator!=(const LLURI& first, const LLURI& second)
  554. {
  555. return (first.asString() != second.asString());
  556. }