Panda3D
Loading...
Searching...
No Matches
urlSpec.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file urlSpec.cxx
10 * @author drose
11 * @date 2002-09-24
12 */
13
14#include "urlSpec.h"
15#include "filename.h"
16#include "string_utils.h"
17
18#include <ctype.h>
19
20using std::istream;
21using std::ostream;
22using std::ostringstream;
23using std::setfill;
24using std::setw;
25using std::string;
26
27/**
28 *
29 */
30URLSpec::
31URLSpec() {
32 _port = 0;
33 _flags = 0;
34 _scheme_end = 0;
35 _username_start = 0;
36 _username_end = 0;
37 _server_start = 0;
38 _server_end = 0;
39 _port_start = 0;
40 _port_end = 0;
41 _path_start = 0;
42 _path_end = 0;
43 _query_start = 0;
44}
45
46/**
47 * Creates a URLSpec by appending a path to the end of the old URLSpec,
48 * inserting an intervening forward slash if necessary.
49 */
50URLSpec::
51URLSpec(const URLSpec &url, const Filename &path) {
52 (*this) = url;
53 if (!path.empty()) {
54 string dirname = get_path();
55
56 // Check if the path already ends in a slash.
57 if (!dirname.empty() && dirname[dirname.size() - 1] == '/') {
58 if (path[0] == '/') {
59 // And the filename begins with one. Remove the extra slash.
60 dirname.resize(dirname.size() - 1);
61 }
62 } else {
63 if (path[0] != '/') {
64 // Neither has a slash, so insert one.
65 dirname += '/';
66 }
67 }
68 set_path(dirname + path.get_fullpath());
69 }
70}
71
72/**
73 * Returns a number less than zero if this URLSpec sorts before the other one,
74 * greater than zero if it sorts after, or zero if they are equivalent.
75 */
77compare_to(const URLSpec &other) const {
78 int cmp;
79 if (has_scheme() != other.has_scheme()) {
80 return (has_scheme() < other.has_scheme()) ? -1 : 1;
81 }
82 if (has_scheme()) {
83 cmp = cmp_nocase(get_scheme(), other.get_scheme());
84 if (cmp != 0) {
85 return cmp;
86 }
87 }
88 if (has_username() != other.has_username()) {
89 return (has_username() < other.has_username()) ? -1 : 1;
90 }
91 if (has_username()) {
92 cmp = get_username().compare(other.get_username());
93 if (cmp != 0) {
94 return cmp;
95 }
96 }
97 if (has_server() != other.has_server()) {
98 return (has_server() < other.has_server()) ? -1 : 1;
99 }
100 if (has_server()) {
101 cmp = cmp_nocase(get_server(), other.get_server());
102 if (cmp != 0) {
103 return cmp;
104 }
105 }
106 return get_path_and_query().compare(other.get_path_and_query());
107}
108
109/**
110 *
111 */
112size_t URLSpec::
113get_hash() const {
114 size_t hash = 0;
115 hash = int_hash::add_hash(hash, _flags & (F_has_scheme | F_has_username | F_has_server));
116 if (has_scheme()) {
118 }
119 if (has_username()) {
120 hash = string_hash::add_hash(hash, get_username());
121 }
122 if (has_server()) {
124 }
125 hash = int_hash::add_hash(hash, get_port());
127 return hash;
128}
129
130/**
131 * Returns the scheme specified by the URL, or empty string if no scheme is
132 * specified.
133 */
134string URLSpec::
135get_scheme() const {
136 if (has_scheme()) {
137 return _url.substr(0, _scheme_end);
138 }
139 return string();
140}
141
142/**
143 * Returns the port number specified by the URL, or the default port if not
144 * specified.
145 */
146uint16_t URLSpec::
147get_port() const {
148 if (has_port()) {
149 return _port;
150 }
152}
153
154/**
155 * Returns true if the port number encoded in this URL is the default port
156 * number for the scheme (or if there is no port number), or false if it is a
157 * nonstandard port.
158 */
160is_default_port() const {
161 if (!has_port()) {
162 return true;
163 }
164 return (_port == get_default_port_for_scheme(get_scheme()));
165}
166
167/**
168 * Returns the default port number for the indicated scheme, or 0 if there is
169 * no known default.
170 */
172get_default_port_for_scheme(const string &scheme) {
173 if (scheme == "http" || scheme.empty()) {
174 return 80;
175
176 } else if (scheme == "https") {
177 return 443;
178
179 } else if (scheme == "socks") {
180 return 1080;
181 }
182
183 return 0;
184}
185
186/**
187 * Returns a string consisting of the server name, followed by a colon,
188 * followed by the port number. If the port number is not explicitly given in
189 * the URL, this string will include the implicit port number.
190 * If the server is an IPv6 address, it will be enclosed in square brackets.
191 */
192string URLSpec::
193get_server_and_port() const {
194 ostringstream strm;
195 string server = get_server();
196 if (server.find(':') != string::npos) {
197 // Protect an IPv6 address by enclosing it in square brackets.
198 strm << '[' << server << ']';
199
200 } else {
201 if (!has_port()) {
202 return server;
203 }
204 strm << server;
205 }
206 if (has_port()) {
207 strm << ":" << get_port();
208 }
209 return strm.str();
210}
211
212/**
213 * Returns the path specified by the URL, or "/" if no path is specified.
214 */
215string URLSpec::
216get_path() const {
217 if (has_path()) {
218 return _url.substr(_path_start, _path_end - _path_start);
219 }
220 return "/";
221}
222
223/**
224 * Returns the path (or "/" if no path is specified), followed by the query if
225 * it is specified.
226 */
228get_path_and_query() const {
229 if (has_path()) {
230 return _url.substr(_path_start);
231 }
232 if (has_query()) {
233 return "/?" + get_query();
234 }
235 return "/";
236}
237
238/**
239 * Replaces the scheme part of the URL specification.
240 */
241void URLSpec::
242set_scheme(const string &scheme) {
243 int length_adjust;
244
245 // The scheme is always converted to lowercase.
246 string lc_scheme;
247 lc_scheme.reserve(scheme.length());
248 for (string::const_iterator si = scheme.begin(); si != scheme.end(); ++si) {
249 lc_scheme += tolower(*si);
250 }
251
252 if (lc_scheme.empty()) {
253 // Remove the scheme specification.
254 if (!has_scheme()) {
255 return;
256 }
257 // Increment over the trailing colon so we can remove that too.
258 _scheme_end++;
259 length_adjust = -(int)_scheme_end;
260 _url = _url.substr(_scheme_end);
261 _flags &= ~F_has_scheme;
262
263 } else if (!has_scheme()) {
264 // Insert a new scheme specification. The user may or may not have
265 // specified a colon.
266 if (lc_scheme[lc_scheme.length() - 1] == ':') {
267 length_adjust = lc_scheme.length();
268 _url = lc_scheme + _url;
269
270 } else {
271 length_adjust = lc_scheme.length() + 1;
272 _url = lc_scheme + ":" + _url;
273 }
274
275 // Since the length_adjust flag, above, now accounts for the colon,
276 // subtract one from _scheme_end (which should not include the colon).
277 _scheme_end--;
278 _flags |= F_has_scheme;
279
280 } else {
281 // Replace the existing scheme specification. Since the existing scheme
282 // will already be trailed by a colon, remove the colon from the string if
283 // the user appended one.
284 if (lc_scheme[lc_scheme.length() - 1] == ':') {
285 lc_scheme = lc_scheme.substr(0, lc_scheme.length() - 1);
286 }
287
288 int old_length = (int)_scheme_end;
289 length_adjust = scheme.length() - old_length;
290 _url = lc_scheme + _url.substr(_scheme_end);
291 }
292
293 _scheme_end += length_adjust;
294 _username_start += length_adjust;
295 _username_end += length_adjust;
296 _server_start += length_adjust;
297 _server_end += length_adjust;
298 _port_start += length_adjust;
299 _port_end += length_adjust;
300 _path_start += length_adjust;
301 _path_end += length_adjust;
302 _query_start += length_adjust;
303}
304
305/**
306 * Replaces the authority part of the URL specification. This includes the
307 * username, server, and port.
308 */
309void URLSpec::
310set_authority(const string &authority) {
311 int length_adjust;
312 int extra_slash_adjust = 0;
313
314 if (authority.empty()) {
315 // Remove the authority specification.
316 if (!has_authority()) {
317 return;
318 }
319 _username_start -= 2;
320 length_adjust = -((int)_port_end - (int)_username_start);
321 _url = _url.substr(0, _username_start) + _url.substr(_port_end);
322 _flags &= ~(F_has_authority | F_has_username | F_has_server | F_has_port);
323
324 _username_end = _username_start;
325 _server_start = _username_start;
326 _server_end = _username_start;
327 _port_start = _username_start;
328
329 } else if (!has_authority()) {
330 // Insert a new authority specification.
331 length_adjust = authority.length() + 2;
332
333 string extra_slash;
334 if (has_path() && _url[_path_start] != '/') {
335 // If we have a path but it doesn't begin with a slash, it should.
336 extra_slash = '/';
337 extra_slash_adjust = 1;
338 }
339 _url = _url.substr(0, _username_start) + "//" + authority + extra_slash + _url.substr(_port_end);
340 _flags |= F_has_authority;
341 _username_start += 2;
342
343 } else {
344 // Replace an existing authority specification.
345 int old_length = (int)_port_end - (int)_username_start;
346 length_adjust = authority.length() - old_length;
347 _url = _url.substr(0, _username_start) + authority + _url.substr(_port_end);
348 }
349
350 _port_end += length_adjust;
351 _path_start += length_adjust;
352 _path_end += length_adjust + extra_slash_adjust;
353 _query_start += length_adjust + extra_slash_adjust;
354
355 parse_authority();
356}
357
358/**
359 * Replaces the username part of the URL specification.
360 */
361void URLSpec::
362set_username(const string &username) {
363 if (username.empty() && !has_authority()) {
364 return;
365 }
366 string authority;
367
368 if (!username.empty()) {
369 authority = username + "@";
370 }
371
372 string server = get_server();
373 if (server.find(':') != string::npos) {
374 // Protect an IPv6 address by enclosing it in square brackets.
375 authority += "[" + server + "]";
376 } else {
377 authority += server;
378 }
379
380 if (has_port()) {
381 authority += ":";
382 authority += get_port_str();
383 }
384
385 set_authority(authority);
386}
387
388/**
389 * Replaces the server part of the URL specification.
390 * Unlike set_server_and_port, this method does not require IPv6 addresses to
391 * be enclosed in square brackets.
392 */
393void URLSpec::
394set_server(const string &server) {
395 if (server.empty() && !has_authority()) {
396 return;
397 }
398 string authority;
399
400 if (has_username()) {
401 authority = get_username() + "@";
402 }
403
404 if (server.find(':') != string::npos) {
405 // Protect an IPv6 address by enclosing it in square brackets.
406 authority += "[" + server + "]";
407 } else {
408 authority += server;
409 }
410
411 if (has_port()) {
412 authority += ":";
413 authority += get_port_str();
414 }
415
416 set_authority(authority);
417}
418
419/**
420 * Replaces the port part of the URL specification.
421 */
422void URLSpec::
423set_port(const string &port) {
424 if (port.empty() && !has_authority()) {
425 return;
426 }
427 string authority;
428
429 if (has_username()) {
430 authority = get_username() + "@";
431 }
432
433 string server = get_server();
434 if (server.find(':') != string::npos) {
435 // Protect an IPv6 address by enclosing it in square brackets.
436 authority += "[" + server + "]";
437 } else {
438 authority += server;
439 }
440
441 if (!port.empty()) {
442 authority += ":";
443 authority += port;
444 }
445
446 set_authority(authority);
447}
448
449/**
450 * Replaces the port part of the URL specification, given a numeric port
451 * number.
452 */
453void URLSpec::
454set_port(uint16_t port) {
455 set_port(format_string(port));
456}
457
458/**
459 * Replaces the server and port parts of the URL specification simultaneously.
460 * The input string should be of the form "server:port", or just "server" to
461 * make the port number implicit.
462 * Any IPv6 address must be enclosed in square brackets.
463 */
464void URLSpec::
465set_server_and_port(const string &server_and_port) {
466 if (server_and_port.empty() && !has_authority()) {
467 return;
468 }
469 string authority;
470
471 if (has_username()) {
472 authority = get_username() + "@";
473 }
474 authority += server_and_port;
475 set_authority(authority);
476}
477
478/**
479 * Replaces the path part of the URL specification.
480 */
481void URLSpec::
482set_path(const string &path) {
483 int length_adjust;
484
485 if (path.empty()) {
486 // Remove the path specification.
487 if (!has_path()) {
488 return;
489 }
490 length_adjust = -((int)_path_end - (int)_path_start);
491 _url = _url.substr(0, _path_start) + _url.substr(_path_end);
492 _flags &= ~F_has_path;
493
494 } else if (!has_path()) {
495 // Insert a new path specification.
496 string cpath = path;
497 if (cpath[0] != '/') {
498 // Paths must always begin with a slash.
499 cpath = '/' + cpath;
500 }
501 length_adjust = cpath.length();
502
503 _url = _url.substr(0, _path_start) + cpath + _url.substr(_path_end);
504 _flags |= F_has_path;
505
506 } else {
507 // Replace an existing path specification.
508 string cpath = path;
509 if (cpath[0] != '/') {
510 // Paths must always begin with a slash.
511 cpath = '/' + cpath;
512 }
513 int old_length = (int)_path_end - (int)_path_start;
514 length_adjust = cpath.length() - old_length;
515 _url = _url.substr(0, _path_start) + cpath + _url.substr(_path_end);
516 }
517
518 _path_end += length_adjust;
519 _query_start += length_adjust;
520}
521
522/**
523 * Replaces the query part of the URL specification.
524 */
525void URLSpec::
526set_query(const string &query) {
527 if (query.empty()) {
528 // Remove the query specification.
529 if (!has_query()) {
530 return;
531 }
532 _query_start--;
533 _url = _url.substr(0, _query_start);
534 _flags &= ~F_has_query;
535
536 } else if (!has_query()) {
537 // Insert a new query specification.
538 _url = _url.substr(0, _query_start) + "?" + query;
539 _flags |= F_has_query;
540 _query_start++;
541
542 } else {
543 // Replace an existing query specification.
544 _url = _url.substr(0, _query_start) + query;
545 }
546}
547
548/**
549 * Completely replaces the URL with the indicated string. If
550 * server_name_expected is true, it is a hint that an undecorated URL is
551 * probably a server name, not a local filename.
552 */
554set_url(const string &url, bool server_name_expected) {
555 size_t p, q;
556
557 // Omit leading and trailing whitespace.
558 p = 0;
559 while (p < url.length() && isspace(url[p])) {
560 p++;
561 }
562 q = url.length();
563 while (q > p && isspace(url[q - 1])) {
564 q--;
565 }
566
567 _url = url.substr(p, q - p);
568 _flags = 0;
569
570 if (url.empty()) {
571 // No server name on an empty string.
572 server_name_expected = false;
573 }
574
575 // First, replace backslashes with forward slashes, since this is a common
576 // mistake among Windows users. But don't do this after an embedded
577 // question mark, which begins parameters sent directly to the host (and
578 // maybe these parameters should include backslashes).
579 for (p = 0; p < _url.length() && _url[p] != '?'; p++) {
580 if (_url[p] == '\\') {
581 _url[p] = '/';
582 }
583 }
584
585 // Now normalize the percent-encoding sequences by making them uppercase.
586 for (p = 0; p + 2 < _url.length();) {
587 if (_url[p++] == '%') {
588 if (_url[p] != '%') {
589 _url[p] = toupper(_url[p]);
590 ++p;
591 _url[p] = toupper(_url[p]);
592 }
593 ++p;
594 }
595 }
596
597 // What have we got?
598 _flags = 0;
599 _port = 0;
600
601 // Look for the scheme specification.
602 size_t start = 0;
603
604 _scheme_end = start;
605 size_t next = _url.find_first_of(":/", start);
606 if (next < _url.length() - 1 && _url.substr(next, 2) == ":/") {
607 // We have a scheme.
608 _flags |= F_has_scheme;
609 _scheme_end = next;
610
611 // Ensure the scheme is lowercase.
612 for (size_t p = 0; p < _scheme_end; ++p) {
613 _url[p] = tolower(_url[p]);
614 }
615
616 start = next + 1;
617 }
618
619 // Look for the authority specification, which may include any of username,
620 // server, andor port.
621 _username_start = start;
622 _username_end = start;
623 _server_start = start;
624 _server_end = start;
625 _port_start = start;
626 _port_end = start;
627
628 // Try to determine if an authority is present. It is will generally be
629 // present if a scheme was present; also, we have a hint passed in from the
630 // context as to whether we expect an authority (e.g. a server name) to be
631 // present.
632 bool has_authority = (has_scheme() || server_name_expected);
633
634 // We also know we have an authority if the url contains two slashes at this
635 // point.
636 bool leading_slashes =
637 (start < _url.length() - 1 && _url.substr(start, 2) == "//");
638 if (leading_slashes) {
639 has_authority = true;
640 }
641
642 if (has_authority) {
643 // Now that we know we have an authority, we should ensure there are two
644 // slashes here, since there should be before the authority.
645 if (!leading_slashes) {
646 if (start < _url.length() && _url[start] == '/') {
647 // Well, at least we had one slash. Double it.
648 _url = _url.substr(0, start + 1) + _url.substr(start);
649 } else {
650 // No slashes at all. Insert them.
651 _url = _url.substr(0, start) + "//" + _url.substr(start);
652 }
653 }
654
655 // Begin the actual authority specification.
656 start += 2;
657 _flags |= F_has_authority;
658 _username_start = start;
659 _port_end = _url.find_first_of("/?", start);
660 if (_port_end == string::npos) {
661 _port_end = _url.length();
662 }
663 parse_authority();
664 start = _port_end;
665 }
666
667 // Everything up to the ?, if any, is the path.
668 _path_start = start;
669 _path_end = start;
670 if (start < _url.length() && url[start] != '?') {
671 // We have a path.
672 _flags |= F_has_path;
673 _path_start = start;
674 _path_end = _url.find("?", _path_start);
675 if (_path_end == string::npos) {
676 _path_end = _url.length();
677 }
678 start = _path_end;
679 }
680
681 // Everything after the ? is the query.
682 _query_start = start;
683 if (start < _url.length()) {
684 nassertv(_url[start] == '?');
685 _flags |= F_has_query;
686 _query_start++;
687 }
688}
689
690/**
691 *
692 */
693bool URLSpec::
694input(istream &in) {
695 string url;
696 in >> url;
697 if (!in) {
698 return false;
699 }
700 set_url(url);
701 return true;
702}
703
704/**
705 *
706 */
707void URLSpec::
708output(ostream &out) const {
709 out << get_url();
710}
711
712/**
713 * Returns the source string with all "unsafe" characters quoted, making a
714 * string suitable for placing in a URL. Letters, digits, and the underscore,
715 * comma, period, and hyphen characters, as well as any included in the safe
716 * string, are left alone; all others are converted to hex representation.
717 */
719quote(const string &source, const string &safe) {
720 ostringstream result;
721 result << std::hex << setfill('0');
722
723 for (string::const_iterator si = source.begin(); si != source.end(); ++si) {
724 char ch = (*si);
725 switch (ch) {
726 case '_':
727 case ',':
728 case '.':
729 case '-':
730 // Safe character.
731 result << ch;
732 break;
733
734 default:
735 if (isalnum(ch)) {
736 // Letters and digits are safe.
737 result << ch;
738
739 } else if (safe.find(ch) != string::npos) {
740 // If it's listed in "safe", it's safe.
741 result << ch;
742
743 } else {
744 // Otherwise, escape it.
745 result << '%' << setw(2) << (int)ch;
746 }
747 }
748 }
749
750 return result.str();
751}
752
753/**
754 * Behaves like quote() with the additional behavior of replacing spaces with
755 * plus signs.
756 */
758quote_plus(const string &source, const string &safe) {
759 ostringstream result;
760 result << std::hex << setfill('0');
761
762 for (string::const_iterator si = source.begin(); si != source.end(); ++si) {
763 char ch = (*si);
764 switch (ch) {
765 case '_':
766 case ',':
767 case '.':
768 case '-':
769 // Safe character.
770 result << ch;
771 break;
772
773 case ' ':
774 result << '+';
775 break;
776
777 default:
778 if (isalnum(ch)) {
779 // Letters and digits are safe.
780 result << ch;
781
782 } else if (safe.find(ch) != string::npos) {
783 // If it's listed in "safe", it's safe.
784 result << ch;
785
786 } else {
787 // Otherwise, escape it.
788 result << '%' << setw(2) << (int)ch;
789 }
790 }
791 }
792
793 return result.str();
794}
795
796/**
797 * Reverses the operation of quote(): converts escaped characters of the form
798 * "%xx" to their ascii equivalent.
799 */
801unquote(const string &source) {
802 string result;
803
804 size_t p = 0;
805 while (p < source.length()) {
806 if (source[p] == '%' && p + 2 < source.length()) {
807 int hex = 0;
808 p++;
809 for (int i = 0; i < 2; i++) {
810 int value;
811 char ch = source[p + i];
812 if (isdigit(ch)) {
813 value = ch - '0';
814 } else {
815 value = tolower(ch) - 'a' + 10;
816 }
817 hex = (hex << 4) | value;
818 }
819 result += (char)hex;
820 p += 2;
821
822 } else {
823 result += source[p];
824 p++;
825 }
826 }
827
828 return result;
829}
830
831/**
832 * Reverses the operation of quote_plus(): converts escaped characters of the
833 * form "%xx" to their ascii equivalent, and also converts plus signs to
834 * spaces.
835 */
837unquote_plus(const string &source) {
838 string result;
839
840 size_t p = 0;
841 while (p < source.length()) {
842 if (source[p] == '%' && p + 2 < source.length()) {
843 int hex = 0;
844 p++;
845 for (int i = 0; i < 2; i++) {
846 int value;
847 char ch = source[p + i];
848 if (isdigit(ch)) {
849 value = ch - '0';
850 } else {
851 value = tolower(ch) - 'a' + 10;
852 }
853 hex = (hex << 4) | value;
854 }
855 result += (char)hex;
856 p += 2;
857
858 } else if (source[p] == '+') {
859 result += ' ';
860 p++;
861
862 } else {
863 result += source[p];
864 p++;
865 }
866 }
867
868 return result;
869}
870
871/**
872 * Assumes _url[_username_start .. _port_end - 1] is the authority component
873 * if the URL, consisting of [username@]server[:port]. Parses out the three
874 * pieces and updates the various _start and _end parameters accordingly.
875 */
876void URLSpec::
877parse_authority() {
878 _flags &= ~(F_has_username | F_has_server | F_has_port);
879
880 if (!has_authority()) {
881 return;
882 }
883
884 // Assume we don't have a username or port unless we find them.
885 _username_end = _username_start;
886 _port_start = _port_end;
887
888 // We assume we have a server, even if it becomes the empty string.
889 _flags |= F_has_server;
890 _server_start = _username_start;
891 _server_end = _port_end;
892
893 // Is there a username?
894 size_t at_sign = _url.find('@', _username_start);
895 if (at_sign < _port_end) {
896 // We have a username.
897 _flags |= F_has_username;
898 _username_end = at_sign;
899 _server_start = at_sign + 1;
900 }
901
902 // Is this an IPv6 address in square brackets?
903 size_t bracket = _url.find('[', _server_start);
904 if (bracket < _port_end) {
905 // We won't include the brackets in the server name.
906 ++_server_start;
907
908 bracket = _url.find(']');
909 if (bracket < _server_end) {
910 _server_end = bracket;
911
912 // Is it followed directly by a port colon?
913 size_t colon = _url.find(':', _server_end);
914 if (colon < _port_end) {
915 _port_start = colon + 1;
916 }
917 }
918 } else {
919 // Is there a port?
920 size_t colon = _url.find(':', _server_start);
921 if (colon < _port_end) {
922 // Yep.
923 _server_end = colon;
924 _port_start = colon + 1;
925 }
926 }
927
928 if (_port_start < _port_end) {
929 _flags |= F_has_port;
930
931 // Decode the port into an integer. Don't bother to error check if it's
932 // not really an integer.
933 string port_str = _url.substr(_port_start, _port_end - _port_start);
934 _port = (uint16_t)atoi(port_str.c_str());
935 }
936
937 // Make sure the server name is lowercase only.
938 for (size_t si = _server_start; si != _server_end; ++si) {
939 _url[si] = tolower(_url[si]);
940 }
941
942 // Also make sure the server name doesn't end with a dot. It's happened!
943 // Silly users.
944 if (_server_end > _server_start && _url[_server_end - 1] == '.') {
945 _url = _url.substr(0, _server_end - 1) + _url.substr(_server_end);
946 _server_end--;
947 _port_start--;
948 _port_end--;
949 _path_start--;
950 _path_end--;
951 _query_start--;
952 }
953}
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
std::string get_fullpath() const
Returns the entire filename: directory, basename, extension.
Definition filename.I:338
A container for a URL, e.g.
Definition urlSpec.h:28
bool has_username() const
Returns true if the URL specifies a username (and/or password), false otherwise.
Definition urlSpec.I:77
bool has_path() const
Returns true if the URL includes a path specification (that is, the particular filename on the server...
Definition urlSpec.I:102
static int get_default_port_for_scheme(const std::string &scheme)
Returns the default port number for the indicated scheme, or 0 if there is no known default.
Definition urlSpec.cxx:172
set_port
Replaces the port part of the URL specification.
Definition urlSpec.h:97
set_scheme
Replaces the scheme part of the URL specification.
Definition urlSpec.h:93
set_path
Replaces the path part of the URL specification.
Definition urlSpec.h:99
get_username
Returns the username specified by the URL, if any.
Definition urlSpec.h:95
set_query
Replaces the query part of the URL specification.
Definition urlSpec.h:100
bool has_authority() const
Returns true if the URL specifies an authority (this includes username, server, and/or port),...
Definition urlSpec.I:68
static std::string quote(const std::string &source, const std::string &safe="/")
Returns the source string with all "unsafe" characters quoted, making a string suitable for placing i...
Definition urlSpec.cxx:719
bool has_query() const
Returns true if the URL includes a query specification, false otherwise.
Definition urlSpec.I:110
const std::string & get_url() const
Returns the complete URL specification.
Definition urlSpec.I:184
bool is_default_port() const
Returns true if the port number encoded in this URL is the default port number for the scheme (or if ...
Definition urlSpec.cxx:160
set_server
Replaces the server part of the URL specification.
Definition urlSpec.h:96
get_server
Returns the server name specified by the URL, if any.
Definition urlSpec.h:96
get_query
Returns the query specified by the URL, or empty string if no query is specified.
Definition urlSpec.h:100
get_path
Returns the path specified by the URL, or "/" if no path is specified.
Definition urlSpec.h:99
static std::string unquote_plus(const std::string &source)
Reverses the operation of quote_plus(): converts escaped characters of the form "%xx" to their ascii ...
Definition urlSpec.cxx:837
std::string get_port_str() const
Returns the port specified by the URL as a string, or the empty string if no port is specified.
Definition urlSpec.I:148
get_server_and_port
Returns a string consisting of the server name, followed by a colon, followed by the port number.
Definition urlSpec.h:98
bool has_port() const
Returns true if the URL specifies a port number, false otherwise.
Definition urlSpec.I:93
bool has_scheme() const
Returns true if the URL specifies a scheme (e.g.
Definition urlSpec.I:59
get_scheme
Returns the scheme specified by the URL, or empty string if no scheme is specified.
Definition urlSpec.h:93
static std::string unquote(const std::string &source)
Reverses the operation of quote(): converts escaped characters of the form "%xx" to their ascii equiv...
Definition urlSpec.cxx:801
void set_url(const std::string &url, bool server_name_expected=false)
Completely replaces the URL with the indicated string.
Definition urlSpec.cxx:554
get_port
Returns the port number specified by the URL, or the default port if not specified.
Definition urlSpec.h:97
set_authority
Replaces the authority part of the URL specification.
Definition urlSpec.h:94
static std::string quote_plus(const std::string &source, const std::string &safe="/")
Behaves like quote() with the additional behavior of replacing spaces with plus signs.
Definition urlSpec.cxx:758
bool has_server() const
Returns true if the URL specifies a server name, false otherwise.
Definition urlSpec.I:85
set_server_and_port
Replaces the server and port parts of the URL specification simultaneously.
Definition urlSpec.h:98
std::string get_path_and_query() const
Returns the path (or "/" if no path is specified), followed by the query if it is specified.
Definition urlSpec.cxx:228
set_username
Replaces the username part of the URL specification.
Definition urlSpec.h:95
int compare_to(const URLSpec &other) const
Returns a number less than zero if this URLSpec sorts before the other one, greater than zero if it s...
Definition urlSpec.cxx:77
static size_t add_hash(size_t start, const Key &key)
Adds the indicated key into a running hash.
static size_t add_hash(size_t start, const Key &key)
Adds the elements of the indicated key into a running hash.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
string downcase(const string &s)
Returns the input string with all uppercase letters converted to lowercase.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.