4 * HTTP ancestor class and common classes.
6 * Portable Windows Library
8 * Copyright (c) 1993-2002 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
27 * Revision 1.126 2007/06/29 02:47:28 rjongbloed
28 * Added PString::FindSpan() function (strspn equivalent) with slightly nicer semantics.
30 * Revision 1.125 2007/04/20 07:42:12 csoutheren
31 * Applied 1703668 - Make RTSP URLs accept the Query component
32 * Thanks to Fabrizio Ammollo
34 * Revision 1.124 2007/04/10 05:08:48 rjongbloed
35 * Fixed issue with use of static C string variables in DLL environment,
36 * must use functional interface for correct initialisation.
38 * Revision 1.123 2007/04/08 01:53:28 ykiryanov
39 * Build to support ptlib dll creation
41 * Revision 1.122 2006/08/29 01:33:25 csoutheren
42 * RFC 3261 specifies use of sips for secure SIP, so change URL parsing back...
44 * Revision 1.121 2006/08/21 05:23:13 csoutheren
45 * Add ssip and change h323s to sh323
47 * Revision 1.120 2006/08/21 01:31:03 csoutheren
48 * Add support h323s URLs
50 * Revision 1.119 2006/07/14 00:57:37 csoutheren
51 * Be safe for URLs containing non-ASCII characters
53 * Revision 1.118 2006/06/25 11:22:57 csoutheren
54 * Add pragmas to automate linking with VS 2005
56 * Revision 1.117 2006/06/21 03:28:44 csoutheren
57 * Various cleanups thanks for Frederic Heem
59 * Revision 1.116 2005/11/30 12:47:41 csoutheren
60 * Removed tabs, reformatted some code, and changed tags for Doxygen
62 * Revision 1.115 2005/04/20 05:19:48 csoutheren
63 * Patch 1185334. Ensure SIP URLs correctly store status of port
64 * Thanks to Ted Szoczei
66 * Revision 1.114 2005/01/16 20:36:48 csoutheren
67 * Changed URLS to put IP address in [] if contains a ":"
69 * Revision 1.113 2005/01/04 08:09:41 csoutheren
70 * Fixed Linux configure problems
72 * Revision 1.112 2004/12/08 00:51:12 csoutheren
73 * Move PURLLegacyScheme to header file to allow external usage
75 * Revision 1.111 2004/10/23 11:27:24 ykiryanov
76 * Added ifdef _WIN32_WCE for PocketPC 2003 SDK port
78 * Revision 1.110 2004/08/31 23:40:51 csoutheren
79 * Fixed problem with absolute file paths in URLs
81 * Revision 1.109 2004/07/14 13:15:45 rjongbloed
82 * Fixed minor bug where a URL is "non-empty" if requires a host but has none.
83 * eg could end up with "sip:" or "http://:80" which are illegal.
85 * Revision 1.108 2004/07/12 09:17:20 csoutheren
86 * Fixed warnings and errors under Linux
88 * Revision 1.107 2004/07/06 10:12:52 csoutheren
89 * Added static integer o factory template to assist in ensuring factories are instantiated
91 * Revision 1.106 2004/06/30 12:17:05 rjongbloed
92 * Rewrite of plug in system to use single global variable for all factories to avoid all sorts
93 * of issues with startup orders and Windows DLL multiple instances.
95 * Revision 1.105 2004/06/16 07:48:12 csoutheren
96 * Added assert to clarify usage of default scheme
98 * Revision 1.104 2004/06/03 13:30:58 csoutheren
99 * Renamed INSTANTIATE_FACTORY to avoid potential namespace collisions
100 * Added documentaton on new PINSTANTIATE_FACTORY macro
101 * Added generic form of PINSTANTIATE_FACTORY
103 * Revision 1.103 2004/06/03 12:47:58 csoutheren
104 * Decomposed PFactory declarations to hopefully avoid problems with Windows DLLs
106 * Revision 1.102 2004/06/01 07:28:45 csoutheren
107 * Changed URL parsing to use abstract factory code
109 * Revision 1.101 2004/04/04 00:21:47 csoutheren
110 * FIxed problem with some URL parsing
112 * Revision 1.100 2004/04/03 08:22:20 csoutheren
113 * Remove pseudo-RTTI and replaced with real RTTI
115 * Revision 1.99 2004/03/23 05:08:21 csoutheren
116 * Fixed problem with use of ShellExecuteEx function
118 * Revision 1.98 2004/03/13 06:30:52 rjongbloed
119 * Virtualised parse function.
121 * Revision 1.97 2004/02/24 11:14:44 rjongbloed
122 * Fixed correct initialisation of some internal variables in URL if parse fails.
124 * Revision 1.96 2004/01/17 17:44:17 csoutheren
125 * Changed to use PString::MakeEmpty
127 * Revision 1.95 2003/11/18 09:22:17 csoutheren
128 * Fixed problems with PURL::OpenBrowser, thanks to David Parr
130 * Revision 1.94 2003/08/27 03:37:45 dereksmithies
131 * Fix initialization of pathStr so it really is empty. BIG thanks to Diego Tartara.
133 * Revision 1.93 2003/07/22 03:26:10 csoutheren
134 * Fixed problem with parsing default H323 addresses
136 * Revision 1.92 2003/06/23 15:31:40 ykiryanov
137 * Slightly changed call to ShellExecuteEx to make compatible with Win32
139 * Revision 1.91 2003/06/23 14:31:33 ykiryanov
140 * Modified for WinCE - used ShellExecuteEx instead of ShellExecute
142 * Revision 1.90 2003/06/05 00:15:54 rjongbloed
143 * Fixed callto bug created by previous patch.
145 * Revision 1.89 2003/06/04 01:42:05 rjongbloed
146 * Fixed h323 scheme, does not have a "password" field.
148 * Revision 1.88 2003/06/02 02:46:45 rjongbloed
149 * Fixed issue with callto URL parsing incorrect username field.
150 * Added automatic removal of illegal (though common) "//" in callto URL.
152 * Revision 1.87 2003/05/05 07:30:17 craigs
153 * Fixed problem with URLs that do not specify schemes
155 * Revision 1.86 2003/05/02 13:50:23 craigs
156 * Fixed a problem with callto:localhost
158 * Revision 1.85 2003/05/02 13:20:33 craigs
159 * Fixed callto problems
161 * Revision 1.84 2003/04/28 04:41:22 robertj
162 * Changed URL parsing so if a default scheme is present then explicit scheme
163 * must be "known" to avoid ambiguity with host:port parsing.
165 * Revision 1.83 2003/04/10 00:13:56 robertj
166 * Fixed correct decoding of user/password/host/port field, for non h323 schemes.
168 * Revision 1.82 2003/04/08 06:28:14 craigs
169 * Fixed introduced problem with HTTP server mistaking relative URLs for proxy requests
171 * Revision 1.81 2003/04/04 08:03:55 robertj
172 * Fixed special case of h323 URL default port changing depending on
173 * if it the host is an endpoint or gatekeeper.
175 * Revision 1.80 2003/04/04 05:18:08 robertj
176 * Added "callto", "tel" and fixed "h323" URL types.
178 * Revision 1.79 2002/12/02 00:17:03 robertj
179 * Fixed URL parsing/display problems with non-path URL type eg mailto
181 * Revision 1.78 2002/11/22 06:16:49 robertj
182 * Fixed usage of URI (relative http/https URL).
184 * Revision 1.77 2002/11/20 02:10:56 robertj
185 * Fixed some more realtive/absolute path issues.
187 * Revision 1.76 2002/11/20 01:01:49 robertj
188 * Fixed GNU compatibility
190 * Revision 1.75 2002/11/20 00:49:37 robertj
191 * Fixed correct interpretation of url re double slashes as per latest RFC,
192 * including file: mapping and relative paths. Probably still more to do.
194 * Revision 1.74 2002/11/19 22:45:03 robertj
195 * Fixed support for file: scheme under unix
197 * Revision 1.73 2002/11/19 10:36:50 robertj
198 * Added functions to set anf get "file:" URL. as PFilePath and do the right
199 * things with platform dependent directory components.
201 * Revision 1.72 2002/11/06 22:47:25 robertj
202 * Fixed header comment (copyright etc)
204 * Revision 1.71 2002/09/23 07:17:24 robertj
205 * Changes to allow winsock2 to be included.
207 * Revision 1.70 2002/08/28 08:06:11 craigs
208 * Fixed problem (again) with file:// URLs
210 * Revision 1.69 2002/08/28 05:11:23 craigs
211 * Fixed problem with file:// URLs
213 * Revision 1.68 2002/05/02 05:11:29 craigs
214 * Fixed problem with not translating + chars in URL query parameters
216 * Revision 1.67 2002/03/19 23:39:57 robertj
217 * Fixed string output to include PathOnly variant, lost in previous mod.
219 * Revision 1.66 2002/03/19 23:24:08 robertj
220 * Fixed problems with backward compatibility on parameters processing.
222 * Revision 1.65 2002/03/18 05:02:27 robertj
223 * Added functions to set component parts of URL.
224 * Fixed output of parameters when more than one ';' involved.
226 * Revision 1.64 2001/11/09 05:46:14 robertj
227 * Removed double slash on sip URL.
228 * Fixed extra : if have username but no password.
231 * Revision 1.63 2001/11/08 00:32:49 robertj
232 * Added parsing of ';' based parameter fields into string dictionary if there are multiple parameters, with '=' values.
234 * Revision 1.62 2001/10/31 01:33:07 robertj
235 * Added extra const for constant HTTP tag name strings.
237 * Revision 1.61 2001/10/03 00:26:34 robertj
238 * Upgraded client to HTTP/1.1 and for chunked mode entity bodies.
240 * Revision 1.60 2001/09/28 00:45:42 robertj
241 * Broke out internal static function for unstranslating URL strings.
243 * Revision 1.59 2001/07/16 00:43:06 craigs
244 * Added ability to parse other transport URLs
246 * Revision 1.58 2000/05/02 08:29:07 craigs
247 * Removed "memory leaks" caused by brain-dead GNU linker
249 * Revision 1.57 1999/05/11 12:24:18 robertj
250 * Fixed URL parser so leading blanks are ignored.
252 * Revision 1.56 1999/05/04 15:26:01 robertj
253 * Improved HTTP/1.1 compatibility (pass through user commands).
254 * Fixed problems with quicktime installer.
256 * Revision 1.55 1999/04/21 01:56:13 robertj
257 * Fixed problem with escape codes greater that %80
259 * Revision 1.54 1999/01/16 12:45:54 robertj
260 * Added RTSP schemes to URL's
262 * Revision 1.53 1998/11/30 05:38:15 robertj
263 * Moved PURL::Open() code to .cxx file to avoid linking unused code.
265 * Revision 1.52 1998/11/30 04:51:53 robertj
266 * New directory structure
268 * Revision 1.51 1998/09/23 06:22:07 robertj
269 * Added open source copyright license.
271 * Revision 1.50 1998/02/03 10:02:34 robertj
272 * Added ability to get scheme, host and port from URL as a string.
274 * Revision 1.49 1998/02/03 06:27:26 robertj
275 * Fixed URL encoding to be closer to RFC
277 * Revision 1.48 1998/01/26 02:49:16 robertj
280 * Revision 1.47 1997/11/10 12:40:20 robertj
281 * Fixed illegal character set for URL's.
283 * Revision 1.46 1997/07/14 11:47:10 robertj
284 * Added "const" to numerous variables.
286 * Revision 1.45 1997/07/12 09:45:01 robertj
287 * Fixed bug when URL has + sign in somthing other than parameters.
289 * Revision 1.44 1997/06/06 08:54:47 robertj
290 * Allowed username/password on http scheme URL.
292 * Revision 1.43 1997/04/06 07:46:09 robertj
293 * Fixed bug where URL has more than special character ('?', '#' etc).
295 * Revision 1.42 1997/03/28 04:40:24 robertj
296 * Added tags for cookies.
298 * Revision 1.41 1997/03/18 22:03:44 robertj
299 * Fixed bug that incorrectly parses URL with double slashes.
301 * Revision 1.40 1997/02/14 13:55:44 robertj
302 * Fixed bug in URL for reproducing fields with special characters, must be escaped and weren't.
304 * Revision 1.39 1997/01/12 04:15:21 robertj
305 * Globalised MIME tag strings.
307 * Revision 1.38 1996/09/14 13:09:28 robertj
309 * rearranged sockets to help support IPX.
310 * added indirect channel class and moved all protocols to descend from it,
311 * separating the protocol from the low level byte transport.
313 * Revision 1.37 1996/08/25 09:37:41 robertj
314 * Added function to detect "local" host name.
315 * Fixed printing of trailing '/' in empty URL, is distinction between with and without.
317 * Revision 1.36 1996/08/22 13:22:26 robertj
318 * Fixed bug in print of URLs, extra @ signs.
320 * Revision 1.35 1996/08/19 13:42:40 robertj
321 * Fixed errors in URL parsing and display.
322 * Fixed "Forbidden" problem out of HTTP authorisation system.
323 * Fixed authorisation so if have no user/password on basic authentication, does not require it.
325 * Revision 1.34 1996/07/27 04:13:47 robertj
326 * Fixed use of HTTP proxy on non-persistent connections.
328 * Revision 1.33 1996/07/15 10:37:20 robertj
329 * Improved proxy "self" detection (especially localhost).
331 * Revision 1.32 1996/06/28 13:20:24 robertj
332 * Modified HTTPAuthority so gets PHTTPReqest (mainly for URL) passed in.
333 * Moved HTTP form resource to another compilation module.
334 * Fixed memory leak in POST command.
336 * Revision 1.31 1996/06/10 10:00:00 robertj
337 * Added global function for query parameters parsing.
339 * Revision 1.30 1996/06/07 13:52:23 robertj
340 * Added PUT to HTTP proxy FTP. Necessitating redisign of entity body processing.
342 * Revision 1.29 1996/06/05 12:33:04 robertj
343 * Fixed bug in parsing URL with no path, is NOT absolute!
345 * Revision 1.28 1996/05/30 10:07:26 robertj
346 * Fixed bug in version number checking of return code compatibility.
348 * Revision 1.27 1996/05/26 03:46:42 robertj
349 * Compatibility to GNU 2.7.x
351 * Revision 1.26 1996/05/23 10:02:13 robertj
352 * Added common function for GET and HEAD commands.
353 * Fixed status codes to be the actual status code instead of sequential enum.
354 * This fixed some problems with proxy pass through of status codes.
355 * Fixed bug in URL parsing of username and passwords.
357 * Revision 1.19.1.1 1996/04/17 11:08:22 craigs
358 * New version by craig pending confirmation by robert
360 * Revision 1.19 1996/04/05 01:46:30 robertj
361 * Assured PSocket::Write always writes the number of bytes specified, no longer need write loops.
362 * Added workaraound for NT Netscape Navigator bug with persistent connections.
364 * Revision 1.18 1996/03/31 09:05:07 robertj
367 * Revision 1.17 1996/03/17 05:48:07 robertj
368 * Fixed host name print out of URLs.
369 * Added hit count to PHTTPResource.
371 * Revision 1.16 1996/03/16 05:00:26 robertj
372 * Added ParseReponse() for splitting reponse line into code and info.
373 * Added client side support for HTTP socket.
374 * Added hooks for proxy support in HTTP socket.
375 * Added translation type to TranslateString() to accommodate query variables.
376 * Defaulted scheme field in URL to "http".
377 * Inhibited output of port field on string conversion of URL according to scheme.
379 * Revision 1.15 1996/03/11 10:29:50 robertj
380 * Fixed bug in help image HTML.
382 * Revision 1.14 1996/03/10 13:15:24 robertj
383 * Redesign to make resources thread safe.
385 * Revision 1.13 1996/03/02 03:27:37 robertj
386 * Added function to translate a string to a form suitable for inclusion in a URL.
387 * Added radio button and selection boxes to HTTP form resource.
388 * Fixed bug in URL parsing, losing first / if hostname specified.
390 * Revision 1.12 1996/02/25 11:14:24 robertj
391 * Radio button support for forms.
393 * Revision 1.11 1996/02/25 03:10:34 robertj
394 * Removed pass through HTTP resource.
395 * Fixed PHTTPConfig resource to use correct name for config key.
397 * Revision 1.10 1996/02/19 13:48:28 robertj
398 * Put multiple uses of literal strings into const variables.
399 * Fixed URL parsing so that the unmangling of strings occurs correctly.
400 * Moved nested classes from PHTTPForm.
401 * Added overwrite option to AddResource().
402 * Added get/set string to PHTTPString resource.
404 * Revision 1.9 1996/02/13 13:09:17 robertj
405 * Added extra parameters to callback function in PHTTPResources, required
406 * by descendants to make informed decisions on data being loaded.
408 * Revision 1.8 1996/02/08 12:26:29 robertj
409 * Redesign of resource callback mechanism.
410 * Added new resource types for HTML data entry forms.
412 * Revision 1.7 1996/02/03 11:33:19 robertj
413 * Changed RadCmd() so can distinguish between I/O error and unknown command.
415 * Revision 1.6 1996/02/03 11:11:49 robertj
416 * Numerous bug fixes.
417 * Added expiry date and ismodifiedsince support.
419 * Revision 1.5 1996/01/30 23:32:40 robertj
422 * Revision 1.4 1996/01/28 14:19:09 robertj
423 * Split HTML into separate source file.
424 * Beginning of pass through resource type.
425 * Changed PCharArray in OnLoadData to PString for convenience in mangling data.
426 * Made PHTTPSpace return standard page on selection of partial path.
428 * Revision 1.3 1996/01/28 02:49:16 robertj
429 * Further implementation.
431 * Revision 1.2 1996/01/26 02:24:30 robertj
432 * Further implemetation.
434 * Revision 1.1 1996/01/23 13:04:32 robertj
440 #pragma implementation "http.h"
441 #pragma implementation "url.h"
446 #define P_DISABLE_FACTORY_INSTANCES
447 #include <ptlib/pfactory.h>
448 #include <ptlib/sockets.h>
449 #include <ptclib/http.h>
450 #include <ptclib/url.h>
455 #include <shellapi.h>
456 #pragma comment(lib,"shell32.lib")
461 // http://host:port/path...
462 // https://host:port/path....
463 // gopher://host:port
466 // prospero://host:port
467 // ftp://user:password@host:port/path...
468 // telnet://user:password@host:port
469 // file://hostname/path...
471 // mailto:user@hostname
474 #define DEFAULT_FTP_PORT 21
475 #define DEFAULT_TELNET_PORT 23
476 #define DEFAULT_GOPHER_PORT 70
477 #define DEFAULT_HTTP_PORT 80
478 #define DEFAULT_NNTP_PORT 119
479 #define DEFAULT_WAIS_PORT 210
480 #define DEFAULT_HTTPS_PORT 443
481 #define DEFAULT_RTSP_PORT 554
482 #define DEFAULT_RTSPU_PORT 554
483 #define DEFAULT_PROSPERO_PORT 1525
484 #define DEFAULT_H323_PORT 1720
485 #define DEFAULT_H323S_PORT 1300
486 #define DEFAULT_H323RAS_PORT 1719
487 #define DEFAULT_SIP_PORT 5060
488 #define DEFAULT_SIPS_PORT 5061
490 #define DEFINE_LEGACY_URL_SCHEME(schemeName, user, pass, host, def, defhost, query, params, frags, path, rel, port) \
491 class PURLLegacyScheme_##schemeName : public PURLLegacyScheme \
494 PURLLegacyScheme_##schemeName() \
495 : PURLLegacyScheme(#schemeName ) \
497 hasUsername = user; \
498 hasPassword = pass; \
499 hasHostPort = host; \
500 defaultToUserIfNoAt = def; \
501 defaultHostToLocal = defhost; \
503 hasParameters = params; \
504 hasFragments = frags; \
506 relativeImpliesScheme = rel; \
507 defaultPort = port; \
510 static PFactory<PURLScheme>::Worker<PURLLegacyScheme_##schemeName> schemeName##Factory(#schemeName, true); \
512 DEFINE_LEGACY_URL_SCHEME(http, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, DEFAULT_HTTP_PORT )
513 DEFINE_LEGACY_URL_SCHEME(file
, FALSE
, FALSE
, TRUE
, FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, TRUE
, FALSE
, 0)
514 DEFINE_LEGACY_URL_SCHEME(https
, FALSE
, FALSE
, TRUE
, FALSE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, TRUE
, DEFAULT_HTTPS_PORT
)
515 DEFINE_LEGACY_URL_SCHEME(gopher
, FALSE
, FALSE
, TRUE
, FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, TRUE
, FALSE
, DEFAULT_GOPHER_PORT
)
516 DEFINE_LEGACY_URL_SCHEME(wais
, FALSE
, FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, TRUE
, FALSE
, DEFAULT_WAIS_PORT
)
517 DEFINE_LEGACY_URL_SCHEME(nntp
, FALSE
, FALSE
, TRUE
, FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, TRUE
, FALSE
, DEFAULT_NNTP_PORT
)
518 DEFINE_LEGACY_URL_SCHEME(prospero
, FALSE
, FALSE
, TRUE
, FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, TRUE
, FALSE
, DEFAULT_PROSPERO_PORT
)
519 DEFINE_LEGACY_URL_SCHEME(rtsp
, FALSE
, FALSE
, TRUE
, FALSE
, TRUE
, TRUE
, FALSE
, FALSE
, TRUE
, FALSE
, DEFAULT_RTSP_PORT
)
520 DEFINE_LEGACY_URL_SCHEME(rtspu
, FALSE
, FALSE
, TRUE
, FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, TRUE
, FALSE
, DEFAULT_RTSPU_PORT
)
521 DEFINE_LEGACY_URL_SCHEME(ftp
, TRUE
, TRUE
, TRUE
, FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, TRUE
, FALSE
, DEFAULT_FTP_PORT
)
522 DEFINE_LEGACY_URL_SCHEME(telnet
, TRUE
, TRUE
, TRUE
, FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, DEFAULT_TELNET_PORT
)
523 DEFINE_LEGACY_URL_SCHEME(mailto
, FALSE
, FALSE
, FALSE
, FALSE
, TRUE
, TRUE
, FALSE
, FALSE
, FALSE
, FALSE
, 0)
524 DEFINE_LEGACY_URL_SCHEME(news
, FALSE
, FALSE
, FALSE
, FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, FALSE
, FALSE
, 0)
525 DEFINE_LEGACY_URL_SCHEME(h323
, TRUE
, FALSE
, TRUE
, TRUE
, FALSE
, FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, DEFAULT_H323_PORT
)
526 DEFINE_LEGACY_URL_SCHEME(h323s
, TRUE
, FALSE
, TRUE
, TRUE
, FALSE
, FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, DEFAULT_H323S_PORT
)
527 DEFINE_LEGACY_URL_SCHEME(sip
, TRUE
, TRUE
, TRUE
, FALSE
, FALSE
, FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, DEFAULT_SIP_PORT
)
528 DEFINE_LEGACY_URL_SCHEME(sips
, TRUE
, TRUE
, TRUE
, FALSE
, FALSE
, FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, DEFAULT_SIPS_PORT
)
529 DEFINE_LEGACY_URL_SCHEME(tel
, FALSE
, FALSE
, FALSE
, TRUE
, FALSE
, FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, 0)
530 DEFINE_LEGACY_URL_SCHEME(fax
, FALSE
, FALSE
, FALSE
, TRUE
, FALSE
, FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, 0)
531 DEFINE_LEGACY_URL_SCHEME(callto
, FALSE
, FALSE
, FALSE
, TRUE
, FALSE
, FALSE
, TRUE
, FALSE
, FALSE
, FALSE
, 0)
533 PINSTANTIATE_FACTORY(PURLScheme
, PString
)
535 #define DEFAULT_SCHEME "http"
536 #define FILE_SCHEME "file"
538 //////////////////////////////////////////////////////////////////////////////
542 : //scheme(SchemeTable[DEFAULT_SCHEME].name),
543 scheme(DEFAULT_SCHEME
),
545 portSupplied (FALSE
),
551 PURL::PURL(const char * str
, const char * defaultScheme
)
553 Parse(str
, defaultScheme
);
557 PURL::PURL(const PString
& str
, const char * defaultScheme
)
559 Parse(str
, defaultScheme
);
563 PURL::PURL(const PFilePath
& filePath
)
564 : //scheme(SchemeTable[FILE_SCHEME].name),
567 portSupplied (FALSE
),
570 PStringArray pathArray
= filePath
.GetDirectory().GetPath();
571 hostname
= pathArray
[0];
574 for (i
= 1; i
< pathArray
.GetSize(); i
++)
575 pathArray
[i
-1] = pathArray
[i
];
576 pathArray
[i
-1] = filePath
.GetFileName();
582 PObject::Comparison
PURL::Compare(const PObject
& obj
) const
584 PAssert(PIsDescendant(&obj
, PURL
), PInvalidCast
);
585 return urlString
.Compare(((const PURL
&)obj
).urlString
);
589 PINDEX
PURL::HashFunction() const
591 return urlString
.HashFunction();
595 void PURL::PrintOn(ostream
& stream
) const
601 void PURL::ReadFrom(istream
& stream
)
609 PString
PURL::TranslateString(const PString
& str
, TranslationType type
)
613 PString safeChars
= "abcdefghijklmnopqrstuvwxyz"
614 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
615 "0123456789$-_.!*'(),";
617 case LoginTranslation
:
618 safeChars
+= "+;?&=";
621 case PathTranslation
:
622 safeChars
+= "+:@&=";
625 case QueryTranslation
:
628 PINDEX pos
= (PINDEX
)-1;
629 while ((pos
= xlat
.FindSpan(safeChars
, pos
+1)) != P_MAX_INDEX
)
630 xlat
.Splice(psprintf("%%%02X", (BYTE
)xlat
[pos
]), pos
, 1);
632 if (type
== QueryTranslation
) {
633 PINDEX space
= (PINDEX
)-1;
634 while ((space
= xlat
.Find(' ', space
+1)) != P_MAX_INDEX
)
642 PString
PURL::UntranslateString(const PString
& str
, TranslationType type
)
648 if (type
== PURL::QueryTranslation
) {
650 while ((pos
= xlat
.Find('+', pos
+1)) != P_MAX_INDEX
)
655 while ((pos
= xlat
.Find('%', pos
+1)) != P_MAX_INDEX
) {
656 int digit1
= xlat
[pos
+1];
657 int digit2
= xlat
[pos
+2];
658 if (isxdigit(digit1
) && isxdigit(digit2
)) {
660 (isdigit(digit2
) ? (digit2
-'0') : (toupper(digit2
)-'A'+10)) +
661 ((isdigit(digit1
) ? (digit1
-'0') : (toupper(digit1
)-'A'+10)) << 4));
662 xlat
.Delete(pos
+1, 2);
670 static void SplitVars(const PString
& str
, PStringToString
& vars
, char sep1
, char sep2
)
674 PINDEX sep1next
= str
.Find(sep1
, sep1prev
);
675 if (sep1next
== P_MAX_INDEX
)
676 sep1next
--; // Implicit assumption string is not a couple of gigabytes long ...
678 PINDEX sep2pos
= str
.Find(sep2
, sep1prev
);
679 if (sep2pos
> sep1next
)
682 PCaselessString key
= PURL::UntranslateString(str(sep1prev
, sep2pos
-1), PURL::QueryTranslation
);
684 PString data
= PURL::UntranslateString(str(sep2pos
+1, sep1next
-1), PURL::QueryTranslation
);
686 if (vars
.Contains(key
))
687 vars
.SetAt(key
, vars
[key
] + ',' + data
);
689 vars
.SetAt(key
, data
);
692 sep1prev
= sep1next
+1;
693 } while (sep1prev
!= P_MAX_INDEX
);
697 void PURL::SplitQueryVars(const PString
& queryStr
, PStringToString
& queryVars
)
699 SplitVars(queryStr
, queryVars
, '&', '=');
703 BOOL
PURL::InternalParse(const char * cstr
, const char * defaultScheme
)
708 username
.MakeEmpty();
709 password
.MakeEmpty();
710 hostname
.MakeEmpty();
712 portSupplied
= FALSE
;
713 relativePath
= FALSE
;
716 paramVars
.RemoveAll();
717 fragment
.MakeEmpty();
718 queryVars
.RemoveAll();
720 // copy the string so we can take bits off it
721 while (((*cstr
& 0x80) == 0x00) && isspace(*cstr
))
725 // Character set as per RFC2396
727 while ( ((*cstr
& 0x80) != 0x00) || isalnum(url
[pos
]) || url
[pos
] == '+' || url
[pos
] == '-' || url
[pos
] == '.')
732 // get information which tells us how to parse URL for this
734 PURLScheme
* schemeInfo
= NULL
;
736 // Determine if the URL has an explicit scheme
737 if (url
[pos
] == ':') {
739 // get the scheme information, or get the default scheme
740 schemeInfo
= PFactory
<PURLScheme
>::CreateInstance(url
.Left(pos
));
741 if (schemeInfo
== NULL
&& defaultScheme
== NULL
) {
742 PFactory
<PURLScheme
>::KeyList_T keyList
= PFactory
<PURLScheme
>::GetKeyList();
743 if (keyList
.size() != 0)
744 schemeInfo
= PFactory
<PURLScheme
>::CreateInstance(keyList
[0]);
746 if (schemeInfo
!= NULL
)
747 url
.Delete(0, pos
+1);
750 // if we could not match a scheme, then use the specified default scheme
751 if (schemeInfo
== NULL
&& defaultScheme
!= NULL
)
752 schemeInfo
= PFactory
<PURLScheme
>::CreateInstance(defaultScheme
);
754 // if that still fails, then use the global default scheme
755 if (schemeInfo
== NULL
)
756 schemeInfo
= PFactory
<PURLScheme
>::CreateInstance(DEFAULT_SCHEME
);
758 // if that fails, then there is nowehere to go
759 PAssert(schemeInfo
!= NULL
, "Default scheme not available");
760 scheme
= schemeInfo
->GetName();
761 if (!schemeInfo
->Parse(url
, *this))
767 BOOL
PURL::LegacyParse(const PString
& _url
, const PURLLegacyScheme
* schemeInfo
)
772 // Super special case!
773 if (scheme
*= "callto") {
775 // Actually not part of MS spec, but a lot of people put in the // into
776 // the URL, so we take it out of it is there.
777 if (url
.GetLength() > 2 && url
[0] == '/' && url
[1] == '/')
780 // For some bizarre reason callto uses + instead of ; for paramters
781 // We do a loop so that phone numbers of the form +61243654666 still work
784 } while (pos
!= P_MAX_INDEX
&& isdigit(url
[pos
+1]));
786 if (pos
!= P_MAX_INDEX
) {
787 SplitVars(url(pos
+1, P_MAX_INDEX
), paramVars
, '+', '=');
788 url
.Delete(pos
, P_MAX_INDEX
);
791 hostname
= paramVars("gateway");
793 username
= UntranslateString(url
, LoginTranslation
);
795 PCaselessString type
= paramVars("type");
796 if (type
== "directory") {
798 if (pos
== P_MAX_INDEX
)
799 username
= UntranslateString(url
, LoginTranslation
);
801 hostname
= UntranslateString(url
.Left(pos
), LoginTranslation
);
802 username
= UntranslateString(url
.Mid(pos
+1), LoginTranslation
);
806 // Now look for an @ and split user and host
808 if (pos
!= P_MAX_INDEX
) {
809 username
= UntranslateString(url
.Left(pos
), LoginTranslation
);
810 hostname
= UntranslateString(url
.Mid(pos
+1), LoginTranslation
);
813 if (type
== "ip" || type
== "host")
814 hostname
= UntranslateString(url
, LoginTranslation
);
816 username
= UntranslateString(url
, LoginTranslation
);
821 // Allow for [ipv6] form
822 pos
= hostname
.Find(']');
823 if (pos
== P_MAX_INDEX
)
825 pos
= hostname
.Find(':', pos
);
826 if (pos
!= P_MAX_INDEX
) {
827 port
= (WORD
)hostname
.Mid(pos
+1).AsUnsigned();
829 hostname
.Delete(pos
, P_MAX_INDEX
);
832 password
= paramVars("password");
836 // if the URL should have leading slash, then remove it if it has one
837 if (schemeInfo
!= NULL
&& schemeInfo
->hasHostPort
&& schemeInfo
->hasPath
) {
838 if (url
.GetLength() > 2 && url
[0] == '/' && url
[1] == '/')
844 // parse user/password/host/port
845 if (!relativePath
&& schemeInfo
->hasHostPort
) {
846 PString endHostChars
;
847 if (schemeInfo
->hasPath
)
849 if (schemeInfo
->hasQuery
)
851 if (schemeInfo
->hasParameters
)
853 if (schemeInfo
->hasFragments
)
855 if (endHostChars
.IsEmpty())
858 pos
= url
.FindOneOf(endHostChars
);
860 PString uphp
= url
.Left(pos
);
861 if (pos
!= P_MAX_INDEX
)
866 // if the URL is of type UserPasswordHostPort, then parse it
867 if (schemeInfo
->hasUsername
) {
868 // extract username and password
869 PINDEX pos2
= uphp
.Find('@');
870 PINDEX pos3
= P_MAX_INDEX
;
871 if (schemeInfo
->hasPassword
)
872 pos3
= uphp
.Find(':');
879 if (schemeInfo
->defaultToUserIfNoAt
) {
880 if (pos3
== P_MAX_INDEX
)
881 username
= UntranslateString(uphp
, LoginTranslation
);
883 username
= UntranslateString(uphp
.Left(pos3
), LoginTranslation
);
884 password
= UntranslateString(uphp
.Mid(pos3
+1), LoginTranslation
);
892 username
= UntranslateString(uphp
.Left(pos2
), LoginTranslation
);
894 username
= UntranslateString(uphp
.Left(pos3
), LoginTranslation
);
895 password
= UntranslateString(uphp(pos3
+1, pos2
-1), LoginTranslation
);
897 uphp
.Delete(0, pos2
+1);
901 // if the URL does not have a port, then this is the hostname
902 if (schemeInfo
->defaultPort
== 0)
903 hostname
= UntranslateString(uphp
, LoginTranslation
);
905 // determine if the URL has a port number
906 // Allow for [ipv6] form
907 pos
= uphp
.Find(']');
908 if (pos
== P_MAX_INDEX
)
910 pos
= uphp
.Find(':', pos
);
911 if (pos
== P_MAX_INDEX
)
912 hostname
= UntranslateString(uphp
, LoginTranslation
);
914 hostname
= UntranslateString(uphp
.Left(pos
), LoginTranslation
);
915 port
= (WORD
)uphp
.Mid(pos
+1).AsUnsigned();
919 if (hostname
.IsEmpty() && schemeInfo
->defaultHostToLocal
)
920 hostname
= PIPSocket::GetHostName();
924 if (schemeInfo
->hasQuery
) {
925 // chop off any trailing query
927 if (pos
!= P_MAX_INDEX
) {
928 SplitQueryVars(url(pos
+1, P_MAX_INDEX
), queryVars
);
929 url
.Delete(pos
, P_MAX_INDEX
);
933 if (schemeInfo
->hasParameters
) {
934 // chop off any trailing parameters
936 if (pos
!= P_MAX_INDEX
) {
937 SplitVars(url(pos
+1, P_MAX_INDEX
), paramVars
, ';', '=');
938 url
.Delete(pos
, P_MAX_INDEX
);
942 if (schemeInfo
->hasFragments
) {
943 // chop off any trailing fragment
945 if (pos
!= P_MAX_INDEX
) {
946 fragment
= UntranslateString(url(pos
+1, P_MAX_INDEX
), PathTranslation
);
947 url
.Delete(pos
, P_MAX_INDEX
);
951 if (schemeInfo
->hasPath
)
952 SetPathStr(url
); // the hierarchy is what is left
954 // if the rest of the URL isn't a path, then we are finished!
955 pathStr
= UntranslateString(url
, PathTranslation
);
959 if (port
== 0 && schemeInfo
->defaultPort
!= 0 && !relativePath
) {
960 // Yes another horrible, horrible special case!
961 if (scheme
== "h323" && paramVars("type") == "gk")
962 port
= DEFAULT_H323RAS_PORT
;
964 port
= schemeInfo
->defaultPort
;
972 PFilePath
PURL::AsFilePath() const
974 //if (scheme != SchemeTable[FILE_SCHEME].name)
975 // return PString::Empty();
976 if (scheme
!= FILE_SCHEME
)
977 return PString::Empty();
982 for (PINDEX i
= 0; i
< path
.GetSize(); i
++) {
984 str
<< PDIR_SEPARATOR
;
989 if (hostname
!= "localhost")
990 str
<< PDIR_SEPARATOR
<< hostname
;
991 for (PINDEX i
= 0; i
< path
.GetSize(); i
++)
992 str
<< PDIR_SEPARATOR
<< path
[i
];
999 PString
PURL::AsString(UrlFormat fmt
) const
1004 if (scheme
.IsEmpty())
1005 return PString::Empty();
1007 //const schemeStruct * schemeInfo = GetSchemeInfo(scheme);
1008 //if (schemeInfo == NULL)
1009 // schemeInfo = &SchemeTable[PARRAYSIZE(SchemeTable)-1];
1010 const PURLScheme
* schemeInfo
= PFactory
<PURLScheme
>::CreateInstance(scheme
);
1011 if (schemeInfo
== NULL
)
1012 schemeInfo
= PFactory
<PURLScheme
>::CreateInstance(DEFAULT_SCHEME
);
1014 return schemeInfo
->AsString(fmt
, *this);
1017 PString
PURL::LegacyAsString(PURL::UrlFormat fmt
, const PURLLegacyScheme
* schemeInfo
) const
1022 if (fmt
== HostPortOnly
) {
1023 if (schemeInfo
->hasHostPort
&& hostname
.IsEmpty())
1026 str
<< scheme
<< ':';
1029 if (schemeInfo
->relativeImpliesScheme
)
1030 return PString::Empty();
1034 if (schemeInfo
->hasPath
&& schemeInfo
->hasHostPort
)
1037 if (schemeInfo
->hasUsername
) {
1039 str
<< TranslateString(username
, LoginTranslation
);
1040 if (schemeInfo
->hasPassword
&& !password
)
1041 str
<< ':' << TranslateString(password
, LoginTranslation
);
1046 if (schemeInfo
->hasHostPort
) {
1047 if (hostname
.Find(':') != P_MAX_INDEX
)
1048 str
<< '[' << hostname
<< ']';
1053 if (schemeInfo
->defaultPort
!= 0) {
1054 if (port
!= schemeInfo
->defaultPort
|| portSupplied
)
1061 // URIOnly and PathOnly
1062 if (schemeInfo
->hasPath
) {
1063 for (i
= 0; i
< path
.GetSize(); i
++) {
1064 if (i
> 0 || !relativePath
)
1066 str
<< TranslateString(path
[i
], PathTranslation
);
1070 str
<< TranslateString(pathStr
, PathTranslation
);
1072 if (fmt
== URIOnly
) {
1074 str
<< "#" << TranslateString(fragment
, PathTranslation
);
1076 for (i
= 0; i
< paramVars
.GetSize(); i
++) {
1077 str
<< ';' << TranslateString(paramVars
.GetKeyAt(i
), QueryTranslation
);
1078 PString data
= paramVars
.GetDataAt(i
);
1080 str
<< '=' << TranslateString(data
, QueryTranslation
);
1083 if (!queryVars
.IsEmpty())
1084 str
<< '?' << GetQuery();
1091 void PURL::SetScheme(const PString
& s
)
1098 void PURL::SetUserName(const PString
& u
)
1105 void PURL::SetPassword(const PString
& p
)
1112 void PURL::SetHostName(const PString
& h
)
1119 void PURL::SetPort(WORD newPort
)
1126 void PURL::SetPathStr(const PString
& p
)
1130 path
= pathStr
.Tokenise("/", TRUE
);
1132 if (path
.GetSize() > 0 && path
[0].IsEmpty())
1135 for (PINDEX i
= 0; i
< path
.GetSize(); i
++) {
1136 path
[i
] = UntranslateString(path
[i
], PathTranslation
);
1137 if (i
> 0 && path
[i
] == ".." && path
[i
-1] != "..") {
1147 void PURL::SetPath(const PStringArray
& p
)
1151 pathStr
.MakeEmpty();
1152 for (PINDEX i
= 0; i
< path
.GetSize(); i
++)
1153 pathStr
+= '/' + path
[i
];
1159 PString
PURL::GetParameters() const
1163 for (PINDEX i
= 0; i
< paramVars
.GetSize(); i
++) {
1166 str
<< paramVars
.GetKeyAt(i
);
1167 PString data
= paramVars
.GetDataAt(i
);
1176 void PURL::SetParameters(const PString
& parameters
)
1178 SplitVars(parameters
, paramVars
, ';', '=');
1183 void PURL::SetParamVars(const PStringToString
& p
)
1190 void PURL::SetParamVar(const PString
& key
, const PString
& data
)
1193 paramVars
.RemoveAt(key
);
1195 paramVars
.SetAt(key
, data
);
1200 PString
PURL::GetQuery() const
1204 for (PINDEX i
= 0; i
< queryVars
.GetSize(); i
++) {
1207 str
<< TranslateString(queryVars
.GetKeyAt(i
), QueryTranslation
)
1209 << TranslateString(queryVars
.GetDataAt(i
), QueryTranslation
);
1216 void PURL::SetQuery(const PString
& queryStr
)
1218 SplitQueryVars(queryStr
, queryVars
);
1223 void PURL::SetQueryVars(const PStringToString
& q
)
1230 void PURL::SetQueryVar(const PString
& key
, const PString
& data
)
1233 queryVars
.RemoveAt(key
);
1235 queryVars
.SetAt(key
, data
);
1240 BOOL
PURL::OpenBrowser(const PString
& url
)
1243 SHELLEXECUTEINFO sei
;
1244 ZeroMemory(&sei
, sizeof(SHELLEXECUTEINFO
));
1245 sei
.cbSize
= sizeof(SHELLEXECUTEINFO
);
1246 sei
.lpVerb
= TEXT("open");
1250 sei
.lpFile
= url
.AsUCS2();
1251 #endif // _WIN32_WCE
1253 if (ShellExecuteEx(&sei
) != 0)
1257 MessageBox(NULL
, "Unable to open page"&url
, PProcess::Current().GetName(), MB_TASKMODAL
);
1259 MessageBox(NULL
, _T("Unable to open page"), PProcess::Current().GetName().AsUCS2(), MB_APPLMODAL
);
1260 #endif // _WIN32_WCE
1267 void PURL::Recalculate()
1269 //if (scheme.IsEmpty())
1270 // scheme = SchemeTable[DEFAULT_SCHEME].name;
1271 if (scheme
.IsEmpty())
1272 scheme
= DEFAULT_SCHEME
;
1274 urlString
= AsString(HostPortOnly
) + AsString(URIOnly
);
1278 //////////////////////////////////////////////////////////////////////////////
1281 static char const * const HTTPCommands
[PHTTP::NumCommands
] = {
1282 // HTTP 1.0 commands
1283 "GET", "HEAD", "POST",
1285 // HTTP 1.1 commands
1286 "PUT", "DELETE", "TRACE", "OPTIONS",
1293 const PString
& PHTTP::AllowTag () { static PString s
= "Allow"; return s
; }
1294 const PString
& PHTTP::AuthorizationTag () { static PString s
= "Authorization"; return s
; }
1295 const PString
& PHTTP::ContentEncodingTag () { static PString s
= "Content-Encoding"; return s
; }
1296 const PString
& PHTTP::ContentLengthTag () { static PString s
= "Content-Length"; return s
; }
1297 const PString
& PHTTP::ContentTypeTag () { static PString s
= "Content-Type"; return s
; }
1298 const PString
& PHTTP::DateTag () { static PString s
= "Date"; return s
; }
1299 const PString
& PHTTP::ExpiresTag () { static PString s
= "Expires"; return s
; }
1300 const PString
& PHTTP::FromTag () { static PString s
= "From"; return s
; }
1301 const PString
& PHTTP::IfModifiedSinceTag () { static PString s
= "If-Modified-Since"; return s
; }
1302 const PString
& PHTTP::LastModifiedTag () { static PString s
= "Last-Modified"; return s
; }
1303 const PString
& PHTTP::LocationTag () { static PString s
= "Location"; return s
; }
1304 const PString
& PHTTP::PragmaTag () { static PString s
= "Pragma"; return s
; }
1305 const PString
& PHTTP::PragmaNoCacheTag () { static PString s
= "no-cache"; return s
; }
1306 const PString
& PHTTP::RefererTag () { static PString s
= "Referer"; return s
; }
1307 const PString
& PHTTP::ServerTag () { static PString s
= "Server"; return s
; }
1308 const PString
& PHTTP::UserAgentTag () { static PString s
= "User-Agent"; return s
; }
1309 const PString
& PHTTP::WWWAuthenticateTag () { static PString s
= "WWW-Authenticate"; return s
; }
1310 const PString
& PHTTP::MIMEVersionTag () { static PString s
= "MIME-Version"; return s
; }
1311 const PString
& PHTTP::ConnectionTag () { static PString s
= "Connection"; return s
; }
1312 const PString
& PHTTP::KeepAliveTag () { static PString s
= "Keep-Alive"; return s
; }
1313 const PString
& PHTTP::TransferEncodingTag() { static PString s
= "Transfer-Encoding"; return s
; }
1314 const PString
& PHTTP::ChunkedTag () { static PString s
= "chunked"; return s
; }
1315 const PString
& PHTTP::ProxyConnectionTag () { static PString s
= "Proxy-Connection"; return s
; }
1316 const PString
& PHTTP::ProxyAuthorizationTag(){ static PString s
= "Proxy-Authorization"; return s
; }
1317 const PString
& PHTTP::ProxyAuthenticateTag(){ static PString s
= "Proxy-Authenticate"; return s
; }
1318 const PString
& PHTTP::ForwardedTag () { static PString s
= "Forwarded"; return s
; }
1319 const PString
& PHTTP::SetCookieTag () { static PString s
= "Set-Cookie"; return s
; }
1320 const PString
& PHTTP::CookieTag () { static PString s
= "Cookie"; return s
; }
1325 : PInternetProtocol("www 80", NumCommands
, HTTPCommands
)
1330 PINDEX
PHTTP::ParseResponse(const PString
& line
)
1332 PINDEX endVer
= line
.Find(' ');
1333 if (endVer
== P_MAX_INDEX
) {
1334 lastResponseInfo
= "Bad response";
1335 lastResponseCode
= PHTTP::InternalServerError
;
1339 lastResponseInfo
= line
.Left(endVer
);
1340 PINDEX endCode
= line
.Find(' ', endVer
+1);
1341 lastResponseCode
= line(endVer
+1,endCode
-1).AsInteger();
1342 if (lastResponseCode
== 0)
1343 lastResponseCode
= PHTTP::InternalServerError
;
1344 lastResponseInfo
&= line
.Mid(endCode
);
1349 // End Of File ///////////////////////////////////////////////////////////////