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