• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-WNDR4500v2-V1.0.0.60_1.0.38/ap/gpl/timemachine/gettext-0.17/gettext-tools/gnulib-lib/libxml/
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
133typedef 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
162static int initialized = 0;
163static char *proxy = NULL;	 /* the proxy name if any */
164static int proxyPort;	/* the proxy port if any */
165static unsigned int timeout = 60;/* the select() timeout in seconds */
166
167static 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 */
175static void
176xmlHTTPErrMemory(const char *extra)
177{
178    __xmlSimpleError(XML_FROM_HTTP, XML_ERR_NO_MEMORY, NULL, NULL, extra);
179}
180
181/**
182 * A portability function
183 */
184static int socket_errno(void) {
185#ifdef _WINSOCKAPI_
186    return(WSAGetLastError());
187#else
188    return(errno);
189#endif
190}
191
192#ifdef SUPPORT_IP6
193static
194int 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
213void
214xmlNanoHTTPInit(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    }
244done:
245    initialized = 1;
246}
247
248/**
249 * xmlNanoHTTPCleanup:
250 *
251 * Cleanup the HTTP protocol layer.
252 */
253
254void
255xmlNanoHTTPCleanup(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
277static void
278xmlNanoHTTPScanURL(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
334void
335xmlNanoHTTPScanProxy(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
379static xmlNanoHTTPCtxtPtr
380xmlNanoHTTPNewCtxt(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
407static void
408xmlNanoHTTPFreeCtxt(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
442static int
443xmlNanoHTTPSend(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
503static int
504xmlNanoHTTPRecv(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
607static char *
608xmlNanoHTTPReadLine(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
653static void
654xmlNanoHTTPScanAnswer(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
804static int
805xmlNanoHTTPConnectAttempt(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
963static int
964xmlNanoHTTPConnectHost(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
1149void*
1150xmlNanoHTTPOpen(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
1169void*
1170xmlNanoHTTPOpenRedir(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 */
1188int
1189xmlNanoHTTPRead(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 */
1240void
1241xmlNanoHTTPClose(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
1267void*
1268xmlNanoHTTPMethodRedir(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
1285retry:
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
1509void*
1510xmlNanoHTTPMethod(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 */
1529int
1530xmlNanoHTTPFetch(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 */
1575int
1576xmlNanoHTTPSave(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 */
1612int
1613xmlNanoHTTPReturnCode(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 */
1630const char *
1631xmlNanoHTTPAuthHeader(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 */
1649int
1650xmlNanoHTTPContentLength( 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 */
1664const char *
1665xmlNanoHTTPRedir( 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 */
1679const char *
1680xmlNanoHTTPEncoding( 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 */
1694const char *
1695xmlNanoHTTPMimeType( 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 */
1713static int
1714xmlNanoHTTPFetchContent( 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
1760int 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>
1783int 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"
1792