Panda3D
 All Classes Functions Variables Enumerations
urlSpec.cxx
00001 // Filename: urlSpec.cxx
00002 // Created by:  drose (24Sep02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "urlSpec.h"
00016 
00017 #include <ctype.h>
00018 
00019 
00020 ////////////////////////////////////////////////////////////////////
00021 //     Function: URLSpec::Constructor
00022 //       Access: Published
00023 //  Description:
00024 ////////////////////////////////////////////////////////////////////
00025 URLSpec::
00026 URLSpec() {
00027   _port = 0;
00028   _flags = 0;
00029   _scheme_end = 0;
00030   _username_start = 0;
00031   _username_end = 0;
00032   _server_start = 0;
00033   _server_end = 0;
00034   _port_start = 0;
00035   _port_end = 0;
00036   _path_start = 0;
00037   _path_end = 0;
00038   _query_start = 0;
00039 }
00040 
00041 ////////////////////////////////////////////////////////////////////
00042 //     Function: URLSpec::Copy Assignment Operator
00043 //       Access: Published
00044 //  Description:
00045 ////////////////////////////////////////////////////////////////////
00046 void URLSpec::
00047 operator = (const URLSpec &copy) {
00048   _url = copy._url;
00049   _port = copy._port;
00050   _flags = copy._flags;
00051   _scheme_end = copy._scheme_end;
00052   _username_start = copy._username_start;
00053   _username_end = copy._username_end;
00054   _server_start = copy._server_start;
00055   _server_end = copy._server_end;
00056   _port_start = copy._port_start;
00057   _port_end = copy._port_end;
00058   _path_start = copy._path_start;
00059   _path_end = copy._path_end;
00060   _query_start = copy._query_start;
00061 }
00062 
00063 ////////////////////////////////////////////////////////////////////
00064 //     Function: URLSpec::get_scheme
00065 //       Access: Published
00066 //  Description: Returns the scheme specified by the URL, or empty
00067 //               string if no scheme is specified.
00068 ////////////////////////////////////////////////////////////////////
00069 string URLSpec::
00070 get_scheme() const {
00071   if (has_scheme()) {
00072     return _url.substr(0, _scheme_end);
00073   }
00074   return string();
00075 }
00076 
00077 ////////////////////////////////////////////////////////////////////
00078 //     Function: URLSpec::get_port
00079 //       Access: Published
00080 //  Description: Returns the port number specified by the URL, or the
00081 //               default port if not specified.
00082 ////////////////////////////////////////////////////////////////////
00083 int URLSpec::
00084 get_port() const {
00085   if (has_port()) {
00086     return _port;
00087   }
00088   return get_default_port_for_scheme(get_scheme());
00089 }
00090 
00091 ////////////////////////////////////////////////////////////////////
00092 //     Function: URLSpec::is_default_port
00093 //       Access: Published
00094 //  Description: Returns true if the port number encoded in this URL
00095 //               is the default port number for the scheme (or if
00096 //               there is no port number), or false if it is a
00097 //               nonstandard port.
00098 ////////////////////////////////////////////////////////////////////
00099 bool URLSpec::
00100 is_default_port() const {
00101   if (!has_port()) {
00102     return true;
00103   }
00104   return (_port == get_default_port_for_scheme(get_scheme()));
00105 }
00106 
00107 ////////////////////////////////////////////////////////////////////
00108 //     Function: URLSpec::get_default_port_for_scheme
00109 //       Access: Published, Static
00110 //  Description: Returns the default port number for the indicated
00111 //               scheme, or 0 if there is no known default.
00112 ////////////////////////////////////////////////////////////////////
00113 int URLSpec::
00114 get_default_port_for_scheme(const string &scheme) {
00115   if (scheme == "http" || scheme.empty()) {
00116     return 80;
00117 
00118   } else if (scheme == "https") {
00119     return 443;
00120 
00121   } else if (scheme == "socks") {
00122     return 1080;
00123   }
00124 
00125   return 0;
00126 }
00127 
00128 ////////////////////////////////////////////////////////////////////
00129 //     Function: URLSpec::get_server_and_port
00130 //       Access: Published
00131 //  Description: Returns a string consisting of the server name,
00132 //               followed by a colon, followed by the port number.  If
00133 //               the port number is not explicitly given in the URL,
00134 //               this string will include the implicit port number.
00135 ////////////////////////////////////////////////////////////////////
00136 string URLSpec::
00137 get_server_and_port() const {
00138   if (has_port()) {
00139     return _url.substr(_server_start, _port_end - _server_start);
00140   }
00141   ostringstream strm;
00142   strm << get_server() << ":" << get_port();
00143   return strm.str();
00144 }
00145 
00146 ////////////////////////////////////////////////////////////////////
00147 //     Function: URLSpec::get_path
00148 //       Access: Published
00149 //  Description: Returns the path specified by the URL, or "/" if no
00150 //               path is specified.
00151 ////////////////////////////////////////////////////////////////////
00152 string URLSpec::
00153 get_path() const {
00154   if (has_path()) {
00155     return _url.substr(_path_start, _path_end - _path_start);
00156   }
00157   return "/";
00158 }
00159 
00160 ////////////////////////////////////////////////////////////////////
00161 //     Function: URLSpec::get_path_and_query
00162 //       Access: Published
00163 //  Description: Returns the path (or "/" if no path is specified),
00164 //               followed by the query if it is specified.
00165 ////////////////////////////////////////////////////////////////////
00166 string URLSpec::
00167 get_path_and_query() const {
00168   if (has_path()) {
00169     return _url.substr(_path_start);
00170   }
00171   if (has_query()) {
00172     return "/?" + get_query();
00173   }
00174   return "/";
00175 }
00176 
00177 ////////////////////////////////////////////////////////////////////
00178 //     Function: URLSpec::set_scheme
00179 //       Access: Published
00180 //  Description: Replaces the scheme part of the URL specification.
00181 ////////////////////////////////////////////////////////////////////
00182 void URLSpec::
00183 set_scheme(const string &scheme) {
00184   int length_adjust;
00185 
00186   // The scheme is always converted to lowercase.
00187   string lc_scheme;
00188   lc_scheme.reserve(scheme.length());
00189   for (string::const_iterator si = scheme.begin(); si != scheme.end(); ++si) {
00190     lc_scheme += tolower(*si);
00191   }
00192 
00193   if (lc_scheme.empty()) {
00194     // Remove the scheme specification.
00195     if (!has_scheme()) {
00196       return;
00197     }
00198     // Increment over the trailing colon so we can remove that too.
00199     _scheme_end++;
00200     length_adjust = -(int)_scheme_end;
00201     _url = _url.substr(_scheme_end);
00202     _flags &= ~F_has_scheme;
00203 
00204   } else if (!has_scheme()) {
00205     // Insert a new scheme specification.  The user may or may not
00206     // have specified a colon.
00207     if (lc_scheme[lc_scheme.length() - 1] == ':') {
00208       length_adjust = lc_scheme.length();
00209       _url = lc_scheme + _url;
00210 
00211     } else {
00212       length_adjust = lc_scheme.length() + 1;
00213       _url = lc_scheme + ":" + _url;
00214     }
00215 
00216     // Since the length_adjust flag, above, now accounts for the
00217     // colon, subtract one from _scheme_end (which should not include
00218     // the colon).
00219     _scheme_end--;
00220     _flags |= F_has_scheme;
00221 
00222   } else {
00223     // Replace the existing scheme specification.  Since the existing
00224     // scheme will already be trailed by a colon, remove the colon
00225     // from the string if the user appended one.
00226     if (lc_scheme[lc_scheme.length() - 1] == ':') {
00227       lc_scheme = lc_scheme.substr(0, lc_scheme.length() - 1);
00228     }
00229 
00230     int old_length = (int)_scheme_end;
00231     length_adjust = scheme.length() - old_length;
00232     _url = lc_scheme + _url.substr(_scheme_end);
00233   }
00234 
00235   _scheme_end += length_adjust;
00236   _username_start += length_adjust;
00237   _username_end += length_adjust;
00238   _server_start += length_adjust;
00239   _server_end += length_adjust;
00240   _port_start += length_adjust;
00241   _port_end += length_adjust;
00242   _path_start += length_adjust;
00243   _path_end += length_adjust;
00244   _query_start += length_adjust;
00245 }
00246 
00247 ////////////////////////////////////////////////////////////////////
00248 //     Function: URLSpec::set_authority
00249 //       Access: Published
00250 //  Description: Replaces the authority part of the URL specification.
00251 //               This includes the username, server, and port.
00252 ////////////////////////////////////////////////////////////////////
00253 void URLSpec::
00254 set_authority(const string &authority) {
00255   int length_adjust;
00256   int extra_slash_adjust = 0;
00257 
00258   if (authority.empty()) {
00259     // Remove the authority specification.
00260     if (!has_authority()) {
00261       return;
00262     }
00263     _username_start -= 2;
00264     length_adjust = -((int)_port_end - (int)_username_start);
00265     _url = _url.substr(0, _username_start) + _url.substr(_port_end);
00266     _flags &= ~(F_has_authority | F_has_username | F_has_server | F_has_port);
00267 
00268     _username_end = _username_start;
00269     _server_start = _username_start;
00270     _server_end = _username_start;
00271     _port_start = _username_start;
00272 
00273   } else if (!has_authority()) {
00274     // Insert a new authority specification.
00275     length_adjust = authority.length() + 2;
00276 
00277     string extra_slash;
00278     if (has_path() && _url[_path_start] != '/') {
00279       // If we have a path but it doesn't begin with a slash, it should.
00280       extra_slash = '/';
00281       extra_slash_adjust = 1;
00282     }
00283     _url = _url.substr(0, _username_start) + "//" + authority + extra_slash + _url.substr(_port_end);
00284     _flags |= F_has_authority;
00285     _username_start += 2;
00286 
00287   } else {
00288     // Replace an existing authority specification.
00289     int old_length = (int)_port_end - (int)_username_start;
00290     length_adjust = authority.length() - old_length;
00291     _url = _url.substr(0, _username_start) + authority + _url.substr(_port_end);
00292   }
00293 
00294   _port_end += length_adjust;
00295   _path_start += length_adjust;
00296   _path_end += length_adjust + extra_slash_adjust;
00297   _query_start += length_adjust + extra_slash_adjust;
00298 
00299   parse_authority();
00300 }
00301 
00302 ////////////////////////////////////////////////////////////////////
00303 //     Function: URLSpec::set_username
00304 //       Access: Published
00305 //  Description: Replaces the username part of the URL specification.
00306 ////////////////////////////////////////////////////////////////////
00307 void URLSpec::
00308 set_username(const string &username) {
00309   if (username.empty() && !has_authority()) {
00310     return;
00311   }
00312   string authority;
00313 
00314   if (!username.empty()) {
00315     authority = username + "@";
00316   }
00317   authority += get_server();
00318   if (has_port()) {
00319     authority += ":";
00320     authority += get_port_str();
00321   }
00322 
00323   set_authority(authority);
00324 }
00325 
00326 ////////////////////////////////////////////////////////////////////
00327 //     Function: URLSpec::set_server
00328 //       Access: Published
00329 //  Description: Replaces the server part of the URL specification.
00330 ////////////////////////////////////////////////////////////////////
00331 void URLSpec::
00332 set_server(const string &server) {
00333   if (server.empty() && !has_authority()) {
00334     return;
00335   }
00336   string authority;
00337 
00338   if (has_username()) {
00339     authority = get_username() + "@";
00340   }
00341   authority += server;
00342   if (has_port()) {
00343     authority += ":";
00344     authority += get_port_str();
00345   }
00346 
00347   set_authority(authority);
00348 }
00349 
00350 ////////////////////////////////////////////////////////////////////
00351 //     Function: URLSpec::set_port
00352 //       Access: Published
00353 //  Description: Replaces the port part of the URL specification.
00354 ////////////////////////////////////////////////////////////////////
00355 void URLSpec::
00356 set_port(const string &port) {
00357   if (port.empty() && !has_authority()) {
00358     return;
00359   }
00360   string authority;
00361 
00362   if (has_username()) {
00363     authority = get_username() + "@";
00364   }
00365   authority += get_server();
00366 
00367   if (!port.empty()) {
00368     authority += ":";
00369     authority += port;
00370   }
00371 
00372   set_authority(authority);
00373 }
00374 
00375 ////////////////////////////////////////////////////////////////////
00376 //     Function: URLSpec::set_port
00377 //       Access: Published
00378 //  Description: Replaces the port part of the URL specification,
00379 //               given a numeric port number.
00380 ////////////////////////////////////////////////////////////////////
00381 void URLSpec::
00382 set_port(int port) {
00383   ostringstream str;
00384   str << port;
00385   set_port(str.str());
00386 }
00387 
00388 ////////////////////////////////////////////////////////////////////
00389 //     Function: URLSpec::set_server_and_port
00390 //       Access: Published
00391 //  Description: Replaces the server and port parts of the URL
00392 //               specification simultaneously.  The input string
00393 //               should be of the form "server:port", or just
00394 //               "server" to make the port number implicit.
00395 ////////////////////////////////////////////////////////////////////
00396 void URLSpec::
00397 set_server_and_port(const string &server_and_port) {
00398   if (server_and_port.empty() && !has_authority()) {
00399     return;
00400   }
00401   string authority;
00402 
00403   if (has_username()) {
00404     authority = get_username() + "@";
00405   }
00406   authority += server_and_port;
00407   set_authority(authority);
00408 }
00409 
00410 ////////////////////////////////////////////////////////////////////
00411 //     Function: URLSpec::set_path
00412 //       Access: Published
00413 //  Description: Replaces the path part of the URL specification.
00414 ////////////////////////////////////////////////////////////////////
00415 void URLSpec::
00416 set_path(const string &path) {
00417   int length_adjust;
00418 
00419   if (path.empty()) {
00420     // Remove the path specification.
00421     if (!has_path()) {
00422       return;
00423     }
00424     length_adjust = -((int)_path_end - (int)_path_start);
00425     _url = _url.substr(0, _path_start) + _url.substr(_path_end);
00426     _flags &= ~F_has_path;
00427 
00428   } else if (!has_path()) {
00429     // Insert a new path specification.
00430     string cpath = path;
00431     if (cpath[0] != '/') {
00432       // Paths must always begin with a slash.
00433       cpath = '/' + cpath;
00434     }
00435     length_adjust = cpath.length();
00436 
00437     _url = _url.substr(0, _path_start) + cpath + _url.substr(_path_end);
00438     _flags |= F_has_path;
00439 
00440   } else {
00441     // Replace an existing path specification.
00442     string cpath = path;
00443     if (cpath[0] != '/') {
00444       // Paths must always begin with a slash.
00445       cpath = '/' + cpath;
00446     }
00447     int old_length = (int)_path_end - (int)_path_start;
00448     length_adjust = cpath.length() - old_length;
00449     _url = _url.substr(0, _path_start) + cpath + _url.substr(_path_end);
00450   }
00451 
00452   _path_end += length_adjust;
00453   _query_start += length_adjust;
00454 }
00455 
00456 ////////////////////////////////////////////////////////////////////
00457 //     Function: URLSpec::set_query
00458 //       Access: Published
00459 //  Description: Replaces the query part of the URL specification.
00460 ////////////////////////////////////////////////////////////////////
00461 void URLSpec::
00462 set_query(const string &query) {
00463   if (query.empty()) {
00464     // Remove the query specification.
00465     if (!has_query()) {
00466       return;
00467     }
00468     _query_start--;
00469     _url = _url.substr(0, _query_start);
00470     _flags &= ~F_has_query;
00471 
00472   } else if (!has_query()) {
00473     // Insert a new query specification.
00474     _url = _url.substr(0, _query_start) + "?" + query;
00475     _flags |= F_has_query;
00476     _query_start++;
00477 
00478   } else {
00479     // Replace an existing query specification.
00480     _url = _url.substr(0, _query_start) + query;
00481   }
00482 }
00483 
00484 ////////////////////////////////////////////////////////////////////
00485 //     Function: URLSpec::set_url
00486 //       Access: Published
00487 //  Description: Completely replaces the URL with the indicated
00488 //               string.  If server_name_expected is true, it is a
00489 //               hint that an undecorated URL is probably a server
00490 //               name, not a local filename.
00491 ////////////////////////////////////////////////////////////////////
00492 void URLSpec::
00493 set_url(const string &url, bool server_name_expected) {
00494   size_t p, q;
00495 
00496   // Omit leading and trailing whitespace.
00497   p = 0;
00498   while (p < url.length() && isspace(url[p])) {
00499     p++;
00500   }
00501   q = url.length();
00502   while (q > p && isspace(url[q - 1])) {
00503     q--;
00504   }
00505 
00506   _url = url.substr(p, q - p);
00507   _flags = 0;
00508 
00509   if (url.empty()) {
00510     // No server name on an empty string.
00511     server_name_expected = false;
00512   }
00513 
00514   // First, replace backslashes with forward slashes, since this is a
00515   // common mistake among Windows users.  But don't do this after an
00516   // embedded question mark, which begins parameters sent directly to
00517   // the host (and maybe these parameters should include backslashes).
00518   for (p = 0; p < _url.length() && _url[p] != '?'; p++) {
00519     if (_url[p] == '\\') {
00520       _url[p] = '/';
00521     }
00522   }
00523 
00524   // What have we got?
00525   _flags = 0;
00526   _port = 0;
00527 
00528   // Look for the scheme specification.
00529   size_t start = 0;
00530 
00531   _scheme_end = start;
00532   size_t next = _url.find_first_of(":/", start);
00533   if (next < _url.length() - 1 && _url.substr(next, 2) == ":/") {
00534     // We have a scheme.
00535     _flags |= F_has_scheme;
00536     _scheme_end = next;
00537 
00538     // Ensure the scheme is lowercase.
00539     for (size_t p = 0; p < _scheme_end; ++p) {
00540       _url[p] = tolower(_url[p]);
00541     }
00542 
00543     start = next + 1;
00544   }
00545 
00546   // Look for the authority specification, which may include any of
00547   // username, server, and/or port.
00548   _username_start = start;
00549   _username_end = start;
00550   _server_start = start;
00551   _server_end = start;
00552   _port_start = start;
00553   _port_end = start;
00554 
00555   // Try to determine if an authority is present.  It is will
00556   // generally be present if a scheme was present; also, we have a
00557   // hint passed in from the context as to whether we expect an
00558   // authority (e.g. a server name) to be present.
00559   bool has_authority = (has_scheme() || server_name_expected);
00560 
00561   // We also know we have an authority if the url contains two slashes
00562   // at this point.
00563   bool leading_slashes = 
00564     (start < _url.length() - 1 && _url.substr(start, 2) == "//");
00565   if (leading_slashes) {
00566     has_authority = true;
00567   }
00568 
00569   if (has_authority) {
00570     // Now that we know we have an authority, we should ensure there
00571     // are two slashes here, since there should be before the
00572     // authority.
00573     if (!leading_slashes) {
00574       if (start < _url.length() && _url[start] == '/') {
00575         // Well, at least we had one slash.  Double it.
00576         _url = _url.substr(0, start + 1) + _url.substr(start);
00577       } else {
00578         // No slashes at all.  Insert them.
00579         _url = _url.substr(0, start) + "//" + _url.substr(start);
00580       }
00581     }
00582 
00583     // Begin the actual authority specification.
00584     start += 2;
00585     _flags |= F_has_authority;
00586     _username_start = start;
00587     _port_end = _url.find_first_of("/?", start);
00588     if (_port_end == string::npos) {
00589       _port_end = _url.length();
00590     }
00591     parse_authority();
00592     start = _port_end;
00593   }
00594 
00595   // Everything up to the ?, if any, is the path.
00596   _path_start = start;
00597   _path_end = start;
00598   if (start < _url.length() && url[start] != '?') {
00599     // We have a path.
00600     _flags |= F_has_path;
00601     _path_start = start;
00602     _path_end = _url.find("?", _path_start);
00603     if (_path_end == string::npos) {
00604       _path_end = _url.length();
00605     }
00606     start = _path_end;
00607   }
00608 
00609   // Everything after the ? is the query.
00610   _query_start = start;
00611   if (start < _url.length()) {
00612     nassertv(_url[start] == '?');
00613     _flags |= F_has_query;
00614     _query_start++;
00615   }
00616 }
00617 
00618 ////////////////////////////////////////////////////////////////////
00619 //     Function: URLSpec::input
00620 //       Access: Published
00621 //  Description: 
00622 ////////////////////////////////////////////////////////////////////
00623 bool URLSpec::
00624 input(istream &in) {
00625   string url;
00626   in >> url;
00627   if (!in) {
00628     return false;
00629   }
00630   set_url(url);
00631   return true;
00632 }
00633 
00634 ////////////////////////////////////////////////////////////////////
00635 //     Function: URLSpec::output
00636 //       Access: Published
00637 //  Description: 
00638 ////////////////////////////////////////////////////////////////////
00639 void URLSpec::
00640 output(ostream &out) const {
00641   out << get_url();
00642 }
00643 
00644 ////////////////////////////////////////////////////////////////////
00645 //     Function: URLSpec::quote
00646 //       Access: Published, Static
00647 //  Description: Returns the source string with all "unsafe"
00648 //               characters quoted, making a string suitable for
00649 //               placing in a URL.  Letters, digits, and the
00650 //               underscore, comma, period, and hyphen characters, as
00651 //               well as any included in the safe string, are left
00652 //               alone; all others are converted to hex
00653 //               representation.
00654 ////////////////////////////////////////////////////////////////////
00655 string URLSpec::
00656 quote(const string &source, const string &safe) {
00657   ostringstream result;
00658   result << hex << setfill('0');
00659 
00660   for (string::const_iterator si = source.begin(); si != source.end(); ++si) {
00661     char ch = (*si);
00662     switch (ch) {
00663     case '_':
00664     case ',':
00665     case '.':
00666     case '-':
00667       // Safe character.
00668       result << ch;
00669       break;
00670 
00671     default:
00672       if (isalnum(ch)) {
00673         // Letters and digits are safe.
00674         result << ch;
00675 
00676       } else if (safe.find(ch) != string::npos) {
00677         // If it's listed in "safe", it's safe.
00678         result << ch;
00679 
00680       } else {
00681         // Otherwise, escape it.
00682         result << '%' << setw(2) << (int)ch;
00683       }
00684     }
00685   }
00686 
00687   return result.str();
00688 }
00689 
00690 ////////////////////////////////////////////////////////////////////
00691 //     Function: URLSpec::quote_plus
00692 //       Access: Published, Static
00693 //  Description: Behaves like quote() with the additional behavior of
00694 //               replacing spaces with plus signs.
00695 ////////////////////////////////////////////////////////////////////
00696 string URLSpec::
00697 quote_plus(const string &source, const string &safe) {
00698   ostringstream result;
00699   result << hex << setfill('0');
00700 
00701   for (string::const_iterator si = source.begin(); si != source.end(); ++si) {
00702     char ch = (*si);
00703     switch (ch) {
00704     case '_':
00705     case ',':
00706     case '.':
00707     case '-':
00708       // Safe character.
00709       result << ch;
00710       break;
00711 
00712     case ' ':
00713       result << '+';
00714       break;
00715 
00716     default:
00717       if (isalnum(ch)) {
00718         // Letters and digits are safe.
00719         result << ch;
00720 
00721       } else if (safe.find(ch) != string::npos) {
00722         // If it's listed in "safe", it's safe.
00723         result << ch;
00724 
00725       } else {
00726         // Otherwise, escape it.
00727         result << '%' << setw(2) << (int)ch;
00728       }
00729     }
00730   }
00731 
00732   return result.str();
00733 }
00734 
00735 ////////////////////////////////////////////////////////////////////
00736 //     Function: URLSpec::unquote
00737 //       Access: Published, Static
00738 //  Description: Reverses the operation of quote(): converts escaped
00739 //               characters of the form "%xx" to their ascii
00740 //               equivalent.
00741 ////////////////////////////////////////////////////////////////////
00742 string URLSpec::
00743 unquote(const string &source) {
00744   string result;
00745 
00746   size_t p = 0;
00747   while (p < source.length()) {
00748     if (source[p] == '%' && p + 2 < source.length()) {
00749       int hex = 0;
00750       p++;
00751       for (int i = 0; i < 2; i++) {
00752         int value;
00753         char ch = source[p + i];
00754         if (isdigit(ch)) {
00755           value = ch - '0';
00756         } else {
00757           value = tolower(ch) - 'a' + 10;
00758         }
00759         hex = (hex << 4) | value;
00760       }
00761       result += (char)hex;
00762       p += 2;
00763 
00764     } else {
00765       result += source[p];
00766       p++;
00767     }
00768   }
00769 
00770   return result;
00771 }
00772 
00773 ////////////////////////////////////////////////////////////////////
00774 //     Function: URLSpec::unquote_plus
00775 //       Access: Published, Static
00776 //  Description: Reverses the operation of quote_plus(): converts escaped
00777 //               characters of the form "%xx" to their ascii
00778 //               equivalent, and also converts plus signs to spaces.
00779 ////////////////////////////////////////////////////////////////////
00780 string URLSpec::
00781 unquote_plus(const string &source) {
00782   string result;
00783 
00784   size_t p = 0;
00785   while (p < source.length()) {
00786     if (source[p] == '%' && p + 2 < source.length()) {
00787       int hex = 0;
00788       p++;
00789       for (int i = 0; i < 2; i++) {
00790         int value;
00791         char ch = source[p + i];
00792         if (isdigit(ch)) {
00793           value = ch - '0';
00794         } else {
00795           value = tolower(ch) - 'a' + 10;
00796         }
00797         hex = (hex << 4) | value;
00798       }
00799       result += (char)hex;
00800       p += 2;
00801 
00802     } else if (source[p] == '+') {
00803       result += ' ';
00804       p++;
00805 
00806     } else {
00807       result += source[p];
00808       p++;
00809     }
00810   }
00811 
00812   return result;
00813 }
00814 
00815 ////////////////////////////////////////////////////////////////////
00816 //     Function: URLSpec::parse_authority
00817 //       Access: Private
00818 //  Description: Assumes _url[_username_start .. _port_end - 1] is
00819 //               the authority component if the URL, consisting of
00820 //               [username@]server[:port].  Parses out the three
00821 //               pieces and updates the various _start and _end
00822 //               parameters accordingly.
00823 ////////////////////////////////////////////////////////////////////
00824 void URLSpec::
00825 parse_authority() {
00826   _flags &= ~(F_has_username | F_has_server | F_has_port);
00827 
00828   if (!has_authority()) {
00829     return;
00830   }
00831 
00832   // Assume we don't have a username or port unless we find them.
00833   _username_end = _username_start;
00834   _port_start = _port_end;
00835 
00836   // We assume we have a server, even if it becomes the empty string.
00837   _flags |= F_has_server;
00838   _server_start = _username_start;
00839   _server_end = _port_end;
00840 
00841   // Is there a username?
00842   size_t at_sign = _url.find('@', _username_start);
00843   if (at_sign < _port_end) {
00844     // We have a username.
00845     _flags |= F_has_username;
00846     _username_end = at_sign;
00847     _server_start = at_sign + 1;
00848   }
00849 
00850   // Is there a port?
00851   size_t colon = _url.find(':', _server_start);
00852   if (colon < _port_end) {
00853     // Yep.
00854     _flags |= F_has_port;
00855     _server_end = colon;
00856     _port_start = colon + 1;
00857         
00858     // Decode the port into an integer.  Don't bother to error
00859     // check if it's not really an integer.
00860     string port_str = _url.substr(_port_start, _port_end - _port_start);
00861     _port = atoi(port_str.c_str());
00862   }
00863 
00864   // Make sure the server name is lowercase only.
00865   for (size_t si = _server_start; si != _server_end; ++si) {
00866     _url[si] = tolower(_url[si]);
00867   }
00868 
00869   // Also make sure the server name doesn't end with a dot.  It's
00870   // happened!  Silly users.
00871   if (_server_end > _server_start && _url[_server_end - 1] == '.') {
00872     _url = _url.substr(0, _server_end - 1) + _url.substr(_server_end);
00873     _server_end--;
00874     _port_start--;
00875     _port_end--;
00876     _path_start--;
00877     _path_end--;
00878     _query_start--;
00879   }
00880 }
 All Classes Functions Variables Enumerations