Changed output and intermediate directories, tuned up compile parameters for Windows...
[pwlib.git] / src / ptclib / http.cxx
blob18a011d2c75ff7d4f702aa8bce42b63fd20f8e9b
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.122 2006/08/29 01:33:25 csoutheren
28 * RFC 3261 specifies use of sips for secure SIP, so change URL parsing back...
30 * Revision 1.121 2006/08/21 05:23:13 csoutheren
31 * Add ssip and change h323s to sh323
33 * Revision 1.120 2006/08/21 01:31:03 csoutheren
34 * Add support h323s URLs
36 * Revision 1.119 2006/07/14 00:57:37 csoutheren
37 * Be safe for URLs containing non-ASCII characters
39 * Revision 1.118 2006/06/25 11:22:57 csoutheren
40 * Add pragmas to automate linking with VS 2005
42 * Revision 1.117 2006/06/21 03:28:44 csoutheren
43 * Various cleanups thanks for Frederic Heem
45 * Revision 1.116 2005/11/30 12:47:41 csoutheren
46 * Removed tabs, reformatted some code, and changed tags for Doxygen
48 * Revision 1.115 2005/04/20 05:19:48 csoutheren
49 * Patch 1185334. Ensure SIP URLs correctly store status of port
50 * Thanks to Ted Szoczei
52 * Revision 1.114 2005/01/16 20:36:48 csoutheren
53 * Changed URLS to put IP address in [] if contains a ":"
55 * Revision 1.113 2005/01/04 08:09:41 csoutheren
56 * Fixed Linux configure problems
58 * Revision 1.112 2004/12/08 00:51:12 csoutheren
59 * Move PURLLegacyScheme to header file to allow external usage
61 * Revision 1.111 2004/10/23 11:27:24 ykiryanov
62 * Added ifdef _WIN32_WCE for PocketPC 2003 SDK port
64 * Revision 1.110 2004/08/31 23:40:51 csoutheren
65 * Fixed problem with absolute file paths in URLs
67 * Revision 1.109 2004/07/14 13:15:45 rjongbloed
68 * Fixed minor bug where a URL is "non-empty" if requires a host but has none.
69 * eg could end up with "sip:" or "http://:80" which are illegal.
71 * Revision 1.108 2004/07/12 09:17:20 csoutheren
72 * Fixed warnings and errors under Linux
74 * Revision 1.107 2004/07/06 10:12:52 csoutheren
75 * Added static integer o factory template to assist in ensuring factories are instantiated
77 * Revision 1.106 2004/06/30 12:17:05 rjongbloed
78 * Rewrite of plug in system to use single global variable for all factories to avoid all sorts
79 * of issues with startup orders and Windows DLL multiple instances.
81 * Revision 1.105 2004/06/16 07:48:12 csoutheren
82 * Added assert to clarify usage of default scheme
84 * Revision 1.104 2004/06/03 13:30:58 csoutheren
85 * Renamed INSTANTIATE_FACTORY to avoid potential namespace collisions
86 * Added documentaton on new PINSTANTIATE_FACTORY macro
87 * Added generic form of PINSTANTIATE_FACTORY
89 * Revision 1.103 2004/06/03 12:47:58 csoutheren
90 * Decomposed PFactory declarations to hopefully avoid problems with Windows DLLs
92 * Revision 1.102 2004/06/01 07:28:45 csoutheren
93 * Changed URL parsing to use abstract factory code
95 * Revision 1.101 2004/04/04 00:21:47 csoutheren
96 * FIxed problem with some URL parsing
98 * Revision 1.100 2004/04/03 08:22:20 csoutheren
99 * Remove pseudo-RTTI and replaced with real RTTI
101 * Revision 1.99 2004/03/23 05:08:21 csoutheren
102 * Fixed problem with use of ShellExecuteEx function
104 * Revision 1.98 2004/03/13 06:30:52 rjongbloed
105 * Virtualised parse function.
107 * Revision 1.97 2004/02/24 11:14:44 rjongbloed
108 * Fixed correct initialisation of some internal variables in URL if parse fails.
110 * Revision 1.96 2004/01/17 17:44:17 csoutheren
111 * Changed to use PString::MakeEmpty
113 * Revision 1.95 2003/11/18 09:22:17 csoutheren
114 * Fixed problems with PURL::OpenBrowser, thanks to David Parr
116 * Revision 1.94 2003/08/27 03:37:45 dereksmithies
117 * Fix initialization of pathStr so it really is empty. BIG thanks to Diego Tartara.
119 * Revision 1.93 2003/07/22 03:26:10 csoutheren
120 * Fixed problem with parsing default H323 addresses
122 * Revision 1.92 2003/06/23 15:31:40 ykiryanov
123 * Slightly changed call to ShellExecuteEx to make compatible with Win32
125 * Revision 1.91 2003/06/23 14:31:33 ykiryanov
126 * Modified for WinCE - used ShellExecuteEx instead of ShellExecute
128 * Revision 1.90 2003/06/05 00:15:54 rjongbloed
129 * Fixed callto bug created by previous patch.
131 * Revision 1.89 2003/06/04 01:42:05 rjongbloed
132 * Fixed h323 scheme, does not have a "password" field.
134 * Revision 1.88 2003/06/02 02:46:45 rjongbloed
135 * Fixed issue with callto URL parsing incorrect username field.
136 * Added automatic removal of illegal (though common) "//" in callto URL.
138 * Revision 1.87 2003/05/05 07:30:17 craigs
139 * Fixed problem with URLs that do not specify schemes
141 * Revision 1.86 2003/05/02 13:50:23 craigs
142 * Fixed a problem with callto:localhost
144 * Revision 1.85 2003/05/02 13:20:33 craigs
145 * Fixed callto problems
147 * Revision 1.84 2003/04/28 04:41:22 robertj
148 * Changed URL parsing so if a default scheme is present then explicit scheme
149 * must be "known" to avoid ambiguity with host:port parsing.
151 * Revision 1.83 2003/04/10 00:13:56 robertj
152 * Fixed correct decoding of user/password/host/port field, for non h323 schemes.
154 * Revision 1.82 2003/04/08 06:28:14 craigs
155 * Fixed introduced problem with HTTP server mistaking relative URLs for proxy requests
157 * Revision 1.81 2003/04/04 08:03:55 robertj
158 * Fixed special case of h323 URL default port changing depending on
159 * if it the host is an endpoint or gatekeeper.
161 * Revision 1.80 2003/04/04 05:18:08 robertj
162 * Added "callto", "tel" and fixed "h323" URL types.
164 * Revision 1.79 2002/12/02 00:17:03 robertj
165 * Fixed URL parsing/display problems with non-path URL type eg mailto
167 * Revision 1.78 2002/11/22 06:16:49 robertj
168 * Fixed usage of URI (relative http/https URL).
170 * Revision 1.77 2002/11/20 02:10:56 robertj
171 * Fixed some more realtive/absolute path issues.
173 * Revision 1.76 2002/11/20 01:01:49 robertj
174 * Fixed GNU compatibility
176 * Revision 1.75 2002/11/20 00:49:37 robertj
177 * Fixed correct interpretation of url re double slashes as per latest RFC,
178 * including file: mapping and relative paths. Probably still more to do.
180 * Revision 1.74 2002/11/19 22:45:03 robertj
181 * Fixed support for file: scheme under unix
183 * Revision 1.73 2002/11/19 10:36:50 robertj
184 * Added functions to set anf get "file:" URL. as PFilePath and do the right
185 * things with platform dependent directory components.
187 * Revision 1.72 2002/11/06 22:47:25 robertj
188 * Fixed header comment (copyright etc)
190 * Revision 1.71 2002/09/23 07:17:24 robertj
191 * Changes to allow winsock2 to be included.
193 * Revision 1.70 2002/08/28 08:06:11 craigs
194 * Fixed problem (again) with file:// URLs
196 * Revision 1.69 2002/08/28 05:11:23 craigs
197 * Fixed problem with file:// URLs
199 * Revision 1.68 2002/05/02 05:11:29 craigs
200 * Fixed problem with not translating + chars in URL query parameters
202 * Revision 1.67 2002/03/19 23:39:57 robertj
203 * Fixed string output to include PathOnly variant, lost in previous mod.
205 * Revision 1.66 2002/03/19 23:24:08 robertj
206 * Fixed problems with backward compatibility on parameters processing.
208 * Revision 1.65 2002/03/18 05:02:27 robertj
209 * Added functions to set component parts of URL.
210 * Fixed output of parameters when more than one ';' involved.
212 * Revision 1.64 2001/11/09 05:46:14 robertj
213 * Removed double slash on sip URL.
214 * Fixed extra : if have username but no password.
215 * Added h323: scheme
217 * Revision 1.63 2001/11/08 00:32:49 robertj
218 * Added parsing of ';' based parameter fields into string dictionary if there are multiple parameters, with '=' values.
220 * Revision 1.62 2001/10/31 01:33:07 robertj
221 * Added extra const for constant HTTP tag name strings.
223 * Revision 1.61 2001/10/03 00:26:34 robertj
224 * Upgraded client to HTTP/1.1 and for chunked mode entity bodies.
226 * Revision 1.60 2001/09/28 00:45:42 robertj
227 * Broke out internal static function for unstranslating URL strings.
229 * Revision 1.59 2001/07/16 00:43:06 craigs
230 * Added ability to parse other transport URLs
232 * Revision 1.58 2000/05/02 08:29:07 craigs
233 * Removed "memory leaks" caused by brain-dead GNU linker
235 * Revision 1.57 1999/05/11 12:24:18 robertj
236 * Fixed URL parser so leading blanks are ignored.
238 * Revision 1.56 1999/05/04 15:26:01 robertj
239 * Improved HTTP/1.1 compatibility (pass through user commands).
240 * Fixed problems with quicktime installer.
242 * Revision 1.55 1999/04/21 01:56:13 robertj
243 * Fixed problem with escape codes greater that %80
245 * Revision 1.54 1999/01/16 12:45:54 robertj
246 * Added RTSP schemes to URL's
248 * Revision 1.53 1998/11/30 05:38:15 robertj
249 * Moved PURL::Open() code to .cxx file to avoid linking unused code.
251 * Revision 1.52 1998/11/30 04:51:53 robertj
252 * New directory structure
254 * Revision 1.51 1998/09/23 06:22:07 robertj
255 * Added open source copyright license.
257 * Revision 1.50 1998/02/03 10:02:34 robertj
258 * Added ability to get scheme, host and port from URL as a string.
260 * Revision 1.49 1998/02/03 06:27:26 robertj
261 * Fixed URL encoding to be closer to RFC
263 * Revision 1.48 1998/01/26 02:49:16 robertj
264 * GNU support.
266 * Revision 1.47 1997/11/10 12:40:20 robertj
267 * Fixed illegal character set for URL's.
269 * Revision 1.46 1997/07/14 11:47:10 robertj
270 * Added "const" to numerous variables.
272 * Revision 1.45 1997/07/12 09:45:01 robertj
273 * Fixed bug when URL has + sign in somthing other than parameters.
275 * Revision 1.44 1997/06/06 08:54:47 robertj
276 * Allowed username/password on http scheme URL.
278 * Revision 1.43 1997/04/06 07:46:09 robertj
279 * Fixed bug where URL has more than special character ('?', '#' etc).
281 * Revision 1.42 1997/03/28 04:40:24 robertj
282 * Added tags for cookies.
284 * Revision 1.41 1997/03/18 22:03:44 robertj
285 * Fixed bug that incorrectly parses URL with double slashes.
287 * Revision 1.40 1997/02/14 13:55:44 robertj
288 * Fixed bug in URL for reproducing fields with special characters, must be escaped and weren't.
290 * Revision 1.39 1997/01/12 04:15:21 robertj
291 * Globalised MIME tag strings.
293 * Revision 1.38 1996/09/14 13:09:28 robertj
294 * Major upgrade:
295 * rearranged sockets to help support IPX.
296 * added indirect channel class and moved all protocols to descend from it,
297 * separating the protocol from the low level byte transport.
299 * Revision 1.37 1996/08/25 09:37:41 robertj
300 * Added function to detect "local" host name.
301 * Fixed printing of trailing '/' in empty URL, is distinction between with and without.
303 * Revision 1.36 1996/08/22 13:22:26 robertj
304 * Fixed bug in print of URLs, extra @ signs.
306 * Revision 1.35 1996/08/19 13:42:40 robertj
307 * Fixed errors in URL parsing and display.
308 * Fixed "Forbidden" problem out of HTTP authorisation system.
309 * Fixed authorisation so if have no user/password on basic authentication, does not require it.
311 * Revision 1.34 1996/07/27 04:13:47 robertj
312 * Fixed use of HTTP proxy on non-persistent connections.
314 * Revision 1.33 1996/07/15 10:37:20 robertj
315 * Improved proxy "self" detection (especially localhost).
317 * Revision 1.32 1996/06/28 13:20:24 robertj
318 * Modified HTTPAuthority so gets PHTTPReqest (mainly for URL) passed in.
319 * Moved HTTP form resource to another compilation module.
320 * Fixed memory leak in POST command.
322 * Revision 1.31 1996/06/10 10:00:00 robertj
323 * Added global function for query parameters parsing.
325 * Revision 1.30 1996/06/07 13:52:23 robertj
326 * Added PUT to HTTP proxy FTP. Necessitating redisign of entity body processing.
328 * Revision 1.29 1996/06/05 12:33:04 robertj
329 * Fixed bug in parsing URL with no path, is NOT absolute!
331 * Revision 1.28 1996/05/30 10:07:26 robertj
332 * Fixed bug in version number checking of return code compatibility.
334 * Revision 1.27 1996/05/26 03:46:42 robertj
335 * Compatibility to GNU 2.7.x
337 * Revision 1.26 1996/05/23 10:02:13 robertj
338 * Added common function for GET and HEAD commands.
339 * Fixed status codes to be the actual status code instead of sequential enum.
340 * This fixed some problems with proxy pass through of status codes.
341 * Fixed bug in URL parsing of username and passwords.
343 * Revision 1.19.1.1 1996/04/17 11:08:22 craigs
344 * New version by craig pending confirmation by robert
346 * Revision 1.19 1996/04/05 01:46:30 robertj
347 * Assured PSocket::Write always writes the number of bytes specified, no longer need write loops.
348 * Added workaraound for NT Netscape Navigator bug with persistent connections.
350 * Revision 1.18 1996/03/31 09:05:07 robertj
351 * HTTP 1.1 upgrade.
353 * Revision 1.17 1996/03/17 05:48:07 robertj
354 * Fixed host name print out of URLs.
355 * Added hit count to PHTTPResource.
357 * Revision 1.16 1996/03/16 05:00:26 robertj
358 * Added ParseReponse() for splitting reponse line into code and info.
359 * Added client side support for HTTP socket.
360 * Added hooks for proxy support in HTTP socket.
361 * Added translation type to TranslateString() to accommodate query variables.
362 * Defaulted scheme field in URL to "http".
363 * Inhibited output of port field on string conversion of URL according to scheme.
365 * Revision 1.15 1996/03/11 10:29:50 robertj
366 * Fixed bug in help image HTML.
368 * Revision 1.14 1996/03/10 13:15:24 robertj
369 * Redesign to make resources thread safe.
371 * Revision 1.13 1996/03/02 03:27:37 robertj
372 * Added function to translate a string to a form suitable for inclusion in a URL.
373 * Added radio button and selection boxes to HTTP form resource.
374 * Fixed bug in URL parsing, losing first / if hostname specified.
376 * Revision 1.12 1996/02/25 11:14:24 robertj
377 * Radio button support for forms.
379 * Revision 1.11 1996/02/25 03:10:34 robertj
380 * Removed pass through HTTP resource.
381 * Fixed PHTTPConfig resource to use correct name for config key.
383 * Revision 1.10 1996/02/19 13:48:28 robertj
384 * Put multiple uses of literal strings into const variables.
385 * Fixed URL parsing so that the unmangling of strings occurs correctly.
386 * Moved nested classes from PHTTPForm.
387 * Added overwrite option to AddResource().
388 * Added get/set string to PHTTPString resource.
390 * Revision 1.9 1996/02/13 13:09:17 robertj
391 * Added extra parameters to callback function in PHTTPResources, required
392 * by descendants to make informed decisions on data being loaded.
394 * Revision 1.8 1996/02/08 12:26:29 robertj
395 * Redesign of resource callback mechanism.
396 * Added new resource types for HTML data entry forms.
398 * Revision 1.7 1996/02/03 11:33:19 robertj
399 * Changed RadCmd() so can distinguish between I/O error and unknown command.
401 * Revision 1.6 1996/02/03 11:11:49 robertj
402 * Numerous bug fixes.
403 * Added expiry date and ismodifiedsince support.
405 * Revision 1.5 1996/01/30 23:32:40 robertj
406 * Added single .
408 * Revision 1.4 1996/01/28 14:19:09 robertj
409 * Split HTML into separate source file.
410 * Beginning of pass through resource type.
411 * Changed PCharArray in OnLoadData to PString for convenience in mangling data.
412 * Made PHTTPSpace return standard page on selection of partial path.
414 * Revision 1.3 1996/01/28 02:49:16 robertj
415 * Further implementation.
417 * Revision 1.2 1996/01/26 02:24:30 robertj
418 * Further implemetation.
420 * Revision 1.1 1996/01/23 13:04:32 robertj
421 * Initial revision
425 #ifdef __GNUC__
426 #pragma implementation "http.h"
427 #pragma implementation "url.h"
428 #endif
430 #include <ptlib.h>
432 #define P_DISABLE_FACTORY_INSTANCES
433 #include <ptlib/pfactory.h>
434 #include <ptlib/sockets.h>
435 #include <ptclib/http.h>
436 #include <ptclib/url.h>
438 #include <ctype.h>
440 #ifdef WIN32
441 #include <shellapi.h>
442 #pragma comment(lib,"shell32.lib")
443 #endif
446 // RFC 1738
447 // http://host:port/path...
448 // https://host:port/path....
449 // gopher://host:port
450 // wais://host:port
451 // nntp://host:port
452 // prospero://host:port
453 // ftp://user:password@host:port/path...
454 // telnet://user:password@host:port
455 // file://hostname/path...
457 // mailto:user@hostname
458 // news:string
460 #define DEFAULT_FTP_PORT 21
461 #define DEFAULT_TELNET_PORT 23
462 #define DEFAULT_GOPHER_PORT 70
463 #define DEFAULT_HTTP_PORT 80
464 #define DEFAULT_NNTP_PORT 119
465 #define DEFAULT_WAIS_PORT 210
466 #define DEFAULT_HTTPS_PORT 443
467 #define DEFAULT_RTSP_PORT 554
468 #define DEFAULT_RTSPU_PORT 554
469 #define DEFAULT_PROSPERO_PORT 1525
470 #define DEFAULT_H323_PORT 1720
471 #define DEFAULT_H323S_PORT 1300
472 #define DEFAULT_H323RAS_PORT 1719
473 #define DEFAULT_SIP_PORT 5060
474 #define DEFAULT_SIPS_PORT 5061
476 #define DEFINE_LEGACY_URL_SCHEME(schemeName, user, pass, host, def, defhost, query, params, frags, path, rel, port) \
477 class PURLLegacyScheme_##schemeName : public PURLLegacyScheme \
479 public: \
480 PURLLegacyScheme_##schemeName() \
481 : PURLLegacyScheme(#schemeName ) \
483 hasUsername = user; \
484 hasPassword = pass; \
485 hasHostPort = host; \
486 defaultToUserIfNoAt = def; \
487 defaultHostToLocal = defhost; \
488 hasQuery = query; \
489 hasParameters = params; \
490 hasFragments = frags; \
491 hasPath = path; \
492 relativeImpliesScheme = rel; \
493 defaultPort = port; \
495 }; \
496 static PFactory<PURLScheme>::Worker<PURLLegacyScheme_##schemeName> schemeName##Factory(#schemeName, true); \
498 DEFINE_LEGACY_URL_SCHEME(http, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, DEFAULT_HTTP_PORT )
499 DEFINE_LEGACY_URL_SCHEME(file, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, 0)
500 DEFINE_LEGACY_URL_SCHEME(https, FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, DEFAULT_HTTPS_PORT)
501 DEFINE_LEGACY_URL_SCHEME(gopher, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_GOPHER_PORT)
502 DEFINE_LEGACY_URL_SCHEME(wais, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_WAIS_PORT)
503 DEFINE_LEGACY_URL_SCHEME(nntp, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_NNTP_PORT)
504 DEFINE_LEGACY_URL_SCHEME(prospero, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_PROSPERO_PORT)
505 DEFINE_LEGACY_URL_SCHEME(rtsp, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_RTSP_PORT)
506 DEFINE_LEGACY_URL_SCHEME(rtspu, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_RTSPU_PORT)
507 DEFINE_LEGACY_URL_SCHEME(ftp, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, DEFAULT_FTP_PORT)
508 DEFINE_LEGACY_URL_SCHEME(telnet, TRUE, TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, DEFAULT_TELNET_PORT)
509 DEFINE_LEGACY_URL_SCHEME(mailto, FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, 0)
510 DEFINE_LEGACY_URL_SCHEME(news, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, 0)
511 DEFINE_LEGACY_URL_SCHEME(h323, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, DEFAULT_H323_PORT)
512 DEFINE_LEGACY_URL_SCHEME(h323s, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, DEFAULT_H323S_PORT)
513 DEFINE_LEGACY_URL_SCHEME(sip, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, DEFAULT_SIP_PORT)
514 DEFINE_LEGACY_URL_SCHEME(sips, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, DEFAULT_SIPS_PORT)
515 DEFINE_LEGACY_URL_SCHEME(tel, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, 0)
516 DEFINE_LEGACY_URL_SCHEME(fax, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, 0)
517 DEFINE_LEGACY_URL_SCHEME(callto, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, 0)
519 PINSTANTIATE_FACTORY(PURLScheme, PString)
521 #define DEFAULT_SCHEME "http"
522 #define FILE_SCHEME "file"
524 //////////////////////////////////////////////////////////////////////////////
525 // PURL
527 PURL::PURL()
528 : //scheme(SchemeTable[DEFAULT_SCHEME].name),
529 scheme(DEFAULT_SCHEME),
530 port(0),
531 portSupplied (FALSE),
532 relativePath(FALSE)
537 PURL::PURL(const char * str, const char * defaultScheme)
539 Parse(str, defaultScheme);
543 PURL::PURL(const PString & str, const char * defaultScheme)
545 Parse(str, defaultScheme);
549 PURL::PURL(const PFilePath & filePath)
550 : //scheme(SchemeTable[FILE_SCHEME].name),
551 scheme(FILE_SCHEME),
552 port(0),
553 portSupplied (FALSE),
554 relativePath(FALSE)
556 PStringArray pathArray = filePath.GetDirectory().GetPath();
557 hostname = pathArray[0];
559 PINDEX i;
560 for (i = 1; i < pathArray.GetSize(); i++)
561 pathArray[i-1] = pathArray[i];
562 pathArray[i-1] = filePath.GetFileName();
564 SetPath(pathArray);
568 PObject::Comparison PURL::Compare(const PObject & obj) const
570 PAssert(PIsDescendant(&obj, PURL), PInvalidCast);
571 return urlString.Compare(((const PURL &)obj).urlString);
575 PINDEX PURL::HashFunction() const
577 return urlString.HashFunction();
581 void PURL::PrintOn(ostream & stream) const
583 stream << urlString;
587 void PURL::ReadFrom(istream & stream)
589 PString s;
590 stream >> s;
591 Parse(s);
595 PString PURL::TranslateString(const PString & str, TranslationType type)
597 PString xlat = str;
599 PString safeChars = "abcdefghijklmnopqrstuvwxyz"
600 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
601 "0123456789$-_.!*'(),";
602 switch (type) {
603 case LoginTranslation :
604 safeChars += "+;?&=";
605 break;
607 case PathTranslation :
608 safeChars += "+:@&=";
609 break;
611 case QueryTranslation :
612 safeChars += ":@";
614 PINDEX pos = (PINDEX)-1;
615 while ((pos += 1+strspn(&xlat[pos+1], safeChars)) < xlat.GetLength())
616 xlat.Splice(psprintf("%%%02X", (BYTE)xlat[pos]), pos, 1);
618 if (type == QueryTranslation) {
619 PINDEX space = (PINDEX)-1;
620 while ((space = xlat.Find(' ', space+1)) != P_MAX_INDEX)
621 xlat[space] = '+';
624 return xlat;
628 PString PURL::UntranslateString(const PString & str, TranslationType type)
630 PString xlat = str;
631 xlat.MakeUnique();
633 PINDEX pos;
634 if (type == PURL::QueryTranslation) {
635 pos = (PINDEX)-1;
636 while ((pos = xlat.Find('+', pos+1)) != P_MAX_INDEX)
637 xlat[pos] = ' ';
640 pos = (PINDEX)-1;
641 while ((pos = xlat.Find('%', pos+1)) != P_MAX_INDEX) {
642 int digit1 = xlat[pos+1];
643 int digit2 = xlat[pos+2];
644 if (isxdigit(digit1) && isxdigit(digit2)) {
645 xlat[pos] = (char)(
646 (isdigit(digit2) ? (digit2-'0') : (toupper(digit2)-'A'+10)) +
647 ((isdigit(digit1) ? (digit1-'0') : (toupper(digit1)-'A'+10)) << 4));
648 xlat.Delete(pos+1, 2);
652 return xlat;
656 static void SplitVars(const PString & str, PStringToString & vars, char sep1, char sep2)
658 PINDEX sep1prev = 0;
659 do {
660 PINDEX sep1next = str.Find(sep1, sep1prev);
661 if (sep1next == P_MAX_INDEX)
662 sep1next--; // Implicit assumption string is not a couple of gigabytes long ...
664 PINDEX sep2pos = str.Find(sep2, sep1prev);
665 if (sep2pos > sep1next)
666 sep2pos = sep1next;
668 PCaselessString key = PURL::UntranslateString(str(sep1prev, sep2pos-1), PURL::QueryTranslation);
669 if (!key) {
670 PString data = PURL::UntranslateString(str(sep2pos+1, sep1next-1), PURL::QueryTranslation);
672 if (vars.Contains(key))
673 vars.SetAt(key, vars[key] + ',' + data);
674 else
675 vars.SetAt(key, data);
678 sep1prev = sep1next+1;
679 } while (sep1prev != P_MAX_INDEX);
683 void PURL::SplitQueryVars(const PString & queryStr, PStringToString & queryVars)
685 SplitVars(queryStr, queryVars, '&', '=');
689 BOOL PURL::InternalParse(const char * cstr, const char * defaultScheme)
691 urlString = cstr;
693 scheme.MakeEmpty();
694 username.MakeEmpty();
695 password.MakeEmpty();
696 hostname.MakeEmpty();
697 port = 0;
698 portSupplied = FALSE;
699 relativePath = FALSE;
700 pathStr.MakeEmpty();
701 path.SetSize(0);
702 paramVars.RemoveAll();
703 fragment.MakeEmpty();
704 queryVars.RemoveAll();
706 // copy the string so we can take bits off it
707 while (((*cstr & 0x80) == 0x00) && isspace(*cstr))
708 cstr++;
709 PString url = cstr;
711 // Character set as per RFC2396
712 PINDEX pos = 0;
713 while ( ((*cstr & 0x80) != 0x00) || isalnum(url[pos]) || url[pos] == '+' || url[pos] == '-' || url[pos] == '.')
714 pos++;
716 PString schemeName;
718 // get information which tells us how to parse URL for this
719 // particular scheme
720 PURLScheme * schemeInfo = NULL;
722 // Determine if the URL has an explicit scheme
723 if (url[pos] == ':') {
725 // get the scheme information, or get the default scheme
726 schemeInfo = PFactory<PURLScheme>::CreateInstance(url.Left(pos));
727 if (schemeInfo == NULL && defaultScheme == NULL) {
728 PFactory<PURLScheme>::KeyList_T keyList = PFactory<PURLScheme>::GetKeyList();
729 if (keyList.size() != 0)
730 schemeInfo = PFactory<PURLScheme>::CreateInstance(keyList[0]);
732 if (schemeInfo != NULL)
733 url.Delete(0, pos+1);
736 // if we could not match a scheme, then use the specified default scheme
737 if (schemeInfo == NULL && defaultScheme != NULL)
738 schemeInfo = PFactory<PURLScheme>::CreateInstance(defaultScheme);
740 // if that still fails, then use the global default scheme
741 if (schemeInfo == NULL)
742 schemeInfo = PFactory<PURLScheme>::CreateInstance(DEFAULT_SCHEME);
744 // if that fails, then there is nowehere to go
745 PAssert(schemeInfo != NULL, "Default scheme not available");
746 scheme = schemeInfo->GetName();
747 if (!schemeInfo->Parse(url, *this))
748 return FALSE;
750 return !IsEmpty();
753 BOOL PURL::LegacyParse(const PString & _url, const PURLLegacyScheme * schemeInfo)
755 PString url = _url;
756 PINDEX pos;
758 // Super special case!
759 if (scheme *= "callto") {
761 // Actually not part of MS spec, but a lot of people put in the // into
762 // the URL, so we take it out of it is there.
763 if (url.GetLength() > 2 && url[0] == '/' && url[1] == '/')
764 url.Delete(0, 2);
766 // For some bizarre reason callto uses + instead of ; for paramters
767 // We do a loop so that phone numbers of the form +61243654666 still work
768 do {
769 pos = url.Find('+');
770 } while (pos != P_MAX_INDEX && isdigit(url[pos+1]));
772 if (pos != P_MAX_INDEX) {
773 SplitVars(url(pos+1, P_MAX_INDEX), paramVars, '+', '=');
774 url.Delete(pos, P_MAX_INDEX);
777 hostname = paramVars("gateway");
778 if (!hostname)
779 username = UntranslateString(url, LoginTranslation);
780 else {
781 PCaselessString type = paramVars("type");
782 if (type == "directory") {
783 pos = url.Find('/');
784 if (pos == P_MAX_INDEX)
785 username = UntranslateString(url, LoginTranslation);
786 else {
787 hostname = UntranslateString(url.Left(pos), LoginTranslation);
788 username = UntranslateString(url.Mid(pos+1), LoginTranslation);
791 else {
792 // Now look for an @ and split user and host
793 pos = url.Find('@');
794 if (pos != P_MAX_INDEX) {
795 username = UntranslateString(url.Left(pos), LoginTranslation);
796 hostname = UntranslateString(url.Mid(pos+1), LoginTranslation);
798 else {
799 if (type == "ip" || type == "host")
800 hostname = UntranslateString(url, LoginTranslation);
801 else
802 username = UntranslateString(url, LoginTranslation);
807 // Allow for [ipv6] form
808 pos = hostname.Find(']');
809 if (pos == P_MAX_INDEX)
810 pos = 0;
811 pos = hostname.Find(':', pos);
812 if (pos != P_MAX_INDEX) {
813 port = (WORD)hostname.Mid(pos+1).AsUnsigned();
814 portSupplied = TRUE;
815 hostname.Delete(pos, P_MAX_INDEX);
818 password = paramVars("password");
819 return TRUE;
822 // if the URL should have leading slash, then remove it if it has one
823 if (schemeInfo != NULL && schemeInfo->hasHostPort && schemeInfo->hasPath) {
824 if (url.GetLength() > 2 && url[0] == '/' && url[1] == '/')
825 url.Delete(0, 2);
826 else
827 relativePath = TRUE;
830 // parse user/password/host/port
831 if (!relativePath && schemeInfo->hasHostPort) {
832 PString endHostChars;
833 if (schemeInfo->hasPath)
834 endHostChars += '/';
835 if (schemeInfo->hasQuery)
836 endHostChars += '?';
837 if (schemeInfo->hasParameters)
838 endHostChars += ';';
839 if (schemeInfo->hasFragments)
840 endHostChars += '#';
841 if (endHostChars.IsEmpty())
842 pos = P_MAX_INDEX;
843 else
844 pos = url.FindOneOf(endHostChars);
846 PString uphp = url.Left(pos);
847 if (pos != P_MAX_INDEX)
848 url.Delete(0, pos);
849 else
850 url.MakeEmpty();
852 // if the URL is of type UserPasswordHostPort, then parse it
853 if (schemeInfo->hasUsername) {
854 // extract username and password
855 PINDEX pos2 = uphp.Find('@');
856 PINDEX pos3 = P_MAX_INDEX;
857 if (schemeInfo->hasPassword)
858 pos3 = uphp.Find(':');
859 switch (pos2) {
860 case 0 :
861 uphp.Delete(0, 1);
862 break;
864 case P_MAX_INDEX :
865 if (schemeInfo->defaultToUserIfNoAt) {
866 if (pos3 == P_MAX_INDEX)
867 username = UntranslateString(uphp, LoginTranslation);
868 else {
869 username = UntranslateString(uphp.Left(pos3), LoginTranslation);
870 password = UntranslateString(uphp.Mid(pos3+1), LoginTranslation);
872 uphp.MakeEmpty();
874 break;
876 default :
877 if (pos3 > pos2)
878 username = UntranslateString(uphp.Left(pos2), LoginTranslation);
879 else {
880 username = UntranslateString(uphp.Left(pos3), LoginTranslation);
881 password = UntranslateString(uphp(pos3+1, pos2-1), LoginTranslation);
883 uphp.Delete(0, pos2+1);
887 // if the URL does not have a port, then this is the hostname
888 if (schemeInfo->defaultPort == 0)
889 hostname = UntranslateString(uphp, LoginTranslation);
890 else {
891 // determine if the URL has a port number
892 // Allow for [ipv6] form
893 pos = uphp.Find(']');
894 if (pos == P_MAX_INDEX)
895 pos = 0;
896 pos = uphp.Find(':', pos);
897 if (pos == P_MAX_INDEX)
898 hostname = UntranslateString(uphp, LoginTranslation);
899 else {
900 hostname = UntranslateString(uphp.Left(pos), LoginTranslation);
901 port = (WORD)uphp.Mid(pos+1).AsUnsigned();
902 portSupplied = TRUE;
905 if (hostname.IsEmpty() && schemeInfo->defaultHostToLocal)
906 hostname = PIPSocket::GetHostName();
910 if (schemeInfo->hasQuery) {
911 // chop off any trailing query
912 pos = url.Find('?');
913 if (pos != P_MAX_INDEX) {
914 SplitQueryVars(url(pos+1, P_MAX_INDEX), queryVars);
915 url.Delete(pos, P_MAX_INDEX);
919 if (schemeInfo->hasParameters) {
920 // chop off any trailing parameters
921 pos = url.Find(';');
922 if (pos != P_MAX_INDEX) {
923 SplitVars(url(pos+1, P_MAX_INDEX), paramVars, ';', '=');
924 url.Delete(pos, P_MAX_INDEX);
928 if (schemeInfo->hasFragments) {
929 // chop off any trailing fragment
930 pos = url.Find('#');
931 if (pos != P_MAX_INDEX) {
932 fragment = UntranslateString(url(pos+1, P_MAX_INDEX), PathTranslation);
933 url.Delete(pos, P_MAX_INDEX);
937 if (schemeInfo->hasPath)
938 SetPathStr(url); // the hierarchy is what is left
939 else {
940 // if the rest of the URL isn't a path, then we are finished!
941 pathStr = UntranslateString(url, PathTranslation);
942 Recalculate();
945 if (port == 0 && schemeInfo->defaultPort != 0 && !relativePath) {
946 // Yes another horrible, horrible special case!
947 if (scheme == "h323" && paramVars("type") == "gk")
948 port = DEFAULT_H323RAS_PORT;
949 else
950 port = schemeInfo->defaultPort;
951 Recalculate();
954 return TRUE;
958 PFilePath PURL::AsFilePath() const
960 //if (scheme != SchemeTable[FILE_SCHEME].name)
961 // return PString::Empty();
962 if (scheme != FILE_SCHEME)
963 return PString::Empty();
965 PStringStream str;
967 if (relativePath) {
968 for (PINDEX i = 0; i < path.GetSize(); i++) {
969 if (i > 0)
970 str << PDIR_SEPARATOR;
971 str << path[i];
974 else {
975 if (hostname != "localhost")
976 str << PDIR_SEPARATOR << hostname;
977 for (PINDEX i = 0; i < path.GetSize(); i++)
978 str << PDIR_SEPARATOR << path[i];
981 return str;
985 PString PURL::AsString(UrlFormat fmt) const
987 if (fmt == FullURL)
988 return urlString;
990 if (scheme.IsEmpty())
991 return PString::Empty();
993 //const schemeStruct * schemeInfo = GetSchemeInfo(scheme);
994 //if (schemeInfo == NULL)
995 // schemeInfo = &SchemeTable[PARRAYSIZE(SchemeTable)-1];
996 const PURLScheme * schemeInfo = PFactory<PURLScheme>::CreateInstance(scheme);
997 if (schemeInfo == NULL)
998 schemeInfo = PFactory<PURLScheme>::CreateInstance(DEFAULT_SCHEME);
1000 return schemeInfo->AsString(fmt, *this);
1003 PString PURL::LegacyAsString(PURL::UrlFormat fmt, const PURLLegacyScheme * schemeInfo) const
1005 PStringStream str;
1006 PINDEX i;
1008 if (fmt == HostPortOnly) {
1009 if (schemeInfo->hasHostPort && hostname.IsEmpty())
1010 return str;
1012 str << scheme << ':';
1014 if (relativePath) {
1015 if (schemeInfo->relativeImpliesScheme)
1016 return PString::Empty();
1017 return str;
1020 if (schemeInfo->hasPath && schemeInfo->hasHostPort)
1021 str << "//";
1023 if (schemeInfo->hasUsername) {
1024 if (!username) {
1025 str << TranslateString(username, LoginTranslation);
1026 if (schemeInfo->hasPassword && !password)
1027 str << ':' << TranslateString(password, LoginTranslation);
1028 str << '@';
1032 if (schemeInfo->hasHostPort) {
1033 if (hostname.Find(':') != P_MAX_INDEX)
1034 str << '[' << hostname << ']';
1035 else
1036 str << hostname;
1039 if (schemeInfo->defaultPort != 0) {
1040 if (port != schemeInfo->defaultPort || portSupplied)
1041 str << ':' << port;
1044 return str;
1047 // URIOnly and PathOnly
1048 if (schemeInfo->hasPath) {
1049 for (i = 0; i < path.GetSize(); i++) {
1050 if (i > 0 || !relativePath)
1051 str << '/';
1052 str << TranslateString(path[i], PathTranslation);
1055 else
1056 str << TranslateString(pathStr, PathTranslation);
1058 if (fmt == URIOnly) {
1059 if (!fragment)
1060 str << "#" << TranslateString(fragment, PathTranslation);
1062 for (i = 0; i < paramVars.GetSize(); i++) {
1063 str << ';' << TranslateString(paramVars.GetKeyAt(i), QueryTranslation);
1064 PString data = paramVars.GetDataAt(i);
1065 if (!data)
1066 str << '=' << TranslateString(data, QueryTranslation);
1069 if (!queryVars.IsEmpty())
1070 str << '?' << GetQuery();
1073 return str;
1077 void PURL::SetScheme(const PString & s)
1079 scheme = s;
1080 Recalculate();
1084 void PURL::SetUserName(const PString & u)
1086 username = u;
1087 Recalculate();
1091 void PURL::SetPassword(const PString & p)
1093 password = p;
1094 Recalculate();
1098 void PURL::SetHostName(const PString & h)
1100 hostname = h;
1101 Recalculate();
1105 void PURL::SetPort(WORD newPort)
1107 port = newPort;
1108 Recalculate();
1112 void PURL::SetPathStr(const PString & p)
1114 pathStr = p;
1116 path = pathStr.Tokenise("/", TRUE);
1118 if (path.GetSize() > 0 && path[0].IsEmpty())
1119 path.RemoveAt(0);
1121 for (PINDEX i = 0; i < path.GetSize(); i++) {
1122 path[i] = UntranslateString(path[i], PathTranslation);
1123 if (i > 0 && path[i] == ".." && path[i-1] != "..") {
1124 path.RemoveAt(i--);
1125 path.RemoveAt(i--);
1129 Recalculate();
1133 void PURL::SetPath(const PStringArray & p)
1135 path = p;
1137 pathStr.MakeEmpty();
1138 for (PINDEX i = 0; i < path.GetSize(); i++)
1139 pathStr += '/' + path[i];
1141 Recalculate();
1145 PString PURL::GetParameters() const
1147 PStringStream str;
1149 for (PINDEX i = 0; i < paramVars.GetSize(); i++) {
1150 if (i > 0)
1151 str << ';';
1152 str << paramVars.GetKeyAt(i);
1153 PString data = paramVars.GetDataAt(i);
1154 if (!data)
1155 str << '=' << data;
1158 return str;
1162 void PURL::SetParameters(const PString & parameters)
1164 SplitVars(parameters, paramVars, ';', '=');
1165 Recalculate();
1169 void PURL::SetParamVars(const PStringToString & p)
1171 paramVars = p;
1172 Recalculate();
1176 void PURL::SetParamVar(const PString & key, const PString & data)
1178 if (data.IsEmpty())
1179 paramVars.RemoveAt(key);
1180 else
1181 paramVars.SetAt(key, data);
1182 Recalculate();
1186 PString PURL::GetQuery() const
1188 PStringStream str;
1190 for (PINDEX i = 0; i < queryVars.GetSize(); i++) {
1191 if (i > 0)
1192 str << '&';
1193 str << TranslateString(queryVars.GetKeyAt(i), QueryTranslation)
1194 << '='
1195 << TranslateString(queryVars.GetDataAt(i), QueryTranslation);
1198 return str;
1202 void PURL::SetQuery(const PString & queryStr)
1204 SplitQueryVars(queryStr, queryVars);
1205 Recalculate();
1209 void PURL::SetQueryVars(const PStringToString & q)
1211 queryVars = q;
1212 Recalculate();
1216 void PURL::SetQueryVar(const PString & key, const PString & data)
1218 if (data.IsEmpty())
1219 queryVars.RemoveAt(key);
1220 else
1221 queryVars.SetAt(key, data);
1222 Recalculate();
1226 BOOL PURL::OpenBrowser(const PString & url)
1228 #ifdef WIN32
1229 SHELLEXECUTEINFO sei;
1230 ZeroMemory(&sei, sizeof(SHELLEXECUTEINFO));
1231 sei.cbSize = sizeof(SHELLEXECUTEINFO);
1232 sei.lpVerb = TEXT("open");
1233 #ifndef _WIN32_WCE
1234 sei.lpFile = url;
1235 #else
1236 USES_CONVERSION;
1237 sei.lpFile = A2T(url);
1238 #endif // _WIN32_WCE
1240 if (ShellExecuteEx(&sei) != 0)
1241 return TRUE;
1243 #ifndef _WIN32_WCE
1244 MessageBox(NULL, "Unable to open page"&url, PProcess::Current().GetName(), MB_TASKMODAL);
1245 #else
1246 MessageBox(NULL, _T("Unable to open page"), A2T(PProcess::Current().GetName()), MB_APPLMODAL);
1247 #endif // _WIN32_WCE
1249 #endif // WIN32
1250 return FALSE;
1254 void PURL::Recalculate()
1256 //if (scheme.IsEmpty())
1257 // scheme = SchemeTable[DEFAULT_SCHEME].name;
1258 if (scheme.IsEmpty())
1259 scheme = DEFAULT_SCHEME;
1261 urlString = AsString(HostPortOnly) + AsString(URIOnly);
1265 //////////////////////////////////////////////////////////////////////////////
1266 // PHTTP
1268 static char const * const HTTPCommands[PHTTP::NumCommands] = {
1269 // HTTP 1.0 commands
1270 "GET", "HEAD", "POST",
1272 // HTTP 1.1 commands
1273 "PUT", "DELETE", "TRACE", "OPTIONS",
1275 // HTTPS command
1276 "CONNECT"
1280 const char * const PHTTP::AllowTag = "Allow";
1281 const char * const PHTTP::AuthorizationTag = "Authorization";
1282 const char * const PHTTP::ContentEncodingTag = "Content-Encoding";
1283 const char * const PHTTP::ContentLengthTag = "Content-Length";
1284 const char * const PHTTP::ContentTypeTag = "Content-Type";
1285 const char * const PHTTP::DateTag = "Date";
1286 const char * const PHTTP::ExpiresTag = "Expires";
1287 const char * const PHTTP::FromTag = "From";
1288 const char * const PHTTP::IfModifiedSinceTag = "If-Modified-Since";
1289 const char * const PHTTP::LastModifiedTag = "Last-Modified";
1290 const char * const PHTTP::LocationTag = "Location";
1291 const char * const PHTTP::PragmaTag = "Pragma";
1292 const char * const PHTTP::PragmaNoCacheTag = "no-cache";
1293 const char * const PHTTP::RefererTag = "Referer";
1294 const char * const PHTTP::ServerTag = "Server";
1295 const char * const PHTTP::UserAgentTag = "User-Agent";
1296 const char * const PHTTP::WWWAuthenticateTag = "WWW-Authenticate";
1297 const char * const PHTTP::MIMEVersionTag = "MIME-Version";
1298 const char * const PHTTP::ConnectionTag = "Connection";
1299 const char * const PHTTP::KeepAliveTag = "Keep-Alive";
1300 const char * const PHTTP::TransferEncodingTag= "Transfer-Encoding";
1301 const char * const PHTTP::ChunkedTag = "chunked";
1302 const char * const PHTTP::ProxyConnectionTag = "Proxy-Connection";
1303 const char * const PHTTP::ProxyAuthorizationTag = "Proxy-Authorization";
1304 const char * const PHTTP::ProxyAuthenticateTag = "Proxy-Authenticate";
1305 const char * const PHTTP::ForwardedTag = "Forwarded";
1306 const char * const PHTTP::SetCookieTag = "Set-Cookie";
1307 const char * const PHTTP::CookieTag = "Cookie";
1311 PHTTP::PHTTP()
1312 : PInternetProtocol("www 80", NumCommands, HTTPCommands)
1317 PINDEX PHTTP::ParseResponse(const PString & line)
1319 PINDEX endVer = line.Find(' ');
1320 if (endVer == P_MAX_INDEX) {
1321 lastResponseInfo = "Bad response";
1322 lastResponseCode = PHTTP::InternalServerError;
1323 return 0;
1326 lastResponseInfo = line.Left(endVer);
1327 PINDEX endCode = line.Find(' ', endVer+1);
1328 lastResponseCode = line(endVer+1,endCode-1).AsInteger();
1329 if (lastResponseCode == 0)
1330 lastResponseCode = PHTTP::InternalServerError;
1331 lastResponseInfo &= line.Mid(endCode);
1332 return 0;
1336 // End Of File ///////////////////////////////////////////////////////////////