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