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