00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "urlSpec.h"
00016
00017 #include <ctype.h>
00018
00019
00020
00021
00022
00023
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
00043
00044
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
00065
00066
00067
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
00079
00080
00081
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
00093
00094
00095
00096
00097
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
00109
00110
00111
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
00130
00131
00132
00133
00134
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
00148
00149
00150
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
00162
00163
00164
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
00179
00180
00181
00182 void URLSpec::
00183 set_scheme(const string &scheme) {
00184 int length_adjust;
00185
00186
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
00195 if (!has_scheme()) {
00196 return;
00197 }
00198
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
00206
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
00217
00218
00219 _scheme_end--;
00220 _flags |= F_has_scheme;
00221
00222 } else {
00223
00224
00225
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
00249
00250
00251
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
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
00275 length_adjust = authority.length() + 2;
00276
00277 string extra_slash;
00278 if (has_path() && _url[_path_start] != '/') {
00279
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
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
00304
00305
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
00328
00329
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
00352
00353
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
00377
00378
00379
00380
00381 void URLSpec::
00382 set_port(int port) {
00383 ostringstream str;
00384 str << port;
00385 set_port(str.str());
00386 }
00387
00388
00389
00390
00391
00392
00393
00394
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
00412
00413
00414
00415 void URLSpec::
00416 set_path(const string &path) {
00417 int length_adjust;
00418
00419 if (path.empty()) {
00420
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
00430 string cpath = path;
00431 if (cpath[0] != '/') {
00432
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
00442 string cpath = path;
00443 if (cpath[0] != '/') {
00444
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
00458
00459
00460
00461 void URLSpec::
00462 set_query(const string &query) {
00463 if (query.empty()) {
00464
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
00474 _url = _url.substr(0, _query_start) + "?" + query;
00475 _flags |= F_has_query;
00476 _query_start++;
00477
00478 } else {
00479
00480 _url = _url.substr(0, _query_start) + query;
00481 }
00482 }
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492 void URLSpec::
00493 set_url(const string &url, bool server_name_expected) {
00494 size_t p, q;
00495
00496
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
00511 server_name_expected = false;
00512 }
00513
00514
00515
00516
00517
00518 for (p = 0; p < _url.length() && _url[p] != '?'; p++) {
00519 if (_url[p] == '\\') {
00520 _url[p] = '/';
00521 }
00522 }
00523
00524
00525 _flags = 0;
00526 _port = 0;
00527
00528
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
00535 _flags |= F_has_scheme;
00536 _scheme_end = next;
00537
00538
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
00547
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
00556
00557
00558
00559 bool has_authority = (has_scheme() || server_name_expected);
00560
00561
00562
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
00571
00572
00573 if (!leading_slashes) {
00574 if (start < _url.length() && _url[start] == '/') {
00575
00576 _url = _url.substr(0, start + 1) + _url.substr(start);
00577 } else {
00578
00579 _url = _url.substr(0, start) + "//" + _url.substr(start);
00580 }
00581 }
00582
00583
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
00596 _path_start = start;
00597 _path_end = start;
00598 if (start < _url.length() && url[start] != '?') {
00599
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
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
00620
00621
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
00636
00637
00638
00639 void URLSpec::
00640 output(ostream &out) const {
00641 out << get_url();
00642 }
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
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
00668 result << ch;
00669 break;
00670
00671 default:
00672 if (isalnum(ch)) {
00673
00674 result << ch;
00675
00676 } else if (safe.find(ch) != string::npos) {
00677
00678 result << ch;
00679
00680 } else {
00681
00682 result << '%' << setw(2) << (int)ch;
00683 }
00684 }
00685 }
00686
00687 return result.str();
00688 }
00689
00690
00691
00692
00693
00694
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
00709 result << ch;
00710 break;
00711
00712 case ' ':
00713 result << '+';
00714 break;
00715
00716 default:
00717 if (isalnum(ch)) {
00718
00719 result << ch;
00720
00721 } else if (safe.find(ch) != string::npos) {
00722
00723 result << ch;
00724
00725 } else {
00726
00727 result << '%' << setw(2) << (int)ch;
00728 }
00729 }
00730 }
00731
00732 return result.str();
00733 }
00734
00735
00736
00737
00738
00739
00740
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
00775
00776
00777
00778
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
00817
00818
00819
00820
00821
00822
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
00833 _username_end = _username_start;
00834 _port_start = _port_end;
00835
00836
00837 _flags |= F_has_server;
00838 _server_start = _username_start;
00839 _server_end = _port_end;
00840
00841
00842 size_t at_sign = _url.find('@', _username_start);
00843 if (at_sign < _port_end) {
00844
00845 _flags |= F_has_username;
00846 _username_end = at_sign;
00847 _server_start = at_sign + 1;
00848 }
00849
00850
00851 size_t colon = _url.find(':', _server_start);
00852 if (colon < _port_end) {
00853
00854 _flags |= F_has_port;
00855 _server_end = colon;
00856 _port_start = colon + 1;
00857
00858
00859
00860 string port_str = _url.substr(_port_start, _port_end - _port_start);
00861 _port = atoi(port_str.c_str());
00862 }
00863
00864
00865 for (size_t si = _server_start; si != _server_end; ++si) {
00866 _url[si] = tolower(_url[si]);
00867 }
00868
00869
00870
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 }