Panda3D
|
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 ©) { 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 }