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