Fixed incorrect usage of result (now object rather than scalar), thanks Michal Zygmun...
[pwlib.git] / src / ptclib / http.cxx
blob19618af5999e5af22153699a0fe915874c259190
1 /*
2 * http.cxx
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
18 * under the License.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
26 * $Log$
27 * Revision 1.105 2004/06/16 07:48:12 csoutheren
28 * Added assert to clarify usage of default scheme
30 * Revision 1.104 2004/06/03 13:30:58 csoutheren
31 * Renamed INSTANTIATE_FACTORY to avoid potential namespace collisions
32 * Added documentaton on new PINSTANTIATE_FACTORY macro
33 * Added generic form of PINSTANTIATE_FACTORY
35 * Revision 1.103 2004/06/03 12:47:58 csoutheren
36 * Decomposed PFactory declarations to hopefully avoid problems with Windows DLLs
38 * Revision 1.102 2004/06/01 07:28:45 csoutheren
39 * Changed URL parsing to use abstract factory code
41 * Revision 1.101 2004/04/04 00:21:47 csoutheren
42 * FIxed problem with some URL parsing
44 * Revision 1.100 2004/04/03 08:22:20 csoutheren
45 * Remove pseudo-RTTI and replaced with real RTTI
47 * Revision 1.99 2004/03/23 05:08:21 csoutheren
48 * Fixed problem with use of ShellExecuteEx function
50 * Revision 1.98 2004/03/13 06:30:52 rjongbloed
51 * Virtualised parse function.
53 * Revision 1.97 2004/02/24 11:14:44 rjongbloed
54 * Fixed correct initialisation of some internal variables in URL if parse fails.
56 * Revision 1.96 2004/01/17 17:44:17 csoutheren
57 * Changed to use PString::MakeEmpty
59 * Revision 1.95 2003/11/18 09:22:17 csoutheren
60 * Fixed problems with PURL::OpenBrowser, thanks to David Parr
62 * Revision 1.94 2003/08/27 03:37:45 dereksmithies
63 * Fix initialization of pathStr so it really is empty. BIG thanks to Diego Tartara.
65 * Revision 1.93 2003/07/22 03:26:10 csoutheren
66 * Fixed problem with parsing default H323 addresses
68 * Revision 1.92 2003/06/23 15:31:40 ykiryanov
69 * Slightly changed call to ShellExecuteEx to make compatible with Win32
71 * Revision 1.91 2003/06/23 14:31:33 ykiryanov
72 * Modified for WinCE - used ShellExecuteEx instead of ShellExecute
74 * Revision 1.90 2003/06/05 00:15:54 rjongbloed
75 * Fixed callto bug created by previous patch.
77 * Revision 1.89 2003/06/04 01:42:05 rjongbloed
78 * Fixed h323 scheme, does not have a "password" field.
80 * Revision 1.88 2003/06/02 02:46:45 rjongbloed
81 * Fixed issue with callto URL parsing incorrect username field.
82 * Added automatic removal of illegal (though common) "//" in callto URL.
84 * Revision 1.87 2003/05/05 07:30:17 craigs
85 * Fixed problem with URLs that do not specify schemes
87 * Revision 1.86 2003/05/02 13:50:23 craigs
88 * Fixed a problem with callto:localhost
90 * Revision 1.85 2003/05/02 13:20:33 craigs
91 * Fixed callto problems
93 * Revision 1.84 2003/04/28 04:41:22 robertj
94 * Changed URL parsing so if a default scheme is present then explicit scheme
95 * must be "known" to avoid ambiguity with host:port parsing.
97 * Revision 1.83 2003/04/10 00:13:56 robertj
98 * Fixed correct decoding of user/password/host/port field, for non h323 schemes.
100 * Revision 1.82 2003/04/08 06:28:14 craigs
101 * Fixed introduced problem with HTTP server mistaking relative URLs for proxy requests
103 * Revision 1.81 2003/04/04 08:03:55 robertj
104 * Fixed special case of h323 URL default port changing depending on
105 * if it the host is an endpoint or gatekeeper.
107 * Revision 1.80 2003/04/04 05:18:08 robertj
108 * Added "callto", "tel" and fixed "h323" URL types.
110 * Revision 1.79 2002/12/02 00:17:03 robertj
111 * Fixed URL parsing/display problems with non-path URL type eg mailto
113 * Revision 1.78 2002/11/22 06:16:49 robertj
114 * Fixed usage of URI (relative http/https URL).
116 * Revision 1.77 2002/11/20 02:10:56 robertj
117 * Fixed some more realtive/absolute path issues.
119 * Revision 1.76 2002/11/20 01:01:49 robertj
120 * Fixed GNU compatibility
122 * Revision 1.75 2002/11/20 00:49:37 robertj
123 * Fixed correct interpretation of url re double slashes as per latest RFC,
124 * including file: mapping and relative paths. Probably still more to do.
126 * Revision 1.74 2002/11/19 22:45:03 robertj
127 * Fixed support for file: scheme under unix
129 * Revision 1.73 2002/11/19 10:36:50 robertj
130 * Added functions to set anf get "file:" URL. as PFilePath and do the right
131 * things with platform dependent directory components.
133 * Revision 1.72 2002/11/06 22:47:25 robertj
134 * Fixed header comment (copyright etc)
136 * Revision 1.71 2002/09/23 07:17:24 robertj
137 * Changes to allow winsock2 to be included.
139 * Revision 1.70 2002/08/28 08:06:11 craigs
140 * Fixed problem (again) with file:// URLs
142 * Revision 1.69 2002/08/28 05:11:23 craigs
143 * Fixed problem with file:// URLs
145 * Revision 1.68 2002/05/02 05:11:29 craigs
146 * Fixed problem with not translating + chars in URL query parameters
148 * Revision 1.67 2002/03/19 23:39:57 robertj
149 * Fixed string output to include PathOnly variant, lost in previous mod.
151 * Revision 1.66 2002/03/19 23:24:08 robertj
152 * Fixed problems with backward compatibility on parameters processing.
154 * Revision 1.65 2002/03/18 05:02:27 robertj
155 * Added functions to set component parts of URL.
156 * Fixed output of parameters when more than one ';' involved.
158 * Revision 1.64 2001/11/09 05:46:14 robertj
159 * Removed double slash on sip URL.
160 * Fixed extra : if have username but no password.
161 * Added h323: scheme
163 * Revision 1.63 2001/11/08 00:32:49 robertj
164 * Added parsing of ';' based parameter fields into string dictionary if there are multiple parameters, with '=' values.
166 * Revision 1.62 2001/10/31 01:33:07 robertj
167 * Added extra const for constant HTTP tag name strings.
169 * Revision 1.61 2001/10/03 00:26:34 robertj
170 * Upgraded client to HTTP/1.1 and for chunked mode entity bodies.
172 * Revision 1.60 2001/09/28 00:45:42 robertj
173 * Broke out internal static function for unstranslating URL strings.
175 * Revision 1.59 2001/07/16 00:43:06 craigs
176 * Added ability to parse other transport URLs
178 * Revision 1.58 2000/05/02 08:29:07 craigs
179 * Removed "memory leaks" caused by brain-dead GNU linker
181 * Revision 1.57 1999/05/11 12:24:18 robertj
182 * Fixed URL parser so leading blanks are ignored.
184 * Revision 1.56 1999/05/04 15:26:01 robertj
185 * Improved HTTP/1.1 compatibility (pass through user commands).
186 * Fixed problems with quicktime installer.
188 * Revision 1.55 1999/04/21 01:56:13 robertj
189 * Fixed problem with escape codes greater that %80
191 * Revision 1.54 1999/01/16 12:45:54 robertj
192 * Added RTSP schemes to URL's
194 * Revision 1.53 1998/11/30 05:38:15 robertj
195 * Moved PURL::Open() code to .cxx file to avoid linking unused code.
197 * Revision 1.52 1998/11/30 04:51:53 robertj
198 * New directory structure
200 * Revision 1.51 1998/09/23 06:22:07 robertj
201 * Added open source copyright license.
203 * Revision 1.50 1998/02/03 10:02:34 robertj
204 * Added ability to get scheme, host and port from URL as a string.
206 * Revision 1.49 1998/02/03 06:27:26 robertj
207 * Fixed URL encoding to be closer to RFC
209 * Revision 1.48 1998/01/26 02:49:16 robertj
210 * GNU support.
212 * Revision 1.47 1997/11/10 12:40:20 robertj
213 * Fixed illegal character set for URL's.
215 * Revision 1.46 1997/07/14 11:47:10 robertj
216 * Added "const" to numerous variables.
218 * Revision 1.45 1997/07/12 09:45:01 robertj
219 * Fixed bug when URL has + sign in somthing other than parameters.
221 * Revision 1.44 1997/06/06 08:54:47 robertj
222 * Allowed username/password on http scheme URL.
224 * Revision 1.43 1997/04/06 07:46:09 robertj
225 * Fixed bug where URL has more than special character ('?', '#' etc).
227 * Revision 1.42 1997/03/28 04:40:24 robertj
228 * Added tags for cookies.
230 * Revision 1.41 1997/03/18 22:03:44 robertj
231 * Fixed bug that incorrectly parses URL with double slashes.
233 * Revision 1.40 1997/02/14 13:55:44 robertj
234 * Fixed bug in URL for reproducing fields with special characters, must be escaped and weren't.
236 * Revision 1.39 1997/01/12 04:15:21 robertj
237 * Globalised MIME tag strings.
239 * Revision 1.38 1996/09/14 13:09:28 robertj
240 * Major upgrade:
241 * rearranged sockets to help support IPX.
242 * added indirect channel class and moved all protocols to descend from it,
243 * separating the protocol from the low level byte transport.
245 * Revision 1.37 1996/08/25 09:37:41 robertj
246 * Added function to detect "local" host name.
247 * Fixed printing of trailing '/' in empty URL, is distinction between with and without.
249 * Revision 1.36 1996/08/22 13:22:26 robertj
250 * Fixed bug in print of URLs, extra @ signs.
252 * Revision 1.35 1996/08/19 13:42:40 robertj
253 * Fixed errors in URL parsing and display.
254 * Fixed "Forbidden" problem out of HTTP authorisation system.
255 * Fixed authorisation so if have no user/password on basic authentication, does not require it.
257 * Revision 1.34 1996/07/27 04:13:47 robertj
258 * Fixed use of HTTP proxy on non-persistent connections.
260 * Revision 1.33 1996/07/15 10:37:20 robertj
261 * Improved proxy "self" detection (especially localhost).
263 * Revision 1.32 1996/06/28 13:20:24 robertj
264 * Modified HTTPAuthority so gets PHTTPReqest (mainly for URL) passed in.
265 * Moved HTTP form resource to another compilation module.
266 * Fixed memory leak in POST command.
268 * Revision 1.31 1996/06/10 10:00:00 robertj
269 * Added global function for query parameters parsing.
271 * Revision 1.30 1996/06/07 13:52:23 robertj
272 * Added PUT to HTTP proxy FTP. Necessitating redisign of entity body processing.
274 * Revision 1.29 1996/06/05 12:33:04 robertj
275 * Fixed bug in parsing URL with no path, is NOT absolute!
277 * Revision 1.28 1996/05/30 10:07:26 robertj
278 * Fixed bug in version number checking of return code compatibility.
280 * Revision 1.27 1996/05/26 03:46:42 robertj
281 * Compatibility to GNU 2.7.x
283 * Revision 1.26 1996/05/23 10:02:13 robertj
284 * Added common function for GET and HEAD commands.
285 * Fixed status codes to be the actual status code instead of sequential enum.
286 * This fixed some problems with proxy pass through of status codes.
287 * Fixed bug in URL parsing of username and passwords.
289 * Revision 1.19.1.1 1996/04/17 11:08:22 craigs
290 * New version by craig pending confirmation by robert
292 * Revision 1.19 1996/04/05 01:46:30 robertj
293 * Assured PSocket::Write always writes the number of bytes specified, no longer need write loops.
294 * Added workaraound for NT Netscape Navigator bug with persistent connections.
296 * Revision 1.18 1996/03/31 09:05:07 robertj
297 * HTTP 1.1 upgrade.
299 * Revision 1.17 1996/03/17 05:48:07 robertj
300 * Fixed host name print out of URLs.
301 * Added hit count to PHTTPResource.
303 * Revision 1.16 1996/03/16 05:00:26 robertj
304 * Added ParseReponse() for splitting reponse line into code and info.
305 * Added client side support for HTTP socket.
306 * Added hooks for proxy support in HTTP socket.
307 * Added translation type to TranslateString() to accommodate query variables.
308 * Defaulted scheme field in URL to "http".
309 * Inhibited output of port field on string conversion of URL according to scheme.
311 * Revision 1.15 1996/03/11 10:29:50 robertj
312 * Fixed bug in help image HTML.
314 * Revision 1.14 1996/03/10 13:15:24 robertj
315 * Redesign to make resources thread safe.
317 * Revision 1.13 1996/03/02 03:27:37 robertj
318 * Added function to translate a string to a form suitable for inclusion in a URL.
319 * Added radio button and selection boxes to HTTP form resource.
320 * Fixed bug in URL parsing, losing first / if hostname specified.
322 * Revision 1.12 1996/02/25 11:14:24 robertj
323 * Radio button support for forms.
325 * Revision 1.11 1996/02/25 03:10:34 robertj
326 * Removed pass through HTTP resource.
327 * Fixed PHTTPConfig resource to use correct name for config key.
329 * Revision 1.10 1996/02/19 13:48:28 robertj
330 * Put multiple uses of literal strings into const variables.
331 * Fixed URL parsing so that the unmangling of strings occurs correctly.
332 * Moved nested classes from PHTTPForm.
333 * Added overwrite option to AddResource().
334 * Added get/set string to PHTTPString resource.
336 * Revision 1.9 1996/02/13 13:09:17 robertj
337 * Added extra parameters to callback function in PHTTPResources, required
338 * by descendants to make informed decisions on data being loaded.
340 * Revision 1.8 1996/02/08 12:26:29 robertj
341 * Redesign of resource callback mechanism.
342 * Added new resource types for HTML data entry forms.
344 * Revision 1.7 1996/02/03 11:33:19 robertj
345 * Changed RadCmd() so can distinguish between I/O error and unknown command.
347 * Revision 1.6 1996/02/03 11:11:49 robertj
348 * Numerous bug fixes.
349 * Added expiry date and ismodifiedsince support.
351 * Revision 1.5 1996/01/30 23:32:40 robertj
352 * Added single .
354 * Revision 1.4 1996/01/28 14:19:09 robertj
355 * Split HTML into separate source file.
356 * Beginning of pass through resource type.
357 * Changed PCharArray in OnLoadData to PString for convenience in mangling data.
358 * Made PHTTPSpace return standard page on selection of partial path.
360 * Revision 1.3 1996/01/28 02:49:16 robertj
361 * Further implementation.
363 * Revision 1.2 1996/01/26 02:24:30 robertj
364 * Further implemetation.
366 * Revision 1.1 1996/01/23 13:04:32 robertj
367 * Initial revision
371 #ifdef __GNUC__
372 #pragma implementation "http.h"
373 #pragma implementation "url.h"
374 #endif
376 #include <ptlib.h>
377 #include <ptlib/sockets.h>
378 #include <ptclib/http.h>
379 #include <ptclib/url.h>
381 #include <ctype.h>
383 #ifdef WIN32
384 #include <shellapi.h>
385 #endif
388 // RFC 1738
389 // http://host:port/path...
390 // https://host:port/path....
391 // gopher://host:port
392 // wais://host:port
393 // nntp://host:port
394 // prospero://host:port
395 // ftp://user:password@host:port/path...
396 // telnet://user:password@host:port
397 // file://hostname/path...
399 // mailto:user@hostname
400 // news:string
402 #define DEFAULT_FTP_PORT 21
403 #define DEFAULT_TELNET_PORT 23
404 #define DEFAULT_GOPHER_PORT 70
405 #define DEFAULT_HTTP_PORT 80
406 #define DEFAULT_NNTP_PORT 119
407 #define DEFAULT_WAIS_PORT 210
408 #define DEFAULT_HTTPS_PORT 443
409 #define DEFAULT_RTSP_PORT 554
410 #define DEFAULT_RTSPU_PORT 554
411 #define DEFAULT_PROSPERO_PORT 1525
412 #define DEFAULT_H323_PORT 1720
413 #define DEFAULT_H323RAS_PORT 1719
414 #define DEFAULT_SIP_PORT 5060
417 struct schemeStruct {
418 const char * name;
419 BOOL hasUsername;
420 BOOL hasPassword;
421 BOOL hasHostPort;
422 BOOL defaultToUserIfNoAt;
423 BOOL defaultHostToLocal;
424 BOOL hasQuery;
425 BOOL hasParameters;
426 BOOL hasFragments;
427 BOOL hasPath;
428 BOOL relativeImpliesScheme;
429 WORD defaultPort;
432 #define DEFAULT_SCHEME 0
433 #define FILE_SCHEME 1
435 static schemeStruct const SchemeTable[] = {
436 // scheme user pass host @def defhost query params frags path rel port
437 { "http", TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, DEFAULT_HTTP_PORT }, // Must be first
438 { "file", FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, 0 }, // Must be second
439 { "https", FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, DEFAULT_HTTPS_PORT },
440 { "gopher", FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_GOPHER_PORT },
441 { "wais", FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_WAIS_PORT },
442 { "nntp", FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_NNTP_PORT },
443 { "prospero", FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_PROSPERO_PORT },
444 { "rtsp", FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_RTSP_PORT },
445 { "rtspu", FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_RTSPU_PORT },
446 { "ftp", TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_FTP_PORT },
447 { "telnet", TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, DEFAULT_TELNET_PORT },
448 { "mailto", FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, 0 },
449 { "news", FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, 0 },
450 { "h323", TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, DEFAULT_H323_PORT },
451 { "sip", TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, DEFAULT_SIP_PORT },
452 { "tel", FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, 0 },
453 { "fax", FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, 0 },
454 { "callto", FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, 0 },
455 { NULL, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, 0 }
458 static const schemeStruct * GetSchemeInfo(const PCaselessString & scheme)
460 PINDEX i;
461 for (i = 0; SchemeTable[i].name != NULL; i++) {
462 if (scheme == SchemeTable[i].name)
463 return &SchemeTable[i];
465 return NULL;
470 PINSTANTIATE_FACTORY(PURLScheme)
472 class PURLLegacyScheme : public PURLScheme
474 public:
475 PURLLegacyScheme(const char * _scheme)
476 : scheme(_scheme) { }
478 BOOL Parse(const PString & url, PURL & purl) const
479 { return purl.LegacyParse(url, this); }
481 PString AsString(PURL::UrlFormat fmt, const PURL & purl) const
482 { return purl.LegacyAsString(fmt, this); }
484 PString GetName() const
485 { return scheme; }
487 PString scheme;
488 BOOL hasUsername;
489 BOOL hasPassword;
490 BOOL hasHostPort;
491 BOOL defaultToUserIfNoAt;
492 BOOL defaultHostToLocal;
493 BOOL hasQuery;
494 BOOL hasParameters;
495 BOOL hasFragments;
496 BOOL hasPath;
497 BOOL relativeImpliesScheme;
498 WORD defaultPort;
502 #define DEFINE_LEGACY_URL_SCHEME(schemeName, user, pass, host, def, defhost, query, params, frags, path, rel, port) \
503 class PURLLegacyScheme_##schemeName : public PURLLegacyScheme \
505 public: \
506 PURLLegacyScheme_##schemeName() \
507 : PURLLegacyScheme(#schemeName ) \
509 hasUsername = user; \
510 hasPassword = pass; \
511 hasHostPort = host; \
512 defaultToUserIfNoAt = def; \
513 defaultHostToLocal = defhost; \
514 hasQuery = query; \
515 hasParameters = params; \
516 hasFragments = frags; \
517 hasPath = path; \
518 relativeImpliesScheme = rel; \
519 defaultPort = port; \
521 }; \
522 static PAbstractSingletonFactory<PURLScheme, PURLLegacyScheme_##schemeName> schemeName##Factory(#schemeName ); \
524 DEFINE_LEGACY_URL_SCHEME(http, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, DEFAULT_HTTP_PORT )
525 DEFINE_LEGACY_URL_SCHEME(file, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, 0)
526 DEFINE_LEGACY_URL_SCHEME(https, FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, DEFAULT_HTTPS_PORT)
527 DEFINE_LEGACY_URL_SCHEME(gopher, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_GOPHER_PORT)
528 DEFINE_LEGACY_URL_SCHEME(wais, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_WAIS_PORT)
529 DEFINE_LEGACY_URL_SCHEME(nntp, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_NNTP_PORT)
530 DEFINE_LEGACY_URL_SCHEME(prospero, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_PROSPERO_PORT)
531 DEFINE_LEGACY_URL_SCHEME(rtsp, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_RTSP_PORT)
532 DEFINE_LEGACY_URL_SCHEME(rtspu, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_RTSPU_PORT)
533 DEFINE_LEGACY_URL_SCHEME(ftp, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_FTP_PORT)
534 DEFINE_LEGACY_URL_SCHEME(telnet, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, DEFAULT_TELNET_PORT)
535 DEFINE_LEGACY_URL_SCHEME(mailto, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, 0)
536 DEFINE_LEGACY_URL_SCHEME(news, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, 0)
537 DEFINE_LEGACY_URL_SCHEME(h323, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, DEFAULT_H323_PORT)
538 DEFINE_LEGACY_URL_SCHEME(sip, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, DEFAULT_SIP_PORT)
539 DEFINE_LEGACY_URL_SCHEME(tel, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, 0)
540 DEFINE_LEGACY_URL_SCHEME(fax, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, 0)
541 DEFINE_LEGACY_URL_SCHEME(callto, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, 0)
543 #define DEFAULT_SCHEME "http"
544 #define FILE_SCHEME "file"
546 //////////////////////////////////////////////////////////////////////////////
547 // PURL
549 PURL::PURL()
550 : //scheme(SchemeTable[DEFAULT_SCHEME].name),
551 scheme(DEFAULT_SCHEME),
552 port(0),
553 relativePath(FALSE)
558 PURL::PURL(const char * str, const char * defaultScheme)
560 Parse(str, defaultScheme);
564 PURL::PURL(const PString & str, const char * defaultScheme)
566 Parse(str, defaultScheme);
570 PURL::PURL(const PFilePath & filePath)
571 : //scheme(SchemeTable[FILE_SCHEME].name),
572 scheme(FILE_SCHEME),
573 port(0),
574 relativePath(FALSE)
576 PStringArray pathArray = filePath.GetDirectory().GetPath();
577 hostname = pathArray[0];
579 PINDEX i;
580 for (i = 1; i < pathArray.GetSize(); i++)
581 pathArray[i-1] = pathArray[i];
582 pathArray[i-1] = filePath.GetFileName();
584 SetPath(pathArray);
588 PObject::Comparison PURL::Compare(const PObject & obj) const
590 PAssert(PIsDescendant(&obj, PURL), PInvalidCast);
591 return urlString.Compare(((const PURL &)obj).urlString);
595 PINDEX PURL::HashFunction() const
597 return urlString.HashFunction();
601 void PURL::PrintOn(ostream & stream) const
603 stream << urlString;
607 void PURL::ReadFrom(istream & stream)
609 PString s;
610 stream >> s;
611 Parse(s);
615 PString PURL::TranslateString(const PString & str, TranslationType type)
617 PString xlat = str;
619 PString safeChars = "abcdefghijklmnopqrstuvwxyz"
620 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
621 "0123456789$-_.!*'(),";
622 switch (type) {
623 case LoginTranslation :
624 safeChars += "+;?&=";
625 break;
627 case PathTranslation :
628 safeChars += "+:@&=";
629 break;
631 case QueryTranslation :
632 safeChars += ":@";
634 PINDEX pos = (PINDEX)-1;
635 while ((pos += 1+strspn(&xlat[pos+1], safeChars)) < xlat.GetLength())
636 xlat.Splice(psprintf("%%%02X", (BYTE)xlat[pos]), pos, 1);
638 if (type == QueryTranslation) {
639 PINDEX space = (PINDEX)-1;
640 while ((space = xlat.Find(' ', space+1)) != P_MAX_INDEX)
641 xlat[space] = '+';
644 return xlat;
648 PString PURL::UntranslateString(const PString & str, TranslationType type)
650 PString xlat = str;
651 xlat.MakeUnique();
653 PINDEX pos;
654 if (type == PURL::QueryTranslation) {
655 pos = (PINDEX)-1;
656 while ((pos = xlat.Find('+', pos+1)) != P_MAX_INDEX)
657 xlat[pos] = ' ';
660 pos = (PINDEX)-1;
661 while ((pos = xlat.Find('%', pos+1)) != P_MAX_INDEX) {
662 int digit1 = xlat[pos+1];
663 int digit2 = xlat[pos+2];
664 if (isxdigit(digit1) && isxdigit(digit2)) {
665 xlat[pos] = (char)(
666 (isdigit(digit2) ? (digit2-'0') : (toupper(digit2)-'A'+10)) +
667 ((isdigit(digit1) ? (digit1-'0') : (toupper(digit1)-'A'+10)) << 4));
668 xlat.Delete(pos+1, 2);
672 return xlat;
676 static void SplitVars(const PString & str, PStringToString & vars, char sep1, char sep2)
678 PINDEX sep1prev = 0;
679 do {
680 PINDEX sep1next = str.Find(sep1, sep1prev);
681 if (sep1next == P_MAX_INDEX)
682 sep1next--; // Implicit assumption string is not a couple of gigabytes long ...
684 PINDEX sep2pos = str.Find(sep2, sep1prev);
685 if (sep2pos > sep1next)
686 sep2pos = sep1next;
688 PCaselessString key = PURL::UntranslateString(str(sep1prev, sep2pos-1), PURL::QueryTranslation);
689 if (!key) {
690 PString data = PURL::UntranslateString(str(sep2pos+1, sep1next-1), PURL::QueryTranslation);
692 if (vars.Contains(key))
693 vars.SetAt(key, vars[key] + ',' + data);
694 else
695 vars.SetAt(key, data);
698 sep1prev = sep1next+1;
699 } while (sep1prev != P_MAX_INDEX);
703 void PURL::SplitQueryVars(const PString & queryStr, PStringToString & queryVars)
705 SplitVars(queryStr, queryVars, '&', '=');
709 BOOL PURL::InternalParse(const char * cstr, const char * defaultScheme)
711 urlString = cstr;
713 scheme.MakeEmpty();
714 username.MakeEmpty();
715 password.MakeEmpty();
716 hostname.MakeEmpty();
717 port = 0;
718 relativePath = FALSE;
719 pathStr.MakeEmpty();
720 path.SetSize(0);
721 paramVars.RemoveAll();
722 fragment.MakeEmpty();
723 queryVars.RemoveAll();
725 // copy the string so we can take bits off it
726 while (isspace(*cstr))
727 cstr++;
728 PString url = cstr;
730 // Character set as per RFC2396
731 PINDEX pos = 0;
732 while (isalnum(url[pos]) || url[pos] == '+' || url[pos] == '-' || url[pos] == '.')
733 pos++;
735 PString schemeName;
737 // get information which tells us how to parse URL for this
738 // particular scheme
739 PURLScheme * schemeInfo = NULL;
741 // Determine if the URL has an explicit scheme
742 if (url[pos] == ':') {
744 // get the scheme information, or get the default scheme
745 schemeInfo = PGenericFactory<PURLScheme>::CreateInstance(url.Left(pos));
746 if (schemeInfo == NULL && defaultScheme == NULL) {
747 PGenericFactory<PURLScheme>::KeyList_T keyList = PGenericFactory<PURLScheme>::GetKeyList();
748 if (keyList.size() != 0)
749 schemeInfo = PGenericFactory<PURLScheme>::CreateInstance(keyList[0]);
751 if (schemeInfo != NULL)
752 url.Delete(0, pos+1);
755 // if we could not match a scheme, then use the specified default scheme
756 if (schemeInfo == NULL && defaultScheme != NULL)
757 schemeInfo = PGenericFactory<PURLScheme>::CreateInstance(defaultScheme);
759 // if that still fails, then use the global default scheme
760 if (schemeInfo == NULL)
761 schemeInfo = PGenericFactory<PURLScheme>::CreateInstance(DEFAULT_SCHEME);
763 // if that fails, then there is nowehere to go
764 PAssert(schemeInfo != NULL, "Default scheme not available");
765 scheme = schemeInfo->GetName();
766 if (!schemeInfo->Parse(url, *this))
767 return FALSE;
769 return !IsEmpty();
772 BOOL PURL::LegacyParse(const PString & _url, const PURLLegacyScheme * schemeInfo)
774 PString url = _url;
775 PINDEX pos;
777 // Super special case!
778 if (scheme *= "callto") {
780 // Actually not part of MS spec, but a lot of people put in the // into
781 // the URL, so we take it out of it is there.
782 if (url.GetLength() > 2 && url[0] == '/' && url[1] == '/')
783 url.Delete(0, 2);
785 // For some bizarre reason callto uses + instead of ; for paramters
786 // We do a loop so that phone numbers of the form +61243654666 still work
787 do {
788 pos = url.Find('+');
789 } while (pos != P_MAX_INDEX && isdigit(url[pos+1]));
791 if (pos != P_MAX_INDEX) {
792 SplitVars(url(pos+1, P_MAX_INDEX), paramVars, '+', '=');
793 url.Delete(pos, P_MAX_INDEX);
796 hostname = paramVars("gateway");
797 if (!hostname)
798 username = UntranslateString(url, LoginTranslation);
799 else {
800 PCaselessString type = paramVars("type");
801 if (type == "directory") {
802 pos = url.Find('/');
803 if (pos == P_MAX_INDEX)
804 username = UntranslateString(url, LoginTranslation);
805 else {
806 hostname = UntranslateString(url.Left(pos), LoginTranslation);
807 username = UntranslateString(url.Mid(pos+1), LoginTranslation);
810 else {
811 // Now look for an @ and split user and host
812 pos = url.Find('@');
813 if (pos != P_MAX_INDEX) {
814 username = UntranslateString(url.Left(pos), LoginTranslation);
815 hostname = UntranslateString(url.Mid(pos+1), LoginTranslation);
817 else {
818 if (type == "ip" || type == "host")
819 hostname = UntranslateString(url, LoginTranslation);
820 else
821 username = UntranslateString(url, LoginTranslation);
826 // Allow for [ipv6] form
827 pos = hostname.Find(']');
828 if (pos == P_MAX_INDEX)
829 pos = 0;
830 pos = hostname.Find(':', pos);
831 if (pos != P_MAX_INDEX) {
832 port = (WORD)hostname.Mid(pos+1).AsUnsigned();
833 hostname.Delete(pos, P_MAX_INDEX);
836 password = paramVars("password");
837 return TRUE;
840 // if the URL should have leading slash, then remove it if it has one
841 if (schemeInfo != NULL && schemeInfo->hasHostPort && schemeInfo->hasPath) {
842 if (url.GetLength() > 2 && url[0] == '/' && url[1] == '/')
843 url.Delete(0, 2);
844 else
845 relativePath = TRUE;
848 // parse user/password/host/port
849 if (!relativePath && schemeInfo->hasHostPort) {
850 PString endHostChars;
851 if (schemeInfo->hasPath)
852 endHostChars += '/';
853 if (schemeInfo->hasQuery)
854 endHostChars += '?';
855 if (schemeInfo->hasParameters)
856 endHostChars += ';';
857 if (schemeInfo->hasFragments)
858 endHostChars += '#';
859 if (endHostChars.IsEmpty())
860 pos = P_MAX_INDEX;
861 else
862 pos = url.FindOneOf(endHostChars);
864 PString uphp = url.Left(pos);
865 if (pos != P_MAX_INDEX)
866 url.Delete(0, pos);
867 else
868 url.MakeEmpty();
870 // if the URL is of type UserPasswordHostPort, then parse it
871 if (schemeInfo->hasUsername) {
872 // extract username and password
873 PINDEX pos2 = uphp.Find('@');
874 PINDEX pos3 = P_MAX_INDEX;
875 if (schemeInfo->hasPassword)
876 pos3 = uphp.Find(':');
877 switch (pos2) {
878 case 0 :
879 uphp.Delete(0, 1);
880 break;
882 case P_MAX_INDEX :
883 if (schemeInfo->defaultToUserIfNoAt) {
884 if (pos3 == P_MAX_INDEX)
885 username = UntranslateString(uphp, LoginTranslation);
886 else {
887 username = UntranslateString(uphp.Left(pos3), LoginTranslation);
888 password = UntranslateString(uphp.Mid(pos3+1), LoginTranslation);
890 uphp.MakeEmpty();
892 break;
894 default :
895 if (pos3 > pos2)
896 username = UntranslateString(uphp.Left(pos2), LoginTranslation);
897 else {
898 username = UntranslateString(uphp.Left(pos3), LoginTranslation);
899 password = UntranslateString(uphp(pos3+1, pos2-1), LoginTranslation);
901 uphp.Delete(0, pos2+1);
905 // if the URL does not have a port, then this is the hostname
906 if (schemeInfo->defaultPort == 0)
907 hostname = UntranslateString(uphp, LoginTranslation);
908 else {
909 // determine if the URL has a port number
910 // Allow for [ipv6] form
911 pos = uphp.Find(']');
912 if (pos == P_MAX_INDEX)
913 pos = 0;
914 pos = uphp.Find(':', pos);
915 if (pos == P_MAX_INDEX)
916 hostname = UntranslateString(uphp, LoginTranslation);
917 else {
918 hostname = UntranslateString(uphp.Left(pos), LoginTranslation);
919 port = (WORD)uphp.Mid(pos+1).AsUnsigned();
922 if (hostname.IsEmpty() && schemeInfo->defaultHostToLocal)
923 hostname = PIPSocket::GetHostName();
927 if (schemeInfo->hasQuery) {
928 // chop off any trailing query
929 pos = url.Find('?');
930 if (pos != P_MAX_INDEX) {
931 SplitQueryVars(url(pos+1, P_MAX_INDEX), queryVars);
932 url.Delete(pos, P_MAX_INDEX);
936 if (schemeInfo->hasParameters) {
937 // chop off any trailing parameters
938 pos = url.Find(';');
939 if (pos != P_MAX_INDEX) {
940 SplitVars(url(pos+1, P_MAX_INDEX), paramVars, ';', '=');
941 url.Delete(pos, P_MAX_INDEX);
945 if (schemeInfo->hasFragments) {
946 // chop off any trailing fragment
947 pos = url.Find('#');
948 if (pos != P_MAX_INDEX) {
949 fragment = UntranslateString(url(pos+1, P_MAX_INDEX), PathTranslation);
950 url.Delete(pos, P_MAX_INDEX);
954 if (schemeInfo->hasPath)
955 SetPathStr(url); // the hierarchy is what is left
956 else {
957 // if the rest of the URL isn't a path, then we are finished!
958 pathStr = UntranslateString(url, PathTranslation);
959 Recalculate();
962 if (port == 0 && schemeInfo->defaultPort != 0 && !relativePath) {
963 // Yes another horrible, horrible special case!
964 if (scheme == "h323" && paramVars("type") == "gk")
965 port = DEFAULT_H323RAS_PORT;
966 else
967 port = schemeInfo->defaultPort;
968 Recalculate();
971 return TRUE;
975 PFilePath PURL::AsFilePath() const
977 //if (scheme != SchemeTable[FILE_SCHEME].name)
978 // return PString::Empty();
979 if (scheme != FILE_SCHEME)
980 return PString::Empty();
982 PStringStream str;
984 if (relativePath) {
985 for (PINDEX i = 0; i < path.GetSize(); i++) {
986 if (i > 0)
987 str << PDIR_SEPARATOR;
988 str << path[i];
991 else {
992 if (hostname != "localhost")
993 str << hostname;
994 for (PINDEX i = 0; i < path.GetSize(); i++)
995 str << PDIR_SEPARATOR << path[i];
998 return str;
1002 PString PURL::AsString(UrlFormat fmt) const
1004 if (fmt == FullURL)
1005 return urlString;
1007 if (scheme.IsEmpty())
1008 return PString::Empty();
1010 //const schemeStruct * schemeInfo = GetSchemeInfo(scheme);
1011 //if (schemeInfo == NULL)
1012 // schemeInfo = &SchemeTable[PARRAYSIZE(SchemeTable)-1];
1013 const PURLScheme * schemeInfo = PGenericFactory<PURLScheme>::CreateInstance(scheme);
1014 if (schemeInfo == NULL)
1015 schemeInfo = PGenericFactory<PURLScheme>::CreateInstance(DEFAULT_SCHEME);
1017 return schemeInfo->AsString(fmt, *this);
1020 PString PURL::LegacyAsString(PURL::UrlFormat fmt, const PURLLegacyScheme * schemeInfo) const
1022 PStringStream str;
1023 PINDEX i;
1025 if (fmt == HostPortOnly) {
1026 str << scheme << ':';
1028 if (relativePath) {
1029 if (schemeInfo->relativeImpliesScheme)
1030 return PString::Empty();
1031 return str;
1034 if (schemeInfo->hasPath && schemeInfo->hasHostPort)
1035 str << "//";
1037 if (schemeInfo->hasUsername) {
1038 if (!username) {
1039 str << TranslateString(username, LoginTranslation);
1040 if (schemeInfo->hasPassword && !password)
1041 str << ':' << TranslateString(password, LoginTranslation);
1042 str << '@';
1046 if (schemeInfo->hasHostPort)
1047 str << hostname;
1049 if (schemeInfo->defaultPort != 0) {
1050 if (port != schemeInfo->defaultPort)
1051 str << ':' << port;
1054 return str;
1057 // URIOnly and PathOnly
1058 if (schemeInfo->hasPath) {
1059 for (i = 0; i < path.GetSize(); i++) {
1060 if (i > 0 || !relativePath)
1061 str << '/';
1062 str << TranslateString(path[i], PathTranslation);
1065 else
1066 str << TranslateString(pathStr, PathTranslation);
1068 if (fmt == URIOnly) {
1069 if (!fragment)
1070 str << "#" << TranslateString(fragment, PathTranslation);
1072 for (i = 0; i < paramVars.GetSize(); i++) {
1073 str << ';' << TranslateString(paramVars.GetKeyAt(i), QueryTranslation);
1074 PString data = paramVars.GetDataAt(i);
1075 if (!data)
1076 str << '=' << TranslateString(data, QueryTranslation);
1079 if (!queryVars.IsEmpty())
1080 str << '?' << GetQuery();
1083 return str;
1087 void PURL::SetScheme(const PString & s)
1089 scheme = s;
1090 Recalculate();
1094 void PURL::SetUserName(const PString & u)
1096 username = u;
1097 Recalculate();
1101 void PURL::SetPassword(const PString & p)
1103 password = p;
1104 Recalculate();
1108 void PURL::SetHostName(const PString & h)
1110 hostname = h;
1111 Recalculate();
1115 void PURL::SetPort(WORD newPort)
1117 port = newPort;
1118 Recalculate();
1122 void PURL::SetPathStr(const PString & p)
1124 pathStr = p;
1126 path = pathStr.Tokenise("/", TRUE);
1128 if (path.GetSize() > 0 && path[0].IsEmpty())
1129 path.RemoveAt(0);
1131 for (PINDEX i = 0; i < path.GetSize(); i++) {
1132 path[i] = UntranslateString(path[i], PathTranslation);
1133 if (i > 0 && path[i] == ".." && path[i-1] != "..") {
1134 path.RemoveAt(i--);
1135 path.RemoveAt(i--);
1139 Recalculate();
1143 void PURL::SetPath(const PStringArray & p)
1145 path = p;
1147 pathStr.MakeEmpty();
1148 for (PINDEX i = 0; i < path.GetSize(); i++)
1149 pathStr += '/' + path[i];
1151 Recalculate();
1155 PString PURL::GetParameters() const
1157 PStringStream str;
1159 for (PINDEX i = 0; i < paramVars.GetSize(); i++) {
1160 if (i > 0)
1161 str << ';';
1162 str << paramVars.GetKeyAt(i);
1163 PString data = paramVars.GetDataAt(i);
1164 if (!data)
1165 str << '=' << data;
1168 return str;
1172 void PURL::SetParameters(const PString & parameters)
1174 SplitVars(parameters, paramVars, ';', '=');
1175 Recalculate();
1179 void PURL::SetParamVars(const PStringToString & p)
1181 paramVars = p;
1182 Recalculate();
1186 void PURL::SetParamVar(const PString & key, const PString & data)
1188 if (data.IsEmpty())
1189 paramVars.RemoveAt(key);
1190 else
1191 paramVars.SetAt(key, data);
1192 Recalculate();
1196 PString PURL::GetQuery() const
1198 PStringStream str;
1200 for (PINDEX i = 0; i < queryVars.GetSize(); i++) {
1201 if (i > 0)
1202 str << '&';
1203 str << TranslateString(queryVars.GetKeyAt(i), QueryTranslation)
1204 << '='
1205 << TranslateString(queryVars.GetDataAt(i), QueryTranslation);
1208 return str;
1212 void PURL::SetQuery(const PString & queryStr)
1214 SplitQueryVars(queryStr, queryVars);
1215 Recalculate();
1219 void PURL::SetQueryVars(const PStringToString & q)
1221 queryVars = q;
1222 Recalculate();
1226 void PURL::SetQueryVar(const PString & key, const PString & data)
1228 if (data.IsEmpty())
1229 queryVars.RemoveAt(key);
1230 else
1231 queryVars.SetAt(key, data);
1232 Recalculate();
1236 BOOL PURL::OpenBrowser(const PString & url)
1238 #ifdef WIN32
1239 SHELLEXECUTEINFO sei;
1240 ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO));
1241 sei.cbSize = sizeof(SHELLEXECUTEINFO);
1242 sei.lpVerb = TEXT("open");
1243 sei.lpFile = url;
1245 if (ShellExecuteEx(&sei) != 0)
1246 return TRUE;
1248 #ifndef _WIN32_WCE
1249 MessageBox(NULL, "Unable to open page"&url, PProcess::Current().GetName(), MB_TASKMODAL);
1250 #else
1251 USES_CONVERSION;
1252 MessageBox(NULL, _T("Unable to open page"), A2T(PProcess::Current().GetName()), MB_APPLMODAL);
1253 #endif // _WIN32_WCE
1255 #endif // WIN32
1256 return FALSE;
1260 void PURL::Recalculate()
1262 //if (scheme.IsEmpty())
1263 // scheme = SchemeTable[DEFAULT_SCHEME].name;
1264 if (scheme.IsEmpty())
1265 scheme = DEFAULT_SCHEME;
1267 urlString = AsString(HostPortOnly) + AsString(URIOnly);
1271 //////////////////////////////////////////////////////////////////////////////
1272 // PHTTP
1274 static char const * const HTTPCommands[PHTTP::NumCommands] = {
1275 // HTTP 1.0 commands
1276 "GET", "HEAD", "POST",
1278 // HTTP 1.1 commands
1279 "PUT", "DELETE", "TRACE", "OPTIONS",
1281 // HTTPS command
1282 "CONNECT"
1286 const char * const PHTTP::AllowTag = "Allow";
1287 const char * const PHTTP::AuthorizationTag = "Authorization";
1288 const char * const PHTTP::ContentEncodingTag = "Content-Encoding";
1289 const char * const PHTTP::ContentLengthTag = "Content-Length";
1290 const char * const PHTTP::ContentTypeTag = "Content-Type";
1291 const char * const PHTTP::DateTag = "Date";
1292 const char * const PHTTP::ExpiresTag = "Expires";
1293 const char * const PHTTP::FromTag = "From";
1294 const char * const PHTTP::IfModifiedSinceTag = "If-Modified-Since";
1295 const char * const PHTTP::LastModifiedTag = "Last-Modified";
1296 const char * const PHTTP::LocationTag = "Location";
1297 const char * const PHTTP::PragmaTag = "Pragma";
1298 const char * const PHTTP::PragmaNoCacheTag = "no-cache";
1299 const char * const PHTTP::RefererTag = "Referer";
1300 const char * const PHTTP::ServerTag = "Server";
1301 const char * const PHTTP::UserAgentTag = "User-Agent";
1302 const char * const PHTTP::WWWAuthenticateTag = "WWW-Authenticate";
1303 const char * const PHTTP::MIMEVersionTag = "MIME-Version";
1304 const char * const PHTTP::ConnectionTag = "Connection";
1305 const char * const PHTTP::KeepAliveTag = "Keep-Alive";
1306 const char * const PHTTP::TransferEncodingTag= "Transfer-Encoding";
1307 const char * const PHTTP::ChunkedTag = "chunked";
1308 const char * const PHTTP::ProxyConnectionTag = "Proxy-Connection";
1309 const char * const PHTTP::ProxyAuthorizationTag = "Proxy-Authorization";
1310 const char * const PHTTP::ProxyAuthenticateTag = "Proxy-Authenticate";
1311 const char * const PHTTP::ForwardedTag = "Forwarded";
1312 const char * const PHTTP::SetCookieTag = "Set-Cookie";
1313 const char * const PHTTP::CookieTag = "Cookie";
1317 PHTTP::PHTTP()
1318 : PInternetProtocol("www 80", NumCommands, HTTPCommands)
1323 PINDEX PHTTP::ParseResponse(const PString & line)
1325 PINDEX endVer = line.Find(' ');
1326 if (endVer == P_MAX_INDEX) {
1327 lastResponseInfo = "Bad response";
1328 lastResponseCode = PHTTP::InternalServerError;
1329 return 0;
1332 lastResponseInfo = line.Left(endVer);
1333 PINDEX endCode = line.Find(' ', endVer+1);
1334 lastResponseCode = line(endVer+1,endCode-1).AsInteger();
1335 if (lastResponseCode == 0)
1336 lastResponseCode = PHTTP::InternalServerError;
1337 lastResponseInfo &= line.Mid(endCode);
1338 return 0;
1342 // End Of File ///////////////////////////////////////////////////////////////