lluri.cpp

Go to the documentation of this file.
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 // static
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 // static
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() + ":@!$'()*+,"); }        // sub_delims - "&;=" + ":@"
00116         std::string escapeQueryValue(const std::string& s)
00117                 { return LLURI::escape(s, unreserved() + ":@!$'()*+,="); }      // sub_delims - "&;" + ":@"
00118 }
00119 
00120 // TODO: USE CURL!! After http textures gets merged everywhere.
00121 // static
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                 // no path, no query
00157                 if (delim_pos == std::string::npos &&
00158                         delim_pos2 == std::string::npos)
00159                 {
00160                         mEscapedAuthority = mEscapedOpaque.substr(2);
00161                         mEscapedPath = "";
00162                 }
00163                 // path exist, no query
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                 // no path, only query
00170                 else if (delim_pos == std::string::npos ||
00171                                  delim_pos2 < delim_pos)
00172                 {
00173                         mEscapedAuthority = mEscapedOpaque.substr(2,delim_pos2-2);
00174                         // query part will be broken out later
00175                         mEscapedPath = mEscapedOpaque.substr(delim_pos2);
00176                 }
00177                 // path and query
00178                 else
00179                 {
00180                         mEscapedAuthority = mEscapedOpaque.substr(2,delim_pos-2);
00181                         // query part will be broken out later
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 // static
00248 LLURI LLURI::buildHTTP(const std::string& prefix,
00249                                            const LLSD& path)
00250 {
00251         LLURI result;
00252         
00253         // TODO: deal with '/' '?' '#' in host_port
00254         if (prefix.find("://") != prefix.npos)
00255         {
00256                 // it is a prefix
00257                 result = LLURI(prefix);
00258         }
00259         else
00260         {
00261                 // it is just a host and optional port
00262                 result.mScheme = "http";
00263                 result.mEscapedAuthority = escapeHostAndPort(prefix);
00264         }
00265 
00266         if (path.isArray())
00267         {
00268                 // break out and escape each path component
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           // do nothing
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 // static
00296 LLURI LLURI::buildHTTP(const std::string& prefix,
00297                                            const LLSD& path,
00298                                            const LLSD& query)
00299 {
00300         LLURI uri = buildHTTP(prefix, path);
00301         // break out and escape each query component
00302         uri.mEscapedQuery = mapToQueryString(query);
00303         uri.mEscapedOpaque += uri.mEscapedQuery ;
00304         uri.mEscapedQuery.erase(0,1); // trim the leading '?'
00305         return uri;
00306 }
00307 
00308 // static
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 // static
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 // static
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                 // get tuple first
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                 // parse tuple
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 }

Generated on Thu Jul 1 06:09:24 2010 for Second Life Viewer by  doxygen 1.4.7