00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "urlSpec.h"
00020
00021 #include <ctype.h>
00022
00023
00024
00025
00026
00027
00028
00029 URLSpec::
00030 URLSpec() {
00031 _port = 0;
00032 _flags = 0;
00033 _scheme_end = 0;
00034 _username_start = 0;
00035 _username_end = 0;
00036 _server_start = 0;
00037 _server_end = 0;
00038 _port_start = 0;
00039 _port_end = 0;
00040 _path_start = 0;
00041 _path_end = 0;
00042 _query_start = 0;
00043 }
00044
00045
00046
00047
00048
00049
00050 void URLSpec::
00051 operator = (const URLSpec ©) {
00052 _url = copy._url;
00053 _port = copy._port;
00054 _flags = copy._flags;
00055 _scheme_end = copy._scheme_end;
00056 _username_start = copy._username_start;
00057 _username_end = copy._username_end;
00058 _server_start = copy._server_start;
00059 _server_end = copy._server_end;
00060 _port_start = copy._port_start;
00061 _port_end = copy._port_end;
00062 _path_start = copy._path_start;
00063 _path_end = copy._path_end;
00064 _query_start = copy._query_start;
00065 }
00066
00067
00068
00069
00070
00071
00072
00073 string URLSpec::
00074 get_scheme() const {
00075 if (has_scheme()) {
00076 return _url.substr(0, _scheme_end);
00077 }
00078 return string();
00079 }
00080
00081
00082
00083
00084
00085
00086
00087 int URLSpec::
00088 get_port() const {
00089 if (has_port()) {
00090 return _port;
00091 }
00092 string scheme = get_scheme();
00093 if (scheme == "https") {
00094 return 443;
00095
00096 } else {
00097 return 80;
00098 }
00099 }
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109 string URLSpec::
00110 get_server_and_port() const {
00111 if (has_port()) {
00112 return _url.substr(_server_start, _port_end - _server_start);
00113 }
00114 ostringstream strm;
00115 strm << get_server() << ":" << get_port();
00116 return strm.str();
00117 }
00118
00119
00120
00121
00122
00123
00124
00125 string URLSpec::
00126 get_path() const {
00127 if (has_path()) {
00128 return _url.substr(_path_start, _path_end - _path_start);
00129 }
00130 return "/";
00131 }
00132
00133
00134
00135
00136
00137
00138 void URLSpec::
00139 set_scheme(const string &scheme) {
00140 int length_adjust;
00141
00142
00143 string lc_scheme;
00144 lc_scheme.reserve(scheme.length());
00145 for (string::const_iterator si = scheme.begin(); si != scheme.end(); ++si) {
00146 lc_scheme += tolower(*si);
00147 }
00148
00149 if (lc_scheme.empty()) {
00150
00151 if (!has_scheme()) {
00152 return;
00153 }
00154
00155 _scheme_end++;
00156 length_adjust = -(int)_scheme_end;
00157 _url = _url.substr(_scheme_end);
00158 _flags &= ~F_has_scheme;
00159
00160 } else if (!has_scheme()) {
00161
00162
00163 if (lc_scheme[lc_scheme.length() - 1] == ':') {
00164 length_adjust = lc_scheme.length();
00165 _url = lc_scheme + _url;
00166
00167 } else {
00168 length_adjust = lc_scheme.length() + 1;
00169 _url = lc_scheme + ":" + _url;
00170 }
00171
00172
00173
00174
00175 _scheme_end--;
00176 _flags |= F_has_scheme;
00177
00178 } else {
00179
00180
00181
00182 if (lc_scheme[lc_scheme.length() - 1] == ':') {
00183 lc_scheme = lc_scheme.substr(0, lc_scheme.length() - 1);
00184 }
00185
00186 int old_length = (int)_scheme_end;
00187 length_adjust = scheme.length() - old_length;
00188 _url = lc_scheme + _url.substr(_scheme_end);
00189 }
00190
00191 _scheme_end += length_adjust;
00192 _username_start += length_adjust;
00193 _username_end += length_adjust;
00194 _server_start += length_adjust;
00195 _server_end += length_adjust;
00196 _port_start += length_adjust;
00197 _port_end += length_adjust;
00198 _path_start += length_adjust;
00199 _path_end += length_adjust;
00200 _query_start += length_adjust;
00201 }
00202
00203
00204
00205
00206
00207
00208
00209 void URLSpec::
00210 set_authority(const string &authority) {
00211 int length_adjust;
00212 int extra_slash_adjust = 0;
00213
00214 if (authority.empty()) {
00215
00216 if (!has_authority()) {
00217 return;
00218 }
00219 _username_start -= 2;
00220 length_adjust = -((int)_port_end - (int)_username_start);
00221 _url = _url.substr(0, _username_start) + _url.substr(_port_end);
00222 _flags &= ~(F_has_authority | F_has_username | F_has_server | F_has_port);
00223
00224 _username_end = _username_start;
00225 _server_start = _username_start;
00226 _server_end = _username_start;
00227 _port_start = _username_start;
00228
00229 } else if (!has_authority()) {
00230
00231 length_adjust = authority.length() + 2;
00232
00233 string extra_slash;
00234 if (has_path() && _url[_path_start] != '/') {
00235
00236 extra_slash = '/';
00237 extra_slash_adjust = 1;
00238 }
00239 _url = _url.substr(0, _username_start) + "//" + authority + extra_slash + _url.substr(_port_end);
00240 _flags |= F_has_authority;
00241 _username_start += 2;
00242
00243 } else {
00244
00245 int old_length = (int)_port_end - (int)_username_start;
00246 length_adjust = authority.length() - old_length;
00247 _url = _url.substr(0, _username_start) + authority + _url.substr(_port_end);
00248 }
00249
00250 _port_end += length_adjust;
00251 _path_start += length_adjust;
00252 _path_end += length_adjust + extra_slash_adjust;
00253 _query_start += length_adjust + extra_slash_adjust;
00254
00255 parse_authority();
00256 }
00257
00258
00259
00260
00261
00262
00263 void URLSpec::
00264 set_username(const string &username) {
00265 if (username.empty() && !has_authority()) {
00266 return;
00267 }
00268 string authority;
00269
00270 if (!username.empty()) {
00271 authority = username + "@";
00272 }
00273 authority += get_server();
00274 if (has_port()) {
00275 authority += ":";
00276 authority += get_port_str();
00277 }
00278
00279 set_authority(authority);
00280 }
00281
00282
00283
00284
00285
00286
00287 void URLSpec::
00288 set_server(const string &server) {
00289 if (server.empty() && !has_authority()) {
00290 return;
00291 }
00292 string authority;
00293
00294 if (has_username()) {
00295 authority = get_username() + "@";
00296 }
00297 authority += server;
00298 if (has_port()) {
00299 authority += ":";
00300 authority += get_port_str();
00301 }
00302
00303 set_authority(authority);
00304 }
00305
00306
00307
00308
00309
00310
00311 void URLSpec::
00312 set_port(const string &port) {
00313 if (port.empty() && !has_authority()) {
00314 return;
00315 }
00316 string authority;
00317
00318 if (has_username()) {
00319 authority = get_username() + "@";
00320 }
00321 authority += get_server();
00322
00323 if (!port.empty()) {
00324 authority += ":";
00325 authority += port;
00326 }
00327
00328 set_authority(authority);
00329 }
00330
00331
00332
00333
00334
00335
00336
00337 void URLSpec::
00338 set_port(int port) {
00339 ostringstream str;
00340 str << port;
00341 set_port(str.str());
00342 }
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352 void URLSpec::
00353 set_server_and_port(const string &server_and_port) {
00354 if (server_and_port.empty() && !has_authority()) {
00355 return;
00356 }
00357 string authority;
00358
00359 if (has_username()) {
00360 authority = get_username() + "@";
00361 }
00362 authority += server_and_port;
00363 set_authority(authority);
00364 }
00365
00366
00367
00368
00369
00370
00371 void URLSpec::
00372 set_path(const string &path) {
00373 int length_adjust;
00374
00375 if (path.empty()) {
00376
00377 if (!has_path()) {
00378 return;
00379 }
00380 length_adjust = -((int)_path_end - (int)_path_start);
00381 _url = _url.substr(0, _path_start) + _url.substr(_path_end);
00382 _flags &= ~F_has_path;
00383
00384 } else if (!has_path()) {
00385
00386 string cpath = path;
00387 if (cpath[0] != '/') {
00388
00389 cpath = '/' + cpath;
00390 }
00391 length_adjust = cpath.length();
00392
00393 _url = _url.substr(0, _path_start) + cpath + _url.substr(_path_end);
00394 _flags |= F_has_path;
00395
00396 } else {
00397
00398 string cpath = path;
00399 if (cpath[0] != '/') {
00400
00401 cpath = '/' + cpath;
00402 }
00403 int old_length = (int)_path_end - (int)_path_start;
00404 length_adjust = cpath.length() - old_length;
00405 _url = _url.substr(0, _path_start) + cpath + _url.substr(_path_end);
00406 }
00407
00408 _path_end += length_adjust;
00409 _query_start += length_adjust;
00410 }
00411
00412
00413
00414
00415
00416
00417 void URLSpec::
00418 set_query(const string &query) {
00419 if (query.empty()) {
00420
00421 if (!has_query()) {
00422 return;
00423 }
00424 _query_start--;
00425 _url = _url.substr(0, _query_start);
00426 _flags &= ~F_has_query;
00427
00428 } else if (!has_query()) {
00429
00430 _url = _url.substr(0, _query_start) + "?" + query;
00431 _flags |= F_has_query;
00432 _query_start++;
00433
00434 } else {
00435
00436 _url = _url.substr(0, _query_start) + query;
00437 }
00438 }
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448 void URLSpec::
00449 set_url(const string &url, bool server_name_expected) {
00450 _url = url;
00451 _flags = 0;
00452
00453 if (url.empty()) {
00454
00455 server_name_expected = false;
00456 }
00457
00458
00459
00460 size_t p;
00461 for (p = 0; p < _url.length(); p++) {
00462 if (_url[p] == '\\') {
00463 _url[p] = '/';
00464 }
00465 }
00466
00467
00468 _flags = 0;
00469 _port = 0;
00470
00471
00472 size_t start = 0;
00473
00474 _scheme_end = start;
00475 size_t next = _url.find_first_of(":/", start);
00476 if (next < _url.length() - 1 && _url.substr(next, 2) == ":/") {
00477
00478 _flags |= F_has_scheme;
00479 _scheme_end = next;
00480
00481
00482 for (size_t p = 0; p < _scheme_end; ++p) {
00483 _url[p] = tolower(_url[p]);
00484 }
00485
00486 start = next + 1;
00487 }
00488
00489
00490
00491 _username_start = start;
00492 _username_end = start;
00493 _server_start = start;
00494 _server_end = start;
00495 _port_start = start;
00496 _port_end = start;
00497
00498
00499
00500
00501
00502 bool has_authority = (has_scheme() || server_name_expected);
00503
00504
00505
00506 bool leading_slashes =
00507 (start < _url.length() - 1 && _url.substr(start, 2) == "//");
00508 if (leading_slashes) {
00509 has_authority = true;
00510 }
00511
00512 if (has_authority) {
00513
00514
00515
00516 if (!leading_slashes) {
00517 if (start < _url.length() && _url[start] == '/') {
00518
00519 _url = _url.substr(0, start + 1) + _url.substr(start);
00520 } else {
00521
00522 _url = _url.substr(0, start) + "//" + _url.substr(start);
00523 }
00524 }
00525
00526
00527 start += 2;
00528 _flags |= F_has_authority;
00529 _username_start = start;
00530 _port_end = _url.find_first_of("/?", start);
00531 if (_port_end == string::npos) {
00532 _port_end = _url.length();
00533 }
00534 parse_authority();
00535 start = _port_end;
00536 }
00537
00538
00539 _path_start = start;
00540 _path_end = start;
00541 if (start < _url.length() && url[start] != '?') {
00542
00543 _flags |= F_has_path;
00544 _path_start = start;
00545 _path_end = _url.find("?", _path_start);
00546 if (_path_end == string::npos) {
00547 _path_end = _url.length();
00548 }
00549 start = _path_end;
00550 }
00551
00552
00553 _query_start = start;
00554 if (start < _url.length()) {
00555 nassertv(_url[start] == '?');
00556 _flags |= F_has_query;
00557 _query_start++;
00558 }
00559 }
00560
00561
00562
00563
00564
00565
00566 bool URLSpec::
00567 input(istream &in) {
00568 string url;
00569 in >> url;
00570 if (!in) {
00571 return false;
00572 }
00573 set_url(url);
00574 return true;
00575 }
00576
00577
00578
00579
00580
00581
00582 void URLSpec::
00583 output(ostream &out) const {
00584 out << get_url();
00585 }
00586
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598 string URLSpec::
00599 quote(const string &source, const string &safe) {
00600 ostringstream result;
00601 result << hex << setfill('0');
00602
00603 for (string::const_iterator si = source.begin(); si != source.end(); ++si) {
00604 char ch = (*si);
00605 switch (ch) {
00606 case '_':
00607 case ',':
00608 case '.':
00609 case '-':
00610
00611 result << ch;
00612 break;
00613
00614 default:
00615 if (isalnum(ch)) {
00616
00617 result << ch;
00618
00619 } else if (safe.find(ch) != string::npos) {
00620
00621 result << ch;
00622
00623 } else {
00624
00625 result << '%' << setw(2) << (int)ch;
00626 }
00627 }
00628 }
00629
00630 return result.str();
00631 }
00632
00633
00634
00635
00636
00637
00638
00639 string URLSpec::
00640 quote_plus(const string &source, const string &safe) {
00641 ostringstream result;
00642 result << hex << setfill('0');
00643
00644 for (string::const_iterator si = source.begin(); si != source.end(); ++si) {
00645 char ch = (*si);
00646 switch (ch) {
00647 case '_':
00648 case ',':
00649 case '.':
00650 case '-':
00651
00652 result << ch;
00653 break;
00654
00655 case ' ':
00656 result << '+';
00657 break;
00658
00659 default:
00660 if (isalnum(ch)) {
00661
00662 result << ch;
00663
00664 } else if (safe.find(ch) != string::npos) {
00665
00666 result << ch;
00667
00668 } else {
00669
00670 result << '%' << setw(2) << (int)ch;
00671 }
00672 }
00673 }
00674
00675 return result.str();
00676 }
00677
00678
00679
00680
00681
00682
00683
00684
00685 string URLSpec::
00686 unquote(const string &source) {
00687 string result;
00688
00689 size_t p = 0;
00690 while (p < source.length()) {
00691 if (source[p] == '%' && p + 2 < source.length()) {
00692 int hex = 0;
00693 p++;
00694 for (int i = 0; i < 2; i++) {
00695 int value;
00696 char ch = source[p + i];
00697 if (isdigit(ch)) {
00698 value = ch - '0';
00699 } else {
00700 value = tolower(ch) - 'a' + 10;
00701 }
00702 hex = (hex << 4) | value;
00703 }
00704 result += (char)hex;
00705 p += 2;
00706
00707 } else {
00708 result += source[p];
00709 p++;
00710 }
00711 }
00712
00713 return result;
00714 }
00715
00716
00717
00718
00719
00720
00721
00722
00723 string URLSpec::
00724 unquote_plus(const string &source) {
00725 string result;
00726
00727 size_t p = 0;
00728 while (p < source.length()) {
00729 if (source[p] == '%' && p + 2 < source.length()) {
00730 int hex = 0;
00731 p++;
00732 for (int i = 0; i < 2; i++) {
00733 int value;
00734 char ch = source[p + i];
00735 if (isdigit(ch)) {
00736 value = ch - '0';
00737 } else {
00738 value = tolower(ch) - 'a' + 10;
00739 }
00740 hex = (hex << 4) | value;
00741 }
00742 result += (char)hex;
00743 p += 2;
00744
00745 } else if (source[p] == '+') {
00746 result += ' ';
00747 p++;
00748
00749 } else {
00750 result += source[p];
00751 p++;
00752 }
00753 }
00754
00755 return result;
00756 }
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767 void URLSpec::
00768 parse_authority() {
00769 _flags &= ~(F_has_username | F_has_server | F_has_port);
00770
00771 if (!has_authority()) {
00772 return;
00773 }
00774
00775
00776 _username_end = _username_start;
00777 _port_start = _port_end;
00778
00779
00780 _flags |= F_has_server;
00781 _server_start = _username_start;
00782 _server_end = _port_end;
00783
00784
00785 size_t at_sign = _url.find('@', _username_start);
00786 if (at_sign < _port_end) {
00787
00788 _flags |= F_has_username;
00789 _username_end = at_sign;
00790 _server_start = at_sign + 1;
00791 }
00792
00793
00794 size_t colon = _url.find(':', _server_start);
00795 if (colon < _port_end) {
00796
00797 _flags |= F_has_port;
00798 _server_end = colon;
00799 _port_start = colon + 1;
00800
00801
00802
00803 string port_str = _url.substr(_port_start, _port_end - _port_start);
00804 _port = atoi(port_str.c_str());
00805 }
00806
00807
00808 for (size_t si = _server_start; si != _server_end; ++si) {
00809 _url[si] = tolower(_url[si]);
00810 }
00811
00812
00813
00814 if (_server_end > _server_start && _url[_server_end - 1] == '.') {
00815 _url = _url.substr(0, _server_end - 1) + _url.substr(_server_end);
00816 _server_end--;
00817 _port_start--;
00818 _port_end--;
00819 _path_start--;
00820 _path_end--;
00821 _query_start--;
00822 }
00823 }