BRANCH release for 2.5.x critical patches
[baserock-morphs:libxml2.git] / nanohttp.c
1 /*
2  * nanohttp.c: minimalist HTTP GET implementation to fetch external subsets.
3  *             focuses on size, streamability, reentrancy and portability
4  *
5  * This is clearly not a general purpose HTTP implementation
6  * If you look for one, check:
7  *         http://www.w3.org/Library/
8  *
9  * See Copyright for the status of this software.
10  *
11  * daniel@veillard.com
12  */
13  
14 /* TODO add compression support, Send the Accept- , and decompress on the
15         fly with ZLIB if found at compile-time */
16
17 #define NEED_SOCKETS
18 #define IN_LIBXML
19 #include "libxml.h"
20
21 #ifdef LIBXML_HTTP_ENABLED
22 #include <string.h>
23
24 #ifdef HAVE_STDLIB_H
25 #include <stdlib.h>
26 #endif
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30 #ifdef HAVE_SYS_TYPES_H
31 #include <sys/types.h>
32 #endif
33 #ifdef HAVE_SYS_SOCKET_H
34 #include <sys/socket.h>
35 #endif
36 #ifdef HAVE_NETINET_IN_H
37 #include <netinet/in.h>
38 #endif
39 #ifdef HAVE_ARPA_INET_H
40 #include <arpa/inet.h>
41 #endif
42 #ifdef HAVE_NETDB_H
43 #include <netdb.h>
44 #endif
45 #ifdef HAVE_RESOLV_H
46 #ifdef HAVE_ARPA_NAMESER_H
47 #include <arpa/nameser.h>
48 #endif
49 #include <resolv.h>
50 #endif
51 #ifdef HAVE_FCNTL_H
52 #include <fcntl.h> 
53 #endif
54 #ifdef HAVE_ERRNO_H
55 #include <errno.h>
56 #endif
57 #ifdef HAVE_SYS_TIME_H
58 #include <sys/time.h>
59 #endif
60 #ifdef HAVE_SYS_SELECT_H
61 #include <sys/select.h>
62 #endif
63 #ifdef HAVE_STRINGS_H
64 #include <strings.h>
65 #endif
66 #ifdef SUPPORT_IP6
67 #include <resolv.h>
68 #endif
69
70 #ifdef VMS
71 #include <stropts>
72 #define SOCKLEN_T unsigned int
73 #define SOCKET int
74 #endif
75
76
77 #ifdef __MINGW32__
78 #define _WINSOCKAPI_
79 #include <wsockcompat.h>
80 #include <winsock2.h>
81 #undef SOCKLEN_T
82 #define SOCKLEN_T unsigned int
83 #endif
84
85
86 #include <libxml/globals.h>
87 #include <libxml/xmlerror.h>
88 #include <libxml/xmlmemory.h>
89 #include <libxml/parser.h> /* for xmlStr(n)casecmp() */
90 #include <libxml/nanohttp.h>
91 #include <libxml/globals.h>
92 #include <libxml/uri.h>
93
94 /**
95  * A couple portability macros
96  */
97 #ifndef _WINSOCKAPI_
98 #define closesocket(s) close(s)
99 #define SOCKET int
100 #endif
101
102 #ifndef SOCKLEN_T
103 #define SOCKLEN_T unsigned int
104 #endif
105 #ifndef SOCKET
106 #define SOCKET int
107 #endif
108
109 #ifdef STANDALONE
110 #define DEBUG_HTTP
111 #define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n)
112 #define xmlStrcasecmpi(a, b) strcasecmp((char *)a, (char *)b)
113 #endif
114
115 #define XML_NANO_HTTP_MAX_REDIR 10
116
117 #define XML_NANO_HTTP_CHUNK     4096
118
119 #define XML_NANO_HTTP_CLOSED    0
120 #define XML_NANO_HTTP_WRITE     1
121 #define XML_NANO_HTTP_READ      2
122 #define XML_NANO_HTTP_NONE      4
123
124 typedef struct xmlNanoHTTPCtxt {
125     char *protocol;     /* the protocol name */
126     char *hostname;     /* the host name */
127     int port;           /* the port */
128     char *path;         /* the path within the URL */
129     SOCKET fd;          /* the file descriptor for the socket */
130     int state;          /* WRITE / READ / CLOSED */
131     char *out;          /* buffer sent (zero terminated) */
132     char *outptr;       /* index within the buffer sent */
133     char *in;           /* the receiving buffer */
134     char *content;      /* the start of the content */
135     char *inptr;        /* the next byte to read from network */
136     char *inrptr;       /* the next byte to give back to the client */
137     int inlen;          /* len of the input buffer */
138     int last;           /* return code for last operation */
139     int returnValue;    /* the protocol return value */
140     int ContentLength;  /* specified content length from HTTP header */
141     char *contentType;  /* the MIME type for the input */
142     char *location;     /* the new URL in case of redirect */
143     char *authHeader;   /* contents of {WWW,Proxy}-Authenticate header */
144 } xmlNanoHTTPCtxt, *xmlNanoHTTPCtxtPtr;
145
146 static int initialized = 0;
147 static char *proxy = NULL;       /* the proxy name if any */
148 static int proxyPort;   /* the proxy port if any */
149 static unsigned int timeout = 60;/* the select() timeout in seconds */
150
151 int xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len );
152 int xmlNanoHTTPContentLength( void * ctx );
153
154 /**
155  * A portability function
156  */
157 static int socket_errno(void) {
158 #ifdef _WINSOCKAPI_
159     return(WSAGetLastError());
160 #else
161     return(errno);
162 #endif
163 }
164
165 #ifdef SUPPORT_IP6
166 static
167 int have_ipv6(void) {
168     int s;
169
170     s = socket (AF_INET6, SOCK_STREAM, 0);
171     if (s != -1) {
172         close (s);
173         return (1);
174     }
175     return (0);
176 }
177 #endif
178
179 /**
180  * xmlNanoHTTPInit:
181  *
182  * Initialize the HTTP protocol layer.
183  * Currently it just checks for proxy informations
184  */
185
186 void
187 xmlNanoHTTPInit(void) {
188     const char *env;
189 #ifdef _WINSOCKAPI_
190     WSADATA wsaData;    
191 #endif
192
193     if (initialized)
194         return;
195
196 #ifdef _WINSOCKAPI_
197     if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
198         return;
199 #endif
200
201     if (proxy == NULL) {
202         proxyPort = 80;
203         env = getenv("no_proxy");
204         if (env != NULL)
205             goto done;
206         env = getenv("http_proxy");
207         if (env != NULL) {
208             xmlNanoHTTPScanProxy(env);
209             goto done;
210         }
211         env = getenv("HTTP_PROXY");
212         if (env != NULL) {
213             xmlNanoHTTPScanProxy(env);
214             goto done;
215         }
216     }
217 done:
218     initialized = 1;
219 }
220
221 /**
222  * xmlNanoHTTPCleanup:
223  *
224  * Cleanup the HTTP protocol layer.
225  */
226
227 void
228 xmlNanoHTTPCleanup(void) {
229     if (proxy != NULL)
230         xmlFree(proxy);
231 #ifdef _WINSOCKAPI_
232     if (initialized)
233         WSACleanup();
234 #endif
235     initialized = 0;
236     return;
237 }
238
239 /**
240  * xmlNanoHTTPScanURL:
241  * @ctxt:  an HTTP context
242  * @URL:  The URL used to initialize the context
243  *
244  * (Re)Initialize an HTTP context by parsing the URL and finding
245  * the protocol host port and path it indicates.
246  */
247
248 static void
249 xmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt, const char *URL) {
250     const char *cur = URL;
251     char buf[4096];
252     int indx = 0;
253     int port = 0;
254
255     if (ctxt->protocol != NULL) { 
256         xmlFree(ctxt->protocol);
257         ctxt->protocol = NULL;
258     }
259     if (ctxt->hostname != NULL) { 
260         xmlFree(ctxt->hostname);
261         ctxt->hostname = NULL;
262     }
263     if (ctxt->path != NULL) { 
264         xmlFree(ctxt->path);
265         ctxt->path = NULL;
266     }
267     if (URL == NULL) return;
268     buf[indx] = 0;
269     while (*cur != 0) {
270         if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
271             buf[indx] = 0;
272             ctxt->protocol = xmlMemStrdup(buf);
273             indx = 0;
274             cur += 3;
275             break;
276         }
277         buf[indx++] = *cur++;
278     }
279     if (*cur == 0) return;
280
281     buf[indx] = 0;
282     while (1) {
283         if ((strchr (cur, '[') && !strchr (cur, ']')) ||
284                 (!strchr (cur, '[') && strchr (cur, ']'))) {
285             xmlGenericError (xmlGenericErrorContext, "\nxmlNanoHTTPScanURL: %s",
286                     "Syntax Error\n");
287             return;
288         }
289
290         if (cur[0] == '[') {
291             cur++;
292             while (cur[0] != ']')
293                 buf[indx++] = *cur++;
294     
295             if (!strchr (buf, ':')) {
296                 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoHTTPScanURL: %s",
297                         "Use [IPv6]/IPv4 format\n");
298                 return;
299             }
300
301             buf[indx] = 0;
302             ctxt->hostname = xmlMemStrdup (buf);
303             indx = 0;
304             cur += 1;
305             if (cur[0] == ':') {
306                 cur++;
307                 while (*cur >= '0' && *cur <= '9') {
308                     port *= 10;
309                     port += *cur - '0';
310                     cur++;
311                 }
312
313                 if (port != 0) ctxt->port = port;
314                 while ((cur[0] != '/') && (*cur != 0))
315                     cur++;
316             }
317             break;
318         }
319         else {
320             if (cur[0] == ':') {
321                 buf[indx] = 0;
322                 ctxt->hostname = xmlMemStrdup (buf);
323                 indx = 0;
324                 cur += 1;
325                 while ((*cur >= '0') && (*cur <= '9')) {
326                     port *= 10;
327                     port += *cur - '0';
328                     cur++;
329                 }
330                 if (port != 0) ctxt->port = port;
331                 while ((cur[0] != '/') && (*cur != 0)) 
332                     cur++;
333                 break;
334             }
335             if ((*cur == '/') || (*cur == 0)) {
336                 buf[indx] = 0;
337                 ctxt->hostname = xmlMemStrdup (buf);
338                 indx = 0;
339                 break;
340             }
341         }
342         buf[indx++] = *cur++;
343     }
344     if (*cur == 0) 
345         ctxt->path = xmlMemStrdup("/");
346     else {
347         indx = 0;
348         buf[indx] = 0;
349         while (*cur != 0)
350             buf[indx++] = *cur++;
351         buf[indx] = 0;
352         ctxt->path = xmlMemStrdup(buf);
353     }   
354 }
355
356 /**
357  * xmlNanoHTTPScanProxy:
358  * @URL:  The proxy URL used to initialize the proxy context
359  *
360  * (Re)Initialize the HTTP Proxy context by parsing the URL and finding
361  * the protocol host port it indicates.
362  * Should be like http://myproxy/ or http://myproxy:3128/
363  * A NULL URL cleans up proxy informations.
364  */
365
366 void
367 xmlNanoHTTPScanProxy(const char *URL) {
368     const char *cur = URL;
369     char buf[4096];
370     int indx = 0;
371     int port = 0;
372
373     if (proxy != NULL) { 
374         xmlFree(proxy);
375         proxy = NULL;
376     }
377     if (proxyPort != 0) { 
378         proxyPort = 0;
379     }
380 #ifdef DEBUG_HTTP
381     if (URL == NULL)
382         xmlGenericError(xmlGenericErrorContext,
383                 "Removing HTTP proxy info\n");
384     else
385         xmlGenericError(xmlGenericErrorContext,
386                 "Using HTTP proxy %s\n", URL);
387 #endif
388     if (URL == NULL) return;
389     buf[indx] = 0;
390     while (*cur != 0) {
391         if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
392             buf[indx] = 0;
393             indx = 0;
394             cur += 3;
395             break;
396         }
397         buf[indx++] = *cur++;
398     }
399     if (*cur == 0) return;
400
401     buf[indx] = 0;
402     while (1) {
403         if ((strchr (cur, '[') && !strchr (cur, ']')) ||
404                 (!strchr (cur, '[') && strchr (cur, ']'))) {
405             xmlGenericError (xmlGenericErrorContext, "\nxmlNanoHTTPScanProxy: %s",
406                     "Syntax error\n");
407             return;
408         }
409
410         if (cur[0] == '[') {
411             cur++;
412             while (cur[0] != ']')
413                 buf[indx++] = *cur++;
414
415             if (!strchr (buf, ':')) {
416                 xmlGenericError (xmlGenericErrorContext, "\nxmlNanoHTTPScanProxy: %s",
417                         "Use [IPv6]/IPv4 format\n");
418                 return;
419             }
420
421             buf[indx] = 0;
422             proxy = xmlMemStrdup (buf);
423             indx = 0;
424             cur += 1;
425             if (cur[0] == ':') {
426                 cur++;
427                 while (*cur >= '0' && *cur <= '9') {
428                     port *= 10;
429                     port += *cur - '0';
430                     cur++;
431                 }
432
433                 if (port != 0) proxyPort = port;
434                 while ((cur[0] != '/') && (*cur != 0))
435                     cur ++;
436             }
437             break;
438         }
439         else {
440             if (cur[0] == ':') {
441                 buf[indx] = 0;
442                 proxy = xmlMemStrdup (buf);
443                 indx = 0;
444                 cur += 1;
445                 while ((*cur >= '0') && (*cur <= '9')) {
446                     port *= 10;
447                     port += *cur - '0';
448                     cur++;
449                 }
450                 if (port != 0) proxyPort = port;
451                 while ((cur[0] != '/') && (*cur != 0)) 
452                     cur++;
453                 break;
454             }
455             if ((*cur == '/') || (*cur == 0)) {
456                 buf[indx] = 0;
457                 proxy = xmlMemStrdup (buf);
458                 indx = 0;
459                 break;
460             }
461         }
462         buf[indx++] = *cur++;
463     }
464 }
465
466 /**
467  * xmlNanoHTTPNewCtxt:
468  * @URL:  The URL used to initialize the context
469  *
470  * Allocate and initialize a new HTTP context.
471  *
472  * Returns an HTTP context or NULL in case of error.
473  */
474
475 static xmlNanoHTTPCtxtPtr
476 xmlNanoHTTPNewCtxt(const char *URL) {
477     xmlNanoHTTPCtxtPtr ret;
478
479     ret = (xmlNanoHTTPCtxtPtr) xmlMalloc(sizeof(xmlNanoHTTPCtxt));
480     if (ret == NULL) return(NULL);
481
482     memset(ret, 0, sizeof(xmlNanoHTTPCtxt));
483     ret->port = 80;
484     ret->returnValue = 0;
485     ret->fd = -1;
486     ret->ContentLength = -1;
487
488     xmlNanoHTTPScanURL(ret, URL);
489
490     return(ret);
491 }
492
493 /**
494  * xmlNanoHTTPFreeCtxt:
495  * @ctxt:  an HTTP context
496  *
497  * Frees the context after closing the connection.
498  */
499
500 static void
501 xmlNanoHTTPFreeCtxt(xmlNanoHTTPCtxtPtr ctxt) {
502     if (ctxt == NULL) return;
503     if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
504     if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
505     if (ctxt->path != NULL) xmlFree(ctxt->path);
506     if (ctxt->out != NULL) xmlFree(ctxt->out);
507     if (ctxt->in != NULL) xmlFree(ctxt->in);
508     if (ctxt->contentType != NULL) xmlFree(ctxt->contentType);
509     if (ctxt->location != NULL) xmlFree(ctxt->location);
510     if (ctxt->authHeader != NULL) xmlFree(ctxt->authHeader);
511     ctxt->state = XML_NANO_HTTP_NONE;
512     if (ctxt->fd >= 0) closesocket(ctxt->fd);
513     ctxt->fd = -1;
514     xmlFree(ctxt);
515 }
516
517 /**
518  * xmlNanoHTTPSend:
519  * @ctxt:  an HTTP context
520  *
521  * Send the input needed to initiate the processing on the server side
522  * Returns number of bytes sent or -1 on error.
523  */
524
525 static int
526 xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt, const char * xmt_ptr, int outlen) {
527
528     int         total_sent = 0;
529
530     if ( (ctxt->state & XML_NANO_HTTP_WRITE) && (xmt_ptr != NULL ) ) {
531         while (total_sent < outlen) {
532             int nsent = send(ctxt->fd, xmt_ptr + total_sent,
533                                       outlen - total_sent, 0);
534             if (nsent>0)
535                 total_sent += nsent;
536             else if ( ( nsent == -1 ) && 
537 #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
538                       ( socket_errno( ) != EAGAIN ) &&
539 #endif
540                         ( socket_errno( ) != EWOULDBLOCK ) ) {
541                 xmlGenericError( xmlGenericErrorContext,
542                                 "xmlNanoHTTPSend error:  %s",
543                                 strerror( socket_errno( ) ) );
544
545                 if ( total_sent == 0 )
546                     total_sent = -1;
547                 break;
548             }
549             else {
550                 /*
551                 **  No data sent
552                 **  Since non-blocking sockets are used, wait for 
553                 **  socket to be writable or default timeout prior
554                 **  to retrying.
555                 */
556
557                 struct timeval  tv;
558                 fd_set          wfd;
559
560                 tv.tv_sec = timeout;
561                 tv.tv_usec = 0;
562                 FD_ZERO( &wfd );
563                 FD_SET( ctxt->fd, &wfd );
564                 (void)select( ctxt->fd + 1, NULL, &wfd, NULL, &tv );
565             }
566         }
567     }
568
569     return total_sent;
570 }
571
572 /**
573  * xmlNanoHTTPRecv:
574  * @ctxt:  an HTTP context
575  *
576  * Read information coming from the HTTP connection.
577  * This is a blocking call (but it blocks in select(), not read()).
578  *
579  * Returns the number of byte read or -1 in case of error.
580  */
581
582 static int
583 xmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt) {
584     fd_set rfd;
585     struct timeval tv;
586
587
588     while (ctxt->state & XML_NANO_HTTP_READ) {
589         if (ctxt->in == NULL) {
590             ctxt->in = (char *) xmlMallocAtomic(65000 * sizeof(char));
591             if (ctxt->in == NULL) {
592                 ctxt->last = -1;
593                 xmlGenericError( xmlGenericErrorContext, 
594                         "xmlNanoHTTPRecv:  Error allocating input memory." );
595                 return(-1);
596             }
597             ctxt->inlen = 65000;
598             ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in;
599         }
600         if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) {
601             int delta = ctxt->inrptr - ctxt->in;
602             int len = ctxt->inptr - ctxt->inrptr;
603             
604             memmove(ctxt->in, ctxt->inrptr, len);
605             ctxt->inrptr -= delta;
606             ctxt->content -= delta;
607             ctxt->inptr -= delta;
608         }
609         if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) {
610             int d_inptr = ctxt->inptr - ctxt->in;
611             int d_content = ctxt->content - ctxt->in;
612             int d_inrptr = ctxt->inrptr - ctxt->in;
613             char *      tmp_ptr = ctxt->in;
614
615             ctxt->inlen *= 2;
616             ctxt->in = (char *) xmlRealloc(tmp_ptr, ctxt->inlen);
617             if (ctxt->in == NULL) {
618                 xmlGenericError( xmlGenericErrorContext,
619                                 "xmlNanoHTTPRecv:  %s %d bytes.",
620                                 "Failed to realloc input buffer to",
621                                 ctxt->inlen );
622                 xmlFree( tmp_ptr );
623                 ctxt->last = -1;
624                 return(-1);
625             }
626             ctxt->inptr = ctxt->in + d_inptr;
627             ctxt->content = ctxt->in + d_content;
628             ctxt->inrptr = ctxt->in + d_inrptr;
629         }
630         ctxt->last = recv(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK, 0);
631         if (ctxt->last > 0) {
632             ctxt->inptr += ctxt->last;
633             return(ctxt->last);
634         }
635         if (ctxt->last == 0) {
636             return(0);
637         }
638         if (ctxt->last == -1) {
639             switch (socket_errno()) {
640                 case EINPROGRESS:
641                 case EWOULDBLOCK:
642 #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
643                 case EAGAIN:
644 #endif
645                     break;
646
647                 case ECONNRESET:
648                 case ESHUTDOWN:
649                     return ( 0 );
650
651                 default:
652                     xmlGenericError( xmlGenericErrorContext,
653                                 "xmlNanoHTTPRecv:  recv( ) failure - %s",
654                                 strerror( socket_errno( ) ) );
655                     return(-1);
656             }
657         }
658
659         tv.tv_sec = timeout;
660         tv.tv_usec = 0;
661         FD_ZERO(&rfd);
662         FD_SET(ctxt->fd, &rfd);
663         
664         if ( (select(ctxt->fd+1, &rfd, NULL, NULL, &tv)<1)
665 #if defined(EINTR)
666                 && (errno != EINTR)
667 #endif
668         )
669                 return(0);
670     }
671     return(0);
672 }
673
674 /**
675  * xmlNanoHTTPReadLine:
676  * @ctxt:  an HTTP context
677  *
678  * Read one line in the HTTP server output, usually for extracting
679  * the HTTP protocol informations from the answer header.
680  *
681  * Returns a newly allocated string with a copy of the line, or NULL
682  *         which indicate the end of the input.
683  */
684
685 static char *
686 xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt) {
687     char buf[4096];
688     char *bp = buf;
689     int rc;
690     
691     while (bp - buf < 4095) {
692         if (ctxt->inrptr == ctxt->inptr) {
693             if ( (rc = xmlNanoHTTPRecv(ctxt)) == 0) {
694                 if (bp == buf)
695                     return(NULL);
696                 else
697                     *bp = 0;
698                 return(xmlMemStrdup(buf));
699             }
700             else if ( rc == -1 ) {
701                 return ( NULL );
702             }
703         }
704         *bp = *ctxt->inrptr++;
705         if (*bp == '\n') {
706             *bp = 0;
707             return(xmlMemStrdup(buf));
708         }
709         if (*bp != '\r')
710             bp++;
711     }
712     buf[4095] = 0;
713     return(xmlMemStrdup(buf));
714 }
715
716
717 /**
718  * xmlNanoHTTPScanAnswer:
719  * @ctxt:  an HTTP context
720  * @line:  an HTTP header line
721  *
722  * Try to extract useful informations from the server answer.
723  * We currently parse and process:
724  *  - The HTTP revision/ return code
725  *  - The Content-Type
726  *  - The Location for redirect processing.
727  *
728  * Returns -1 in case of failure, the file descriptor number otherwise
729  */
730
731 static void
732 xmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt, const char *line) {
733     const char *cur = line;
734
735     if (line == NULL) return;
736
737     if (!strncmp(line, "HTTP/", 5)) {
738         int version = 0;
739         int ret = 0;
740
741         cur += 5;
742         while ((*cur >= '0') && (*cur <= '9')) {
743             version *= 10;
744             version += *cur - '0';
745             cur++;
746         }
747         if (*cur == '.') {
748             cur++;
749             if ((*cur >= '0') && (*cur <= '9')) {
750                 version *= 10;
751                 version += *cur - '0';
752                 cur++;
753             }
754             while ((*cur >= '0') && (*cur <= '9'))
755                 cur++;
756         } else
757             version *= 10;
758         if ((*cur != ' ') && (*cur != '\t')) return;
759         while ((*cur == ' ') || (*cur == '\t')) cur++;
760         if ((*cur < '0') || (*cur > '9')) return;
761         while ((*cur >= '0') && (*cur <= '9')) {
762             ret *= 10;
763             ret += *cur - '0';
764             cur++;
765         }
766         if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return;
767         ctxt->returnValue = ret;
768     } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Content-Type:", 13)) {
769         cur += 13;
770         while ((*cur == ' ') || (*cur == '\t')) cur++;
771         if (ctxt->contentType != NULL)
772             xmlFree(ctxt->contentType);
773         ctxt->contentType = xmlMemStrdup(cur);
774     } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"ContentType:", 12)) {
775         cur += 12;
776         if (ctxt->contentType != NULL) return;
777         while ((*cur == ' ') || (*cur == '\t')) cur++;
778         ctxt->contentType = xmlMemStrdup(cur);
779     } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Location:", 9)) {
780         cur += 9;
781         while ((*cur == ' ') || (*cur == '\t')) cur++;
782         if (ctxt->location != NULL)
783             xmlFree(ctxt->location);
784         ctxt->location = xmlMemStrdup(cur);
785     } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"WWW-Authenticate:", 17)) {
786         cur += 17;
787         while ((*cur == ' ') || (*cur == '\t')) cur++;
788         if (ctxt->authHeader != NULL)
789             xmlFree(ctxt->authHeader);
790         ctxt->authHeader = xmlMemStrdup(cur);
791     } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Proxy-Authenticate:", 19)) {
792         cur += 19;
793         while ((*cur == ' ') || (*cur == '\t')) cur++;
794         if (ctxt->authHeader != NULL)
795             xmlFree(ctxt->authHeader);
796         ctxt->authHeader = xmlMemStrdup(cur);
797     } else if ( !xmlStrncasecmp( BAD_CAST line, BAD_CAST"Content-Length:", 15) ) {
798         cur += 15;
799         ctxt->ContentLength = strtol( cur, NULL, 10 );
800     }
801 }
802
803 /**
804  * xmlNanoHTTPConnectAttempt:
805  * @addr:  a socket address structure
806  *
807  * Attempt a connection to the given IP:port endpoint. It forces
808  * non-blocking semantic on the socket, and allow 60 seconds for
809  * the host to answer.
810  *
811  * Returns -1 in case of failure, the file descriptor number otherwise
812  */
813
814 static int
815 xmlNanoHTTPConnectAttempt(struct sockaddr *addr)
816 {
817     fd_set wfd;
818     struct timeval tv;
819     int status;
820     int addrlen;
821     SOCKET s;
822     
823 #ifdef SUPPORT_IP6
824     if (addr->sa_family == AF_INET6) {
825         s = socket (PF_INET6, SOCK_STREAM, IPPROTO_TCP);
826         addrlen = sizeof (struct sockaddr_in6);
827     }
828     else
829 #endif
830     {
831         s = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
832         addrlen = sizeof (struct sockaddr_in);
833     }
834     if (s==-1) {
835 #ifdef DEBUG_HTTP
836         perror("socket");
837 #endif
838         xmlGenericError( xmlGenericErrorContext,
839                         "xmlNanoHTTPConnectAttempt: %s - %s",
840                         "socket creation failure",
841                         strerror( socket_errno( ) ) );
842         return(-1);
843     }
844     
845 #ifdef _WINSOCKAPI_
846     {
847         u_long one = 1;
848
849         status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0;
850     }
851 #else /* _WINSOCKAPI_ */
852 #if defined(VMS)
853     {
854         int enable = 1;
855         status = ioctl(s, FIONBIO, &enable);
856     }
857 #else /* VMS */
858     if ((status = fcntl(s, F_GETFL, 0)) != -1) {
859 #ifdef O_NONBLOCK
860         status |= O_NONBLOCK;
861 #else /* O_NONBLOCK */
862 #ifdef F_NDELAY
863         status |= F_NDELAY;
864 #endif /* F_NDELAY */
865 #endif /* !O_NONBLOCK */
866         status = fcntl(s, F_SETFL, status);
867     }
868     if (status < 0) {
869 #ifdef DEBUG_HTTP
870         perror("nonblocking");
871 #endif
872         xmlGenericError( xmlGenericErrorContext,
873                         "xmlNanoHTTPConnectAttempt:  %s - %s",
874                         "error setting non-blocking IO",
875                         strerror( socket_errno( ) ) );
876         closesocket(s);
877         return(-1);
878     }
879 #endif /* !VMS */
880 #endif /* !_WINSOCKAPI_ */
881
882     if (connect (s, addr, addrlen) == -1) {
883         switch (socket_errno()) {
884             case EINPROGRESS:
885             case EWOULDBLOCK:
886                 break;
887             default:
888                 xmlGenericError( xmlGenericErrorContext,
889                                 "xmlNanoHTTPConnectAttempt:  %s - %s",
890                                 "error connecting to HTTP server",
891                                 strerror( socket_errno( ) ) );
892                 closesocket(s);
893                 return(-1);
894         }
895     }   
896     
897     tv.tv_sec = timeout;
898     tv.tv_usec = 0;
899     
900     FD_ZERO(&wfd);
901     FD_SET(s, &wfd);
902     
903     switch(select(s+1, NULL, &wfd, NULL, &tv))
904     {
905         case 0:
906             /* Time out */
907             xmlGenericError( xmlGenericErrorContext, 
908                                 "xmlNanoHTTPConnectAttempt: %s",
909                                 "Connect attempt timed out." );
910             closesocket(s);
911             return(-1);
912         case -1:
913             /* Ermm.. ?? */
914             xmlGenericError( xmlGenericErrorContext,
915                                 "xmlNanoHTTPConnectAttempt: %s - %s",
916                                 "Error connecting to host",
917                                 strerror( socket_errno( ) ) );
918             closesocket(s);
919             return(-1);
920     }
921
922     if ( FD_ISSET(s, &wfd) ) {
923         SOCKLEN_T len;
924         len = sizeof(status);
925         if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&status, &len) < 0 ) {
926             /* Solaris error code */
927             xmlGenericError( xmlGenericErrorContext,
928                                 "xmlNanoHTTPConnectAttempt: %s - %s",
929                                 "Error retrieving pending socket errors",
930                                 strerror( socket_errno( ) ) );
931             return (-1);
932         }
933         if ( status ) {
934             closesocket(s);
935             errno = status;
936             xmlGenericError( xmlGenericErrorContext,
937                                 "xmlNanoHTTPConnectAttempt: %s - %s",
938                                 "Error connecting to remote host",
939                                 strerror( status ) );
940             return (-1);
941         }
942     } else {
943         /* pbm */
944         xmlGenericError( xmlGenericErrorContext,
945                 "xmlNanoHTTPConnectAttempt:  %s\n",
946                 "Select returned, but descriptor not set for connection.\n" );
947         closesocket(s);
948         return (-1);
949     }
950     
951     return(s);
952 }
953  
954 /**
955  * xmlNanoHTTPConnectHost:
956  * @host:  the host name
957  * @port:  the port number
958  *
959  * Attempt a connection to the given host:port endpoint. It tries
960  * the multiple IP provided by the DNS if available.
961  *
962  * Returns -1 in case of failure, the file descriptor number otherwise
963  */
964
965 static int
966 xmlNanoHTTPConnectHost(const char *host, int port)
967 {
968     struct hostent *h;
969     struct sockaddr *addr = NULL;
970     struct in_addr ia;
971     struct sockaddr_in sockin;
972
973 #ifdef SUPPORT_IP6
974     struct in6_addr ia6;
975     struct sockaddr_in6 sockin6;
976 #endif
977     int i;
978     int s;
979
980     memset (&sockin, 0, sizeof(sockin));
981 #ifdef SUPPORT_IP6
982     memset (&sockin6, 0, sizeof(sockin6));
983     if (have_ipv6 ())
984 #if !defined(HAVE_GETADDRINFO) && defined(RES_USE_INET6)
985     {
986         if (!(_res.options & RES_INIT))
987             res_init();
988         _res.options |= RES_USE_INET6;
989     }
990 #elif defined(HAVE_GETADDRINFO)
991     {
992         int status;
993         struct addrinfo hints, *res, *result;
994
995         result = NULL;
996         memset (&hints, 0,sizeof(hints));
997         hints.ai_socktype = SOCK_STREAM;
998
999         status = getaddrinfo (host, NULL, &hints, &result);
1000         if (status) {
1001             xmlGenericError (xmlGenericErrorContext,
1002                     "xmlNanoHTTPConnectHost:  %s '%s' - %s",
1003                     "Failed to resolve host", host, gai_strerror (status));
1004
1005             return (-1);
1006         }
1007
1008         for (res = result; res; res = res->ai_next) {
1009             if (res->ai_family == AF_INET || res->ai_family == AF_INET6) {
1010                 if (res->ai_family == AF_INET6) {
1011                     memcpy (&sockin6, res->ai_addr, res->ai_addrlen);
1012                     sockin6.sin6_port = htons (port);
1013                     addr = (struct sockaddr *)&sockin6;
1014                 }
1015                 else {
1016                     memcpy (&sockin, res->ai_addr, res->ai_addrlen);
1017                     sockin.sin_port = htons (port);
1018                     addr = (struct sockaddr *)&sockin;
1019                 }
1020
1021                 s = xmlNanoHTTPConnectAttempt (addr);
1022                 if (s != -1) {
1023                     freeaddrinfo (result);
1024                     return (s);
1025                 }
1026             }
1027         }
1028         if (result)
1029             freeaddrinfo (result);
1030         return (-1);
1031     } else
1032 #endif
1033 #endif
1034     {   
1035         h = gethostbyname (host);
1036         if (h == NULL) {
1037
1038 /*
1039  * Okay, I got fed up by the non-portability of this error message
1040  * extraction code. it work on Linux, if it work on your platform
1041  * and one want to enable it, send me the defined(foobar) needed
1042  */
1043 #if defined(HAVE_NETDB_H) && defined(HOST_NOT_FOUND) && defined(linux)
1044             const char *h_err_txt = "";
1045
1046             switch (h_errno) {
1047                 case HOST_NOT_FOUND:
1048                     h_err_txt = "Authoritive host not found";
1049                     break;
1050
1051                 case TRY_AGAIN:
1052                     h_err_txt =
1053                         "Non-authoritive host not found or server failure.";
1054                     break;
1055
1056                 case NO_RECOVERY:
1057                     h_err_txt =
1058                         "Non-recoverable errors:  FORMERR, REFUSED, or NOTIMP.";
1059                     break;
1060
1061                 case NO_ADDRESS:
1062                     h_err_txt =
1063                         "Valid name, no data record of requested type.";
1064                     break;
1065
1066                 default:
1067                     h_err_txt = "No error text defined.";
1068                     break;
1069             }
1070             xmlGenericError (xmlGenericErrorContext,
1071                 "xmlNanoHTTPConnectHost:  %s '%s' - %s",
1072                 "Failed to resolve host", host, h_err_txt);
1073 #else
1074             xmlGenericError (xmlGenericErrorContext,
1075                 "xmlNanoHTTPConnectHost:  %s '%s'",
1076                 "Failed to resolve host", host);
1077 #endif
1078             return (-1);
1079         }
1080
1081         for (i = 0; h->h_addr_list[i]; i++) {
1082             if (h->h_addrtype == AF_INET) {
1083                 /* A records (IPv4) */
1084                 memcpy (&ia, h->h_addr_list[i], h->h_length);
1085                 sockin.sin_family = h->h_addrtype;
1086                 sockin.sin_addr = ia;
1087                 sockin.sin_port = htons (port);
1088                 addr = (struct sockaddr *) &sockin;
1089 #ifdef SUPPORT_IP6
1090             } else if (have_ipv6 () && (h->h_addrtype == AF_INET6)) {
1091                 /* AAAA records (IPv6) */
1092                 memcpy (&ia6, h->h_addr_list[i], h->h_length);
1093                 sockin6.sin6_family = h->h_addrtype;
1094                 sockin6.sin6_addr = ia6;
1095                 sockin6.sin6_port = htons (port);
1096                 addr = (struct sockaddr *) &sockin6;
1097 #endif
1098             } else
1099                 break;              /* for */
1100
1101             s = xmlNanoHTTPConnectAttempt (addr);
1102             if (s != -1)
1103                 return (s);
1104         }
1105     }
1106 #ifdef DEBUG_HTTP
1107     xmlGenericError(xmlGenericErrorContext,
1108                     "xmlNanoHTTPConnectHost:  unable to connect to '%s'.\n",
1109                     host);
1110 #endif
1111     return (-1);
1112 }
1113
1114
1115 /**
1116  * xmlNanoHTTPOpen:
1117  * @URL:  The URL to load
1118  * @contentType:  if available the Content-Type information will be
1119  *                returned at that location
1120  *
1121  * This function try to open a connection to the indicated resource
1122  * via HTTP GET.
1123  *
1124  * Returns NULL in case of failure, otherwise a request handler.
1125  *     The contentType, if provided must be freed by the caller
1126  */
1127
1128 void*
1129 xmlNanoHTTPOpen(const char *URL, char **contentType) {
1130     if (contentType != NULL) *contentType = NULL;
1131     return(xmlNanoHTTPMethod(URL, NULL, NULL, contentType, NULL, 0));
1132 }
1133
1134 /**
1135  * xmlNanoHTTPOpenRedir:
1136  * @URL:  The URL to load
1137  * @contentType:  if available the Content-Type information will be
1138  *                returned at that location
1139  * @redir: if available the redirected URL will be returned
1140  *
1141  * This function try to open a connection to the indicated resource
1142  * via HTTP GET.
1143  *
1144  * Returns NULL in case of failure, otherwise a request handler.
1145  *     The contentType, if provided must be freed by the caller
1146  */
1147
1148 void*
1149 xmlNanoHTTPOpenRedir(const char *URL, char **contentType, char **redir) {
1150     if (contentType != NULL) *contentType = NULL;
1151     if (redir != NULL) *redir = NULL;
1152     return(xmlNanoHTTPMethodRedir(URL, NULL, NULL, contentType, redir, NULL,0));
1153 }
1154
1155 /**
1156  * xmlNanoHTTPRead:
1157  * @ctx:  the HTTP context
1158  * @dest:  a buffer
1159  * @len:  the buffer length
1160  *
1161  * This function tries to read @len bytes from the existing HTTP connection
1162  * and saves them in @dest. This is a blocking call.
1163  *
1164  * Returns the number of byte read. 0 is an indication of an end of connection.
1165  *         -1 indicates a parameter error.
1166  */
1167 int
1168 xmlNanoHTTPRead(void *ctx, void *dest, int len) {
1169     xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1170
1171     if (ctx == NULL) return(-1);
1172     if (dest == NULL) return(-1);
1173     if (len <= 0) return(0);
1174
1175     while (ctxt->inptr - ctxt->inrptr < len) {
1176         if (xmlNanoHTTPRecv(ctxt) <= 0) break;
1177     }
1178     if (ctxt->inptr - ctxt->inrptr < len)
1179         len = ctxt->inptr - ctxt->inrptr;
1180     memcpy(dest, ctxt->inrptr, len);
1181     ctxt->inrptr += len;
1182     return(len);
1183 }
1184
1185 /**
1186  * xmlNanoHTTPClose:
1187  * @ctx:  the HTTP context
1188  *
1189  * This function closes an HTTP context, it ends up the connection and
1190  * free all data related to it.
1191  */
1192 void
1193 xmlNanoHTTPClose(void *ctx) {
1194     xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1195
1196     if (ctx == NULL) return;
1197
1198     xmlNanoHTTPFreeCtxt(ctxt);
1199 }
1200
1201 /**
1202  * xmlNanoHTTPMethodRedir:
1203  * @URL:  The URL to load
1204  * @method:  the HTTP method to use
1205  * @input:  the input string if any
1206  * @contentType:  the Content-Type information IN and OUT
1207  * @redir:  the redirected URL OUT
1208  * @headers:  the extra headers
1209  * @ilen:  input length
1210  *
1211  * This function try to open a connection to the indicated resource
1212  * via HTTP using the given @method, adding the given extra headers
1213  * and the input buffer for the request content.
1214  *
1215  * Returns NULL in case of failure, otherwise a request handler.
1216  *     The contentType, or redir, if provided must be freed by the caller
1217  */
1218
1219 void*
1220 xmlNanoHTTPMethodRedir(const char *URL, const char *method, const char *input,
1221                   char **contentType, char **redir,
1222                   const char *headers, int ilen ) {
1223     xmlNanoHTTPCtxtPtr ctxt;
1224     char *bp, *p;
1225     int blen, ret;
1226     int head;
1227     int nbRedirects = 0;
1228     char *redirURL = NULL;
1229 #ifdef DEBUG_HTTP
1230     int xmt_bytes;
1231 #endif
1232     
1233     if (URL == NULL) return(NULL);
1234     if (method == NULL) method = "GET";
1235     xmlNanoHTTPInit();
1236
1237 retry:
1238     if (redirURL == NULL)
1239         ctxt = xmlNanoHTTPNewCtxt(URL);
1240     else {
1241         ctxt = xmlNanoHTTPNewCtxt(redirURL);
1242     }
1243
1244     if ( ctxt == NULL ) {
1245         xmlGenericError( xmlGenericErrorContext,
1246                         "xmlNanoHTTPMethodRedir:  %s %s.",
1247                         "Unable to allocate HTTP context to URI",
1248                         ( ( redirURL == NULL ) ? URL : redirURL ) );
1249         return ( NULL );
1250     }
1251
1252     if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
1253         xmlGenericError( xmlGenericErrorContext,
1254                         "xmlNanoHTTPMethodRedir:  %s - %s.",
1255                         "Not a valid HTTP URI",
1256                         ( ( redirURL == NULL ) ? URL : redirURL ) );
1257         xmlNanoHTTPFreeCtxt(ctxt);
1258         if (redirURL != NULL) xmlFree(redirURL);
1259         return(NULL);
1260     }
1261     if (ctxt->hostname == NULL) {
1262         xmlGenericError( xmlGenericErrorContext,
1263                         "xmlNanoHTTPMethodRedir:  %s - %s",
1264                         "Failed to identify host in URI",
1265                         ( ( redirURL == NULL ) ? URL : redirURL ) );
1266         xmlNanoHTTPFreeCtxt(ctxt);
1267         if (redirURL != NULL) xmlFree(redirURL);
1268         return(NULL);
1269     }
1270     if (proxy) {
1271         blen = strlen(ctxt->hostname) * 2 + 16;
1272         ret = xmlNanoHTTPConnectHost(proxy, proxyPort);
1273     }
1274     else {
1275         blen = strlen(ctxt->hostname);
1276         ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
1277     }
1278     if (ret < 0) {
1279         xmlNanoHTTPFreeCtxt(ctxt);
1280         if (redirURL != NULL) xmlFree(redirURL);
1281         return(NULL);
1282     }
1283     ctxt->fd = ret;
1284
1285     if (input == NULL)
1286         ilen = 0;
1287     else
1288         blen += 36;
1289
1290     if (headers != NULL)
1291         blen += strlen(headers) + 2;
1292     if (contentType && *contentType)
1293         blen += strlen(*contentType) + 16;
1294     blen += strlen(method) + strlen(ctxt->path) + 24;
1295     bp = xmlMallocAtomic(blen);
1296     if ( bp == NULL ) {
1297         xmlNanoHTTPFreeCtxt( ctxt );
1298         xmlGenericError( xmlGenericErrorContext,
1299                         "xmlNanoHTTPMethodRedir:  %s",
1300                         "Error allocating HTTP header buffer." );
1301         return ( NULL );
1302     }
1303
1304     p = bp;
1305
1306     if (proxy) {
1307         if (ctxt->port != 80) {
1308             p += snprintf( p, blen - (p - bp), "%s http://%s:%d%s", 
1309                         method, ctxt->hostname,
1310                         ctxt->port, ctxt->path );
1311         }
1312         else 
1313             p += snprintf( p, blen - (p - bp), "%s http://%s%s", method,
1314                         ctxt->hostname, ctxt->path);
1315     }
1316     else
1317         p += snprintf( p, blen - (p - bp), "%s %s", method, ctxt->path);
1318
1319     p += snprintf( p, blen - (p - bp), " HTTP/1.0\r\nHost: %s\r\n", 
1320                     ctxt->hostname);
1321
1322     if (contentType != NULL && *contentType) 
1323         p += snprintf(p, blen - (p - bp), "Content-Type: %s\r\n", *contentType);
1324
1325     if (headers != NULL)
1326         p += snprintf( p, blen - (p - bp), "%s", headers );
1327
1328     if (input != NULL)
1329         snprintf(p, blen - (p - bp), "Content-Length: %d\r\n\r\n", ilen );
1330     else
1331         snprintf(p, blen - (p - bp), "\r\n");
1332
1333 #ifdef DEBUG_HTTP
1334     xmlGenericError(xmlGenericErrorContext,
1335             "-> %s%s", proxy? "(Proxy) " : "", bp);
1336     if ((blen -= strlen(bp)+1) < 0)
1337         xmlGenericError(xmlGenericErrorContext,
1338                 "ERROR: overflowed buffer by %d bytes\n", -blen);
1339 #endif
1340     ctxt->outptr = ctxt->out = bp;
1341     ctxt->state = XML_NANO_HTTP_WRITE;
1342     blen = strlen( ctxt->out );
1343 #ifdef DEBUG_HTTP
1344     xmt_bytes = xmlNanoHTTPSend(ctxt, ctxt->out, blen );
1345     if ( xmt_bytes != blen )
1346         xmlGenericError( xmlGenericErrorContext,
1347                         "xmlNanoHTTPMethodRedir:  Only %d of %d %s %s\n",
1348                         xmt_bytes, blen,
1349                         "bytes of HTTP headers sent to host",
1350                         ctxt->hostname );
1351 #else
1352     xmlNanoHTTPSend(ctxt, ctxt->out, blen );
1353 #endif
1354
1355     if ( input != NULL ) {
1356 #ifdef DEBUG_HTTP
1357         xmt_bytes = xmlNanoHTTPSend( ctxt, input, ilen );
1358
1359         if ( xmt_bytes != ilen )
1360             xmlGenericError( xmlGenericErrorContext,
1361                         "xmlNanoHTTPMethodRedir:  Only %d of %d %s %s\n",
1362                         xmt_bytes, ilen,
1363                         "bytes of HTTP content sent to host",
1364                         ctxt->hostname );
1365 #else
1366         xmlNanoHTTPSend( ctxt, input, ilen );
1367 #endif
1368     }
1369
1370     ctxt->state = XML_NANO_HTTP_READ;
1371     head = 1;
1372
1373     while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
1374         if (head && (*p == 0)) {
1375             head = 0;
1376             ctxt->content = ctxt->inrptr;
1377             xmlFree(p);
1378             break;
1379         }
1380         xmlNanoHTTPScanAnswer(ctxt, p);
1381
1382 #ifdef DEBUG_HTTP
1383         xmlGenericError(xmlGenericErrorContext, "<- %s\n", p);
1384 #endif
1385         xmlFree(p);
1386     }
1387
1388     if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
1389         (ctxt->returnValue < 400)) {
1390 #ifdef DEBUG_HTTP
1391         xmlGenericError(xmlGenericErrorContext,
1392                 "\nRedirect to: %s\n", ctxt->location);
1393 #endif
1394         while ( xmlNanoHTTPRecv(ctxt) > 0 ) ;
1395         if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
1396             nbRedirects++;
1397             if (redirURL != NULL)
1398                 xmlFree(redirURL);
1399             redirURL = xmlMemStrdup(ctxt->location);
1400             xmlNanoHTTPFreeCtxt(ctxt);
1401             goto retry;
1402         }
1403         xmlNanoHTTPFreeCtxt(ctxt);
1404         if (redirURL != NULL) xmlFree(redirURL);
1405 #ifdef DEBUG_HTTP
1406         xmlGenericError(xmlGenericErrorContext,
1407                 "xmlNanoHTTPMethodRedir: Too many redirects, aborting ...\n");
1408 #endif
1409         return(NULL);
1410     }
1411
1412     if (contentType != NULL) {
1413         if (ctxt->contentType != NULL)
1414             *contentType = xmlMemStrdup(ctxt->contentType);
1415         else
1416             *contentType = NULL;
1417     }
1418
1419     if ((redir != NULL) && (redirURL != NULL)) {
1420         *redir = redirURL;
1421     } else {
1422         if (redirURL != NULL)
1423             xmlFree(redirURL);
1424         if (redir != NULL)
1425             *redir = NULL;
1426     }
1427
1428 #ifdef DEBUG_HTTP
1429     if (ctxt->contentType != NULL)
1430         xmlGenericError(xmlGenericErrorContext,
1431                 "\nCode %d, content-type '%s'\n\n",
1432                ctxt->returnValue, ctxt->contentType);
1433     else
1434         xmlGenericError(xmlGenericErrorContext,
1435                 "\nCode %d, no content-type\n\n",
1436                ctxt->returnValue);
1437 #endif
1438
1439     return((void *) ctxt);
1440 }
1441
1442 /**
1443  * xmlNanoHTTPMethod:
1444  * @URL:  The URL to load
1445  * @method:  the HTTP method to use
1446  * @input:  the input string if any
1447  * @contentType:  the Content-Type information IN and OUT
1448  * @headers:  the extra headers
1449  * @ilen:  input length
1450  *
1451  * This function try to open a connection to the indicated resource
1452  * via HTTP using the given @method, adding the given extra headers
1453  * and the input buffer for the request content.
1454  *
1455  * Returns NULL in case of failure, otherwise a request handler.
1456  *     The contentType, if provided must be freed by the caller
1457  */
1458
1459 void*
1460 xmlNanoHTTPMethod(const char *URL, const char *method, const char *input,
1461                   char **contentType, const char *headers, int ilen) {
1462     return(xmlNanoHTTPMethodRedir(URL, method, input, contentType,
1463                                   NULL, headers, ilen));
1464 }
1465
1466 /**
1467  * xmlNanoHTTPFetch:
1468  * @URL:  The URL to load
1469  * @filename:  the filename where the content should be saved
1470  * @contentType:  if available the Content-Type information will be
1471  *                returned at that location
1472  *
1473  * This function try to fetch the indicated resource via HTTP GET
1474  * and save it's content in the file.
1475  *
1476  * Returns -1 in case of failure, 0 incase of success. The contentType,
1477  *     if provided must be freed by the caller
1478  */
1479 int
1480 xmlNanoHTTPFetch(const char *URL, const char *filename, char **contentType) {
1481     void *ctxt = NULL;
1482     char *buf = NULL;
1483     int fd;
1484     int len;
1485     
1486     ctxt = xmlNanoHTTPOpen(URL, contentType);
1487     if (ctxt == NULL) return(-1);
1488
1489     if (!strcmp(filename, "-")) 
1490         fd = 0;
1491     else {
1492         fd = open(filename, O_CREAT | O_WRONLY, 00644);
1493         if (fd < 0) {
1494             xmlNanoHTTPClose(ctxt);
1495             if ((contentType != NULL) && (*contentType != NULL)) {
1496                 xmlFree(*contentType);
1497                 *contentType = NULL;
1498             }
1499             return(-1);
1500         }
1501     }
1502
1503     xmlNanoHTTPFetchContent( ctxt, &buf, &len );
1504     if ( len > 0 ) {
1505         write(fd, buf, len);
1506     }
1507
1508     xmlNanoHTTPClose(ctxt);
1509     close(fd);
1510     return(0);
1511 }
1512
1513 /**
1514  * xmlNanoHTTPSave:
1515  * @ctxt:  the HTTP context
1516  * @filename:  the filename where the content should be saved
1517  *
1518  * This function saves the output of the HTTP transaction to a file
1519  * It closes and free the context at the end
1520  *
1521  * Returns -1 in case of failure, 0 incase of success.
1522  */
1523 int
1524 xmlNanoHTTPSave(void *ctxt, const char *filename) {
1525     char *buf = NULL;
1526     int fd;
1527     int len;
1528     
1529     if (ctxt == NULL) return(-1);
1530
1531     if (!strcmp(filename, "-")) 
1532         fd = 0;
1533     else {
1534         fd = open(filename, O_CREAT | O_WRONLY);
1535         if (fd < 0) {
1536             xmlNanoHTTPClose(ctxt);
1537             return(-1);
1538         }
1539     }
1540
1541     xmlNanoHTTPFetchContent( ctxt, &buf, &len );
1542     if ( len > 0 ) {
1543         write(fd, buf, len);
1544     }
1545
1546     xmlNanoHTTPClose(ctxt);
1547     return(0);
1548 }
1549
1550 /**
1551  * xmlNanoHTTPReturnCode:
1552  * @ctx:  the HTTP context
1553  *
1554  * Get the latest HTTP return code received
1555  *
1556  * Returns the HTTP return code for the request.
1557  */
1558 int
1559 xmlNanoHTTPReturnCode(void *ctx) {
1560     xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1561
1562     if (ctxt == NULL) return(-1);
1563
1564     return(ctxt->returnValue);
1565 }
1566
1567 /**
1568  * xmlNanoHTTPAuthHeader:
1569  * @ctx:  the HTTP context
1570  *
1571  * Get the authentication header of an HTTP context
1572  *
1573  * Returns the stashed value of the WWW-Authenticate or Proxy-Authenticate
1574  * header.
1575  */
1576 const char *
1577 xmlNanoHTTPAuthHeader(void *ctx) {
1578     xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1579
1580     if (ctxt == NULL) return(NULL);
1581
1582     return(ctxt->authHeader);
1583 }
1584
1585 /**
1586  * xmlNanoHTTPContentLength:
1587  * @ctx:  the HTTP context
1588  *
1589  * Provides the specified content length from the HTTP header.
1590  *
1591  * Return the specified content length from the HTTP header.  Note that
1592  * a value of -1 indicates that the content length element was not included in
1593  * the response header.
1594  */
1595 int
1596 xmlNanoHTTPContentLength( void * ctx ) {
1597     xmlNanoHTTPCtxtPtr  ctxt = ctx;
1598
1599     return ( ( ctxt == NULL ) ? -1 : ctxt->ContentLength );
1600 }
1601
1602 /**
1603  * xmlNanoHTTPFetchContent:
1604  * @ctx:  the HTTP context
1605  * @ptr:  pointer to set to the content buffer.
1606  * @len:  integer pointer to hold the length of the content
1607  *
1608  * Check if all the content was read
1609  *
1610  * Returns 0 if all the content was read and available, returns
1611  * -1 if received content length was less than specified or an error 
1612  * occurred.
1613  */
1614 int
1615 xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len ) {
1616     xmlNanoHTTPCtxtPtr  ctxt = ctx;
1617
1618     int                 rc = 0;
1619     int                 cur_lgth;
1620     int                 rcvd_lgth;
1621     int                 dummy_int;
1622     char *              dummy_ptr = NULL;
1623
1624     /*  Dummy up return input parameters if not provided  */
1625
1626     if ( len == NULL )
1627         len = &dummy_int;
1628
1629     if ( ptr == NULL )
1630         ptr = &dummy_ptr;
1631
1632     /*  But can't work without the context pointer  */
1633
1634     if ( ( ctxt == NULL ) || ( ctxt->content == NULL ) ) {
1635         *len = 0;
1636         *ptr = NULL;
1637         return ( -1 );
1638     }
1639
1640     rcvd_lgth = ctxt->inptr - ctxt->content;
1641
1642     while ( (cur_lgth = xmlNanoHTTPRecv( ctxt )) > 0 ) {
1643
1644         rcvd_lgth += cur_lgth;
1645         if ( (ctxt->ContentLength > 0) && (rcvd_lgth >= ctxt->ContentLength) )
1646             break;
1647     }
1648
1649     *ptr = ctxt->content;
1650     *len = rcvd_lgth;
1651
1652     if ( ( ctxt->ContentLength > 0 ) && ( rcvd_lgth < ctxt->ContentLength ) )
1653         rc = -1;
1654     else if ( rcvd_lgth == 0 )
1655         rc = -1;
1656
1657     return ( rc );
1658 }
1659
1660 #ifdef STANDALONE
1661 int main(int argc, char **argv) {
1662     char *contentType = NULL;
1663
1664     if (argv[1] != NULL) {
1665         if (argv[2] != NULL) 
1666             xmlNanoHTTPFetch(argv[1], argv[2], &contentType);
1667         else
1668             xmlNanoHTTPFetch(argv[1], "-", &contentType);
1669         if (contentType != NULL) xmlFree(contentType);
1670     } else {
1671         xmlGenericError(xmlGenericErrorContext,
1672                 "%s: minimal HTTP GET implementation\n", argv[0]);
1673         xmlGenericError(xmlGenericErrorContext,
1674                 "\tusage %s [ URL [ filename ] ]\n", argv[0]);
1675     }
1676     xmlNanoHTTPCleanup();
1677     xmlMemoryDump();
1678     return(0);
1679 }
1680 #endif /* STANDALONE */
1681 #else /* !LIBXML_HTTP_ENABLED */
1682 #ifdef STANDALONE
1683 #include <stdio.h>
1684 int main(int argc, char **argv) {
1685     xmlGenericError(xmlGenericErrorContext,
1686             "%s : HTTP support not compiled in\n", argv[0]);
1687     return(0);
1688 }
1689 #endif /* STANDALONE */
1690 #endif /* LIBXML_HTTP_ENABLED */