00001
00034 #include "linden_common.h"
00035
00036 #include "llapp.h"
00037 #include "lluri.h"
00038 #include "llsd.h"
00039 #include <iomanip>
00040
00041 #include "../llmath/lluuid.h"
00042
00043
00044 #include <boost/tokenizer.hpp>
00045
00046 void encode_character(std::ostream& ostr, std::string::value_type val)
00047 {
00048 ostr << "%" << std::uppercase << std::hex << std::setw(2) << std::setfill('0')
00049
00050
00051 << static_cast<S32>(static_cast<U8>(val));
00052 }
00053
00054
00055 std::string LLURI::escape(
00056 const std::string& str,
00057 const std::string& allowed,
00058 bool is_allowed_sorted)
00059 {
00060
00061
00062
00063
00064 if(!is_allowed_sorted && (str.size() > 2 * allowed.size()))
00065 {
00066
00067
00068 std::string sorted_allowed(allowed);
00069 std::sort(sorted_allowed.begin(), sorted_allowed.end());
00070 return escape(str, sorted_allowed, true);
00071 }
00072
00073 std::ostringstream ostr;
00074 std::string::const_iterator it = str.begin();
00075 std::string::const_iterator end = str.end();
00076 std::string::value_type c;
00077 if(is_allowed_sorted)
00078 {
00079 std::string::const_iterator allowed_begin(allowed.begin());
00080 std::string::const_iterator allowed_end(allowed.end());
00081 for(; it != end; ++it)
00082 {
00083 c = *it;
00084 if(std::binary_search(allowed_begin, allowed_end, c))
00085 {
00086 ostr << c;
00087 }
00088 else
00089 {
00090 encode_character(ostr, c);
00091 }
00092 }
00093 }
00094 else
00095 {
00096 for(; it != end; ++it)
00097 {
00098 c = *it;
00099 if(allowed.find(c) == std::string::npos)
00100 {
00101 encode_character(ostr, c);
00102 }
00103 else
00104 {
00105 ostr << c;
00106 }
00107 }
00108 }
00109 return ostr.str();
00110 }
00111
00112
00113 std::string LLURI::unescape(const std::string& str)
00114 {
00115 std::ostringstream ostr;
00116 std::string::const_iterator it = str.begin();
00117 std::string::const_iterator end = str.end();
00118 for(; it != end; ++it)
00119 {
00120 if((*it) == '%')
00121 {
00122 ++it;
00123 if(it == end) break;
00124 U8 c = hex_as_nybble(*it++);
00125 c = c << 4;
00126 if (it == end) break;
00127 c |= hex_as_nybble(*it);
00128 ostr.put((char)c);
00129 }
00130 else
00131 {
00132 ostr.put(*it);
00133 }
00134 }
00135 return ostr.str();
00136 }
00137
00138 namespace
00139 {
00140 const std::string unreserved()
00141 {
00142 static const std::string s =
00143 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
00144 "0123456789"
00145 "-._~";
00146 return s;
00147 }
00148 const std::string sub_delims()
00149 {
00150 static const std::string s = "!$&'()*+,;=";
00151 return s;
00152 }
00153
00154 std::string escapeHostAndPort(const std::string& s)
00155 { return LLURI::escape(s, unreserved() + sub_delims() +":"); }
00156 std::string escapePathComponent(const std::string& s)
00157 { return LLURI::escape(s, unreserved() + sub_delims() + ":@"); }
00158 std::string escapeQueryVariable(const std::string& s)
00159 { return LLURI::escape(s, unreserved() + ":@!$'()*+,"); }
00160 std::string escapeQueryValue(const std::string& s)
00161 { return LLURI::escape(s, unreserved() + ":@!$'()*+,="); }
00162 }
00163
00164
00165
00166 std::string LLURI::escape(const std::string& str)
00167 {
00168 static std::string default_allowed(unreserved() + ":@!$'()*+,=/?&#;");
00169 static bool initialized = false;
00170 if(!initialized)
00171 {
00172 std::sort(default_allowed.begin(), default_allowed.end());
00173 initialized = true;
00174 }
00175 return escape(str, default_allowed, true);
00176 }
00177
00178 LLURI::LLURI()
00179 {
00180 }
00181
00182 LLURI::LLURI(const std::string& escaped_str)
00183 {
00184 std::string::size_type delim_pos;
00185 delim_pos = escaped_str.find(':');
00186 std::string temp;
00187 if (delim_pos == std::string::npos)
00188 {
00189 mScheme = "";
00190 mEscapedOpaque = escaped_str;
00191 }
00192 else
00193 {
00194 mScheme = escaped_str.substr(0, delim_pos);
00195 mEscapedOpaque = escaped_str.substr(delim_pos+1);
00196 }
00197
00198 parseAuthorityAndPathUsingOpaque();
00199
00200 delim_pos = mEscapedPath.find('?');
00201 if (delim_pos != std::string::npos)
00202 {
00203 mEscapedQuery = mEscapedPath.substr(delim_pos+1);
00204 mEscapedPath = mEscapedPath.substr(0,delim_pos);
00205 }
00206 }
00207
00208 static BOOL isDefault(const std::string& scheme, U16 port)
00209 {
00210 if (scheme == "http")
00211 return port == 80;
00212 if (scheme == "https")
00213 return port == 443;
00214 if (scheme == "ftp")
00215 return port == 21;
00216
00217 return FALSE;
00218 }
00219
00220 void LLURI::parseAuthorityAndPathUsingOpaque()
00221 {
00222 if (mScheme == "http" || mScheme == "https" ||
00223 mScheme == "ftp" || mScheme == "secondlife" )
00224 {
00225 if (mEscapedOpaque.substr(0,2) != "//")
00226 {
00227 return;
00228 }
00229
00230 std::string::size_type delim_pos, delim_pos2;
00231 delim_pos = mEscapedOpaque.find('/', 2);
00232 delim_pos2 = mEscapedOpaque.find('?', 2);
00233
00234 if (delim_pos == std::string::npos &&
00235 delim_pos2 == std::string::npos)
00236 {
00237 mEscapedAuthority = mEscapedOpaque.substr(2);
00238 mEscapedPath = "";
00239 }
00240
00241 else if (delim_pos2 == std::string::npos)
00242 {
00243 mEscapedAuthority = mEscapedOpaque.substr(2,delim_pos-2);
00244 mEscapedPath = mEscapedOpaque.substr(delim_pos);
00245 }
00246
00247 else if (delim_pos == std::string::npos ||
00248 delim_pos2 < delim_pos)
00249 {
00250 mEscapedAuthority = mEscapedOpaque.substr(2,delim_pos2-2);
00251
00252 mEscapedPath = mEscapedOpaque.substr(delim_pos2);
00253 }
00254
00255 else
00256 {
00257 mEscapedAuthority = mEscapedOpaque.substr(2,delim_pos-2);
00258
00259 mEscapedPath = mEscapedOpaque.substr(delim_pos);
00260 }
00261 }
00262 else if (mScheme == "about")
00263 {
00264 mEscapedPath = mEscapedOpaque;
00265 }
00266 }
00267
00268 LLURI::LLURI(const std::string& scheme,
00269 const std::string& userName,
00270 const std::string& password,
00271 const std::string& hostName,
00272 U16 port,
00273 const std::string& escapedPath,
00274 const std::string& escapedQuery)
00275 : mScheme(scheme),
00276 mEscapedPath(escapedPath),
00277 mEscapedQuery(escapedQuery)
00278 {
00279 std::ostringstream auth;
00280 std::ostringstream opaque;
00281
00282 opaque << "//";
00283
00284 if (!userName.empty())
00285 {
00286 auth << escape(userName);
00287 if (!password.empty())
00288 {
00289 auth << ':' << escape(password);
00290 }
00291 auth << '@';
00292 }
00293 auth << hostName;
00294 if (!isDefault(scheme, port))
00295 {
00296 auth << ':' << port;
00297 }
00298 mEscapedAuthority = auth.str();
00299
00300 opaque << mEscapedAuthority << escapedPath << escapedQuery;
00301
00302 mEscapedOpaque = opaque.str();
00303 }
00304
00305 LLURI::~LLURI()
00306 {
00307 }
00308
00309
00310 LLURI LLURI::buildHTTP(const std::string& prefix,
00311 const LLSD& path)
00312 {
00313 LLURI result;
00314
00315
00316 if (prefix.find("://") != prefix.npos)
00317 {
00318
00319 result = LLURI(prefix);
00320 }
00321 else
00322 {
00323
00324 result.mScheme = "http";
00325 result.mEscapedAuthority = escapeHostAndPort(prefix);
00326 }
00327
00328 if (path.isArray())
00329 {
00330
00331 for (LLSD::array_const_iterator it = path.beginArray();
00332 it != path.endArray();
00333 ++it)
00334 {
00335 lldebugs << "PATH: inserting " << it->asString() << llendl;
00336 result.mEscapedPath += "/" + escapePathComponent(it->asString());
00337 }
00338 }
00339 else if(path.isString())
00340 {
00341 result.mEscapedPath += "/" + escapePathComponent(path.asString());
00342 }
00343 else if(path.isUndefined())
00344 {
00345
00346 }
00347 else
00348 {
00349 llwarns << "Valid path arguments to buildHTTP are array, string, or undef, you passed type"
00350 << path.type() << llendl;
00351 }
00352 result.mEscapedOpaque = "//" + result.mEscapedAuthority +
00353 result.mEscapedPath;
00354 return result;
00355 }
00356
00357
00358 LLURI LLURI::buildHTTP(const std::string& prefix,
00359 const LLSD& path,
00360 const LLSD& query)
00361 {
00362 LLURI uri = buildHTTP(prefix, path);
00363
00364 uri.mEscapedQuery = mapToQueryString(query);
00365 uri.mEscapedOpaque += uri.mEscapedQuery ;
00366 uri.mEscapedQuery.erase(0,1);
00367 return uri;
00368 }
00369
00370
00371 LLURI LLURI::buildHTTP(const std::string& host,
00372 const U32& port,
00373 const LLSD& path)
00374 {
00375 return LLURI::buildHTTP(llformat("%s:%u", host.c_str(), port), path);
00376 }
00377
00378
00379 LLURI LLURI::buildHTTP(const std::string& host,
00380 const U32& port,
00381 const LLSD& path,
00382 const LLSD& query)
00383 {
00384 return LLURI::buildHTTP(llformat("%s:%u", host.c_str(), port), path, query);
00385 }
00386
00387 std::string LLURI::asString() const
00388 {
00389 if (mScheme.empty())
00390 {
00391 return mEscapedOpaque;
00392 }
00393 else
00394 {
00395 return mScheme + ":" + mEscapedOpaque;
00396 }
00397 }
00398
00399 std::string LLURI::scheme() const
00400 {
00401 return mScheme;
00402 }
00403
00404 std::string LLURI::opaque() const
00405 {
00406 return unescape(mEscapedOpaque);
00407 }
00408
00409 std::string LLURI::authority() const
00410 {
00411 return unescape(mEscapedAuthority);
00412 }
00413
00414
00415 namespace {
00416 void findAuthorityParts(const std::string& authority,
00417 std::string& user,
00418 std::string& host,
00419 std::string& port)
00420 {
00421 std::string::size_type start_pos = authority.find('@');
00422 if (start_pos == std::string::npos)
00423 {
00424 user = "";
00425 start_pos = 0;
00426 }
00427 else
00428 {
00429 user = authority.substr(0, start_pos);
00430 start_pos += 1;
00431 }
00432
00433 std::string::size_type end_pos = authority.find(':', start_pos);
00434 if (end_pos == std::string::npos)
00435 {
00436 host = authority.substr(start_pos);
00437 port = "";
00438 }
00439 else
00440 {
00441 host = authority.substr(start_pos, end_pos - start_pos);
00442 port = authority.substr(end_pos + 1);
00443 }
00444 }
00445 }
00446
00447 std::string LLURI::hostName() const
00448 {
00449 std::string user, host, port;
00450 findAuthorityParts(mEscapedAuthority, user, host, port);
00451 return unescape(host);
00452 }
00453
00454 std::string LLURI::userName() const
00455 {
00456 std::string user, userPass, host, port;
00457 findAuthorityParts(mEscapedAuthority, userPass, host, port);
00458 std::string::size_type pos = userPass.find(':');
00459 if (pos != std::string::npos)
00460 {
00461 user = userPass.substr(0, pos);
00462 }
00463 return unescape(user);
00464 }
00465
00466 std::string LLURI::password() const
00467 {
00468 std::string pass, userPass, host, port;
00469 findAuthorityParts(mEscapedAuthority, userPass, host, port);
00470 std::string::size_type pos = userPass.find(':');
00471 if (pos != std::string::npos)
00472 {
00473 pass = userPass.substr(pos + 1);
00474 }
00475 return unescape(pass);
00476 }
00477
00478 BOOL LLURI::defaultPort() const
00479 {
00480 return isDefault(mScheme, hostPort());
00481 }
00482
00483 U16 LLURI::hostPort() const
00484 {
00485 std::string user, host, port;
00486 findAuthorityParts(mEscapedAuthority, user, host, port);
00487 if (port.empty())
00488 {
00489 if (mScheme == "http")
00490 return 80;
00491 if (mScheme == "https")
00492 return 443;
00493 if (mScheme == "ftp")
00494 return 21;
00495 return 0;
00496 }
00497 return atoi(port.c_str());
00498 }
00499
00500 std::string LLURI::path() const
00501 {
00502 return unescape(mEscapedPath);
00503 }
00504
00505 LLSD LLURI::pathArray() const
00506 {
00507 typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
00508 boost::char_separator<char> sep("/", "", boost::drop_empty_tokens);
00509 tokenizer tokens(mEscapedPath, sep);
00510 tokenizer::iterator it = tokens.begin();
00511 tokenizer::iterator end = tokens.end();
00512
00513 LLSD params;
00514 for ( ; it != end; ++it)
00515 {
00516 params.append(*it);
00517 }
00518 return params;
00519 }
00520
00521 std::string LLURI::query() const
00522 {
00523 return unescape(mEscapedQuery);
00524 }
00525
00526 LLSD LLURI::queryMap() const
00527 {
00528 return queryMap(mEscapedQuery);
00529 }
00530
00531
00532 LLSD LLURI::queryMap(std::string escaped_query_string)
00533 {
00534 lldebugs << "LLURI::queryMap query params: " << escaped_query_string << llendl;
00535
00536 LLSD result = LLSD::emptyArray();
00537 while(!escaped_query_string.empty())
00538 {
00539
00540 std::string tuple;
00541 std::string::size_type tuple_begin = escaped_query_string.find('&');
00542 if (tuple_begin != std::string::npos)
00543 {
00544 tuple = escaped_query_string.substr(0, tuple_begin);
00545 escaped_query_string = escaped_query_string.substr(tuple_begin+1);
00546 }
00547 else
00548 {
00549 tuple = escaped_query_string;
00550 escaped_query_string = "";
00551 }
00552 if (tuple.empty()) continue;
00553
00554
00555 std::string::size_type key_end = tuple.find('=');
00556 if (key_end != std::string::npos)
00557 {
00558 std::string key = unescape(tuple.substr(0,key_end));
00559 std::string value = unescape(tuple.substr(key_end+1));
00560 lldebugs << "inserting key " << key << " value " << value << llendl;
00561 result[key] = value;
00562 }
00563 else
00564 {
00565 lldebugs << "inserting key " << unescape(tuple) << " value true" << llendl;
00566 result[unescape(tuple)] = true;
00567 }
00568 }
00569 return result;
00570 }
00571
00572 std::string LLURI::mapToQueryString(const LLSD& queryMap)
00573 {
00574 std::string query_string;
00575 if (queryMap.isMap())
00576 {
00577 bool first_element = true;
00578 LLSD::map_const_iterator iter = queryMap.beginMap();
00579 LLSD::map_const_iterator end = queryMap.endMap();
00580 std::ostringstream ostr;
00581 for (; iter != end; ++iter)
00582 {
00583 if(first_element)
00584 {
00585 ostr << "?";
00586 first_element = false;
00587 }
00588 else
00589 {
00590 ostr << "&";
00591 }
00592 ostr << escapeQueryVariable(iter->first);
00593 if(iter->second.isDefined())
00594 {
00595 ostr << "=" << escapeQueryValue(iter->second.asString());
00596 }
00597 }
00598 query_string = ostr.str();
00599 }
00600 return query_string;
00601 }