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