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