blob: cf30c5302437ae97ea95819843ba1b36e8add0ea [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
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@w3.org
12 */
13
14/* TODO add compression support, Send the Accept- , and decompress on the
15 fly with ZLIB if found at compile-time */
16
17#ifdef WIN32
18#define INCLUDE_WINSOCK
19#include "win32config.h"
20#else
21#include "config.h"
22#endif
23
24#include <libxml/xmlversion.h>
25
26#ifdef LIBXML_HTTP_ENABLED
27#include <stdio.h>
28#include <string.h>
29
30#ifdef HAVE_STDLIB_H
31#include <stdlib.h>
32#endif
33#ifdef HAVE_UNISTD_H
34#include <unistd.h>
35#endif
36#ifdef HAVE_SYS_SOCKET_H
37#include <sys/socket.h>
38#endif
39#ifdef HAVE_NETINET_IN_H
40#include <netinet/in.h>
41#endif
42#ifdef HAVE_ARPA_INET_H
43#include <arpa/inet.h>
44#endif
45#ifdef HAVE_NETDB_H
46#include <netdb.h>
47#endif
48#ifdef HAVE_FCNTL_H
49#include <fcntl.h>
50#endif
51#ifdef HAVE_ERRNO_H
52#include <errno.h>
53#endif
54#ifdef HAVE_SYS_TIME_H
55#include <sys/time.h>
56#endif
57#ifdef HAVE_SYS_SELECT_H
58#include <sys/select.h>
59#endif
60#ifdef HAVE_STRINGS_H
61#include <strings.h>
62#endif
63#ifdef SUPPORT_IP6
64#include <resolv.h>
65#endif
66
67#ifdef VMS
68#include <stropts>
69#define SOCKLEN_T unsigned int
70#define SOCKET int
71#endif
72
73#include <libxml/xmlmemory.h>
74#include <libxml/parser.h> /* for xmlStr(n)casecmp() */
75#include <libxml/nanohttp.h>
76
77/**
78 * A couple portability macros
79 */
80#ifndef _WINSOCKAPI_
81#define closesocket(s) close(s)
82#define SOCKET int
83#endif
84
85#ifdef STANDALONE
86#define DEBUG_HTTP
87#define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n)
88#define xmlStrcasecmpi(a, b) strcasecmp((char *)a, (char *)b)
89#endif
90
91#define XML_NANO_HTTP_MAX_REDIR 10
92
93#define XML_NANO_HTTP_CHUNK 4096
94
95#define XML_NANO_HTTP_CLOSED 0
96#define XML_NANO_HTTP_WRITE 1
97#define XML_NANO_HTTP_READ 2
98#define XML_NANO_HTTP_NONE 4
99
100typedef struct xmlNanoHTTPCtxt {
101 char *protocol; /* the protocol name */
102 char *hostname; /* the host name */
103 int port; /* the port */
104 char *path; /* the path within the URL */
105 SOCKET fd; /* the file descriptor for the socket */
106 int state; /* WRITE / READ / CLOSED */
107 char *out; /* buffer sent (zero terminated) */
108 char *outptr; /* index within the buffer sent */
109 char *in; /* the receiving buffer */
110 char *content; /* the start of the content */
111 char *inptr; /* the next byte to read from network */
112 char *inrptr; /* the next byte to give back to the client */
113 int inlen; /* len of the input buffer */
114 int last; /* return code for last operation */
115 int returnValue; /* the protocol return value */
116 char *contentType; /* the MIME type for the input */
117 char *location; /* the new URL in case of redirect */
118 char *authHeader; /* contents of {WWW,Proxy}-Authenticate header */
119} xmlNanoHTTPCtxt, *xmlNanoHTTPCtxtPtr;
120
121static int initialized = 0;
122static char *proxy = NULL; /* the proxy name if any */
123static int proxyPort; /* the proxy port if any */
124static unsigned int timeout = 60;/* the select() timeout in seconds */
125
126/**
127 * A portability function
128 */
129int socket_errno(void) {
130#ifdef _WINSOCKAPI_
131 return(WSAGetLastError());
132#else
133 return(errno);
134#endif
135}
136
137/**
138 * xmlNanoHTTPInit:
139 *
140 * Initialize the HTTP protocol layer.
141 * Currently it just checks for proxy informations
142 */
143
144void
145xmlNanoHTTPInit(void) {
146 const char *env;
147#ifdef _WINSOCKAPI_
148 WSADATA wsaData;
149#endif
150
151 if (initialized)
152 return;
153
154#ifdef _WINSOCKAPI_
155 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
156 return;
157#endif
158
159 if (proxy == NULL) {
160 proxyPort = 80;
161 env = getenv("no_proxy");
162 if (env != NULL)
163 goto done;
164 env = getenv("http_proxy");
165 if (env != NULL) {
166 xmlNanoHTTPScanProxy(env);
167 goto done;
168 }
169 env = getenv("HTTP_PROXY");
170 if (env != NULL) {
171 xmlNanoHTTPScanProxy(env);
172 goto done;
173 }
174 }
175done:
176 initialized = 1;
177}
178
179/**
180 * xmlNanoHTTPClenup:
181 *
182 * Cleanup the HTTP protocol layer.
183 */
184
185void
186xmlNanoHTTPCleanup(void) {
187 if (proxy != NULL)
188 xmlFree(proxy);
189#ifdef _WINSOCKAPI_
190 if (initialized)
191 WSACleanup();
192#endif
193 initialized = 0;
194 return;
195}
196
197/**
198 * xmlNanoHTTPTimeout:
199 * @delay: the delay in seconds
200 *
201 * Set the HTTP timeout, (default is 60secs). 0 means immediate
202 * return, while -1 infinite.
203 */
204
205void
206xmlNanoHTTPTimeout(int delay) {
207 timeout = (unsigned int) delay;
208}
209
210/**
211 * xmlNanoHTTPScanURL:
212 * @ctxt: an HTTP context
213 * @URL: The URL used to initialize the context
214 *
215 * (Re)Initialize an HTTP context by parsing the URL and finding
216 * the protocol host port and path it indicates.
217 */
218
219static void
220xmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt, const char *URL) {
221 const char *cur = URL;
222 char buf[4096];
223 int index = 0;
224 int port = 0;
225
226 if (ctxt->protocol != NULL) {
227 xmlFree(ctxt->protocol);
228 ctxt->protocol = NULL;
229 }
230 if (ctxt->hostname != NULL) {
231 xmlFree(ctxt->hostname);
232 ctxt->hostname = NULL;
233 }
234 if (ctxt->path != NULL) {
235 xmlFree(ctxt->path);
236 ctxt->path = NULL;
237 }
238 if (URL == NULL) return;
239 buf[index] = 0;
240 while (*cur != 0) {
241 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
242 buf[index] = 0;
243 ctxt->protocol = xmlMemStrdup(buf);
244 index = 0;
245 cur += 3;
246 break;
247 }
248 buf[index++] = *cur++;
249 }
250 if (*cur == 0) return;
251
252 buf[index] = 0;
253 while (1) {
254 if (cur[0] == ':') {
255 buf[index] = 0;
256 ctxt->hostname = xmlMemStrdup(buf);
257 index = 0;
258 cur += 1;
259 while ((*cur >= '0') && (*cur <= '9')) {
260 port *= 10;
261 port += *cur - '0';
262 cur++;
263 }
264 if (port != 0) ctxt->port = port;
265 while ((cur[0] != '/') && (*cur != 0))
266 cur++;
267 break;
268 }
269 if ((*cur == '/') || (*cur == 0)) {
270 buf[index] = 0;
271 ctxt->hostname = xmlMemStrdup(buf);
272 index = 0;
273 break;
274 }
275 buf[index++] = *cur++;
276 }
277 if (*cur == 0)
278 ctxt->path = xmlMemStrdup("/");
279 else {
280 index = 0;
281 buf[index] = 0;
282 while (*cur != 0)
283 buf[index++] = *cur++;
284 buf[index] = 0;
285 ctxt->path = xmlMemStrdup(buf);
286 }
287}
288
289/**
290 * xmlNanoHTTPScanProxy:
291 * @URL: The proxy URL used to initialize the proxy context
292 *
293 * (Re)Initialize the HTTP Proxy context by parsing the URL and finding
294 * the protocol host port it indicates.
295 * Should be like http://myproxy/ or http://myproxy:3128/
296 * A NULL URL cleans up proxy informations.
297 */
298
299void
300xmlNanoHTTPScanProxy(const char *URL) {
301 const char *cur = URL;
302 char buf[4096];
303 int index = 0;
304 int port = 0;
305
306 if (proxy != NULL) {
307 xmlFree(proxy);
308 proxy = NULL;
309 }
310 if (proxyPort != 0) {
311 proxyPort = 0;
312 }
313#ifdef DEBUG_HTTP
314 if (URL == NULL)
315 xmlGenericError(xmlGenericErrorContext,
316 "Removing HTTP proxy info\n");
317 else
318 xmlGenericError(xmlGenericErrorContext,
319 "Using HTTP proxy %s\n", URL);
320#endif
321 if (URL == NULL) return;
322 buf[index] = 0;
323 while (*cur != 0) {
324 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
325 buf[index] = 0;
326 index = 0;
327 cur += 3;
328 break;
329 }
330 buf[index++] = *cur++;
331 }
332 if (*cur == 0) return;
333
334 buf[index] = 0;
335 while (1) {
336 if (cur[0] == ':') {
337 buf[index] = 0;
338 proxy = xmlMemStrdup(buf);
339 index = 0;
340 cur += 1;
341 while ((*cur >= '0') && (*cur <= '9')) {
342 port *= 10;
343 port += *cur - '0';
344 cur++;
345 }
346 if (port != 0) proxyPort = port;
347 while ((cur[0] != '/') && (*cur != 0))
348 cur++;
349 break;
350 }
351 if ((*cur == '/') || (*cur == 0)) {
352 buf[index] = 0;
353 proxy = xmlMemStrdup(buf);
354 index = 0;
355 break;
356 }
357 buf[index++] = *cur++;
358 }
359}
360
361/**
362 * xmlNanoHTTPNewCtxt:
363 * @URL: The URL used to initialize the context
364 *
365 * Allocate and initialize a new HTTP context.
366 *
367 * Returns an HTTP context or NULL in case of error.
368 */
369
370static xmlNanoHTTPCtxtPtr
371xmlNanoHTTPNewCtxt(const char *URL) {
372 xmlNanoHTTPCtxtPtr ret;
373
374 ret = (xmlNanoHTTPCtxtPtr) xmlMalloc(sizeof(xmlNanoHTTPCtxt));
375 if (ret == NULL) return(NULL);
376
377 memset(ret, 0, sizeof(xmlNanoHTTPCtxt));
378 ret->port = 80;
379 ret->returnValue = 0;
380 ret->fd = -1;
381
382 xmlNanoHTTPScanURL(ret, URL);
383
384 return(ret);
385}
386
387/**
388 * xmlNanoHTTPFreeCtxt:
389 * @ctxt: an HTTP context
390 *
391 * Frees the context after closing the connection.
392 */
393
394static void
395xmlNanoHTTPFreeCtxt(xmlNanoHTTPCtxtPtr ctxt) {
396 if (ctxt == NULL) return;
397 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
398 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
399 if (ctxt->path != NULL) xmlFree(ctxt->path);
400 if (ctxt->out != NULL) xmlFree(ctxt->out);
401 if (ctxt->in != NULL) xmlFree(ctxt->in);
402 if (ctxt->contentType != NULL) xmlFree(ctxt->contentType);
403 if (ctxt->location != NULL) xmlFree(ctxt->location);
404 if (ctxt->authHeader != NULL) xmlFree(ctxt->authHeader);
405 ctxt->state = XML_NANO_HTTP_NONE;
406 if (ctxt->fd >= 0) closesocket(ctxt->fd);
407 ctxt->fd = -1;
408 xmlFree(ctxt);
409}
410
411/**
412 * xmlNanoHTTPSend:
413 * @ctxt: an HTTP context
414 *
415 * Send the input needed to initiate the processing on the server side
416 */
417
418static void
419xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt) {
420 if (ctxt->state & XML_NANO_HTTP_WRITE) {
421 int total_sent = 0;
422 while (total_sent <strlen(ctxt->outptr)) {
423 int nsent = send(ctxt->fd, ctxt->outptr+total_sent,
424 strlen(ctxt->outptr)-total_sent, 0);
425 if (nsent>0)
426 total_sent += nsent;
427}
428
429 ctxt->last = total_sent;
430 }
431}
432
433/**
434 * xmlNanoHTTPRecv:
435 * @ctxt: an HTTP context
436 *
437 * Read information coming from the HTTP connection.
438 * This is a blocking call (but it blocks in select(), not read()).
439 *
440 * Returns the number of byte read or -1 in case of error.
441 */
442
443static int
444xmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt) {
445 fd_set rfd;
446 struct timeval tv;
447
448
449 while (ctxt->state & XML_NANO_HTTP_READ) {
450 if (ctxt->in == NULL) {
451 ctxt->in = (char *) xmlMalloc(65000 * sizeof(char));
452 if (ctxt->in == NULL) {
453 ctxt->last = -1;
454 return(-1);
455 }
456 ctxt->inlen = 65000;
457 ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in;
458 }
459 if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) {
460 int delta = ctxt->inrptr - ctxt->in;
461 int len = ctxt->inptr - ctxt->inrptr;
462
463 memmove(ctxt->in, ctxt->inrptr, len);
464 ctxt->inrptr -= delta;
465 ctxt->content -= delta;
466 ctxt->inptr -= delta;
467 }
468 if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) {
469 int d_inptr = ctxt->inptr - ctxt->in;
470 int d_content = ctxt->content - ctxt->in;
471 int d_inrptr = ctxt->inrptr - ctxt->in;
472
473 ctxt->inlen *= 2;
474 ctxt->in = (char *) xmlRealloc(ctxt->in, ctxt->inlen);
475 if (ctxt->in == NULL) {
476 ctxt->last = -1;
477 return(-1);
478 }
479 ctxt->inptr = ctxt->in + d_inptr;
480 ctxt->content = ctxt->in + d_content;
481 ctxt->inrptr = ctxt->in + d_inrptr;
482 }
483 ctxt->last = recv(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK, 0);
484 if (ctxt->last > 0) {
485 ctxt->inptr += ctxt->last;
486 return(ctxt->last);
487 }
488 if (ctxt->last == 0) {
489 return(0);
490 }
491 if (ctxt->last == -1) {
492 switch (socket_errno()) {
493 case EINPROGRESS:
494 case EWOULDBLOCK:
495#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
496 case EAGAIN:
497#endif
498 break;
499 default:
500 return(0);
501 }
502 }
503
504 tv.tv_sec = timeout;
505 tv.tv_usec = 0;
506 FD_ZERO(&rfd);
507 FD_SET(ctxt->fd, &rfd);
508
509 if (select(ctxt->fd+1, &rfd, NULL, NULL, &tv)<1)
510 return(0);
511 }
512 return(0);
513}
514
515/**
516 * xmlNanoHTTPReadLine:
517 * @ctxt: an HTTP context
518 *
519 * Read one line in the HTTP server output, usually for extracting
520 * the HTTP protocol informations from the answer header.
521 *
522 * Returns a newly allocated string with a copy of the line, or NULL
523 * which indicate the end of the input.
524 */
525
526static char *
527xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt) {
528 char buf[4096];
529 char *bp = buf;
530
531 while (bp - buf < 4095) {
532 if (ctxt->inrptr == ctxt->inptr) {
533 if (xmlNanoHTTPRecv(ctxt) == 0) {
534 if (bp == buf)
535 return(NULL);
536 else
537 *bp = 0;
538 return(xmlMemStrdup(buf));
539 }
540 }
541 *bp = *ctxt->inrptr++;
542 if (*bp == '\n') {
543 *bp = 0;
544 return(xmlMemStrdup(buf));
545 }
546 if (*bp != '\r')
547 bp++;
548 }
549 buf[4095] = 0;
550 return(xmlMemStrdup(buf));
551}
552
553
554/**
555 * xmlNanoHTTPScanAnswer:
556 * @ctxt: an HTTP context
557 * @line: an HTTP header line
558 *
559 * Try to extract useful informations from the server answer.
560 * We currently parse and process:
561 * - The HTTP revision/ return code
562 * - The Content-Type
563 * - The Location for redirrect processing.
564 *
565 * Returns -1 in case of failure, the file descriptor number otherwise
566 */
567
568static void
569xmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt, const char *line) {
570 const char *cur = line;
571
572 if (line == NULL) return;
573
574 if (!strncmp(line, "HTTP/", 5)) {
575 int version = 0;
576 int ret = 0;
577
578 cur += 5;
579 while ((*cur >= '0') && (*cur <= '9')) {
580 version *= 10;
581 version += *cur - '0';
582 cur++;
583 }
584 if (*cur == '.') {
585 cur++;
586 if ((*cur >= '0') && (*cur <= '9')) {
587 version *= 10;
588 version += *cur - '0';
589 cur++;
590 }
591 while ((*cur >= '0') && (*cur <= '9'))
592 cur++;
593 } else
594 version *= 10;
595 if ((*cur != ' ') && (*cur != '\t')) return;
596 while ((*cur == ' ') || (*cur == '\t')) cur++;
597 if ((*cur < '0') || (*cur > '9')) return;
598 while ((*cur >= '0') && (*cur <= '9')) {
599 ret *= 10;
600 ret += *cur - '0';
601 cur++;
602 }
603 if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return;
604 ctxt->returnValue = ret;
605 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Content-Type:", 13)) {
606 cur += 13;
607 while ((*cur == ' ') || (*cur == '\t')) cur++;
608 if (ctxt->contentType != NULL)
609 xmlFree(ctxt->contentType);
610 ctxt->contentType = xmlMemStrdup(cur);
611 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"ContentType:", 12)) {
612 cur += 12;
613 if (ctxt->contentType != NULL) return;
614 while ((*cur == ' ') || (*cur == '\t')) cur++;
615 ctxt->contentType = xmlMemStrdup(cur);
616 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Location:", 9)) {
617 cur += 9;
618 while ((*cur == ' ') || (*cur == '\t')) cur++;
619 if (ctxt->location != NULL)
620 xmlFree(ctxt->location);
621 ctxt->location = xmlMemStrdup(cur);
622 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"WWW-Authenticate:", 17)) {
623 cur += 17;
624 while ((*cur == ' ') || (*cur == '\t')) cur++;
625 if (ctxt->authHeader != NULL)
626 xmlFree(ctxt->authHeader);
627 ctxt->authHeader = xmlMemStrdup(cur);
628 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Proxy-Authenticate:", 19)) {
629 cur += 19;
630 while ((*cur == ' ') || (*cur == '\t')) cur++;
631 if (ctxt->authHeader != NULL)
632 xmlFree(ctxt->authHeader);
633 ctxt->authHeader = xmlMemStrdup(cur);
634 }
635}
636
637/**
638 * xmlNanoHTTPConnectAttempt:
639 * @ia: an internet adress structure
640 * @port: the port number
641 *
642 * Attempt a connection to the given IP:port endpoint. It forces
643 * non-blocking semantic on the socket, and allow 60 seconds for
644 * the host to answer.
645 *
646 * Returns -1 in case of failure, the file descriptor number otherwise
647 */
648
649static int
650xmlNanoHTTPConnectAttempt(struct sockaddr *addr, int port)
651{
652 SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
653 fd_set wfd;
654 struct timeval tv;
655 int status;
656
657 if (s==-1) {
658#ifdef DEBUG_HTTP
659 perror("socket");
660#endif
661 return(-1);
662 }
663
664#ifdef _WINSOCKAPI_
665 {
666 u_long one = 1;
667
668 status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0;
669 }
670#else /* _WINSOCKAPI_ */
671#if defined(VMS)
672 {
673 int enable = 1;
674 status = ioctl(s, FIONBIO, &enable);
675 }
676#else /* VMS */
677 if ((status = fcntl(s, F_GETFL, 0)) != -1) {
678#ifdef O_NONBLOCK
679 status |= O_NONBLOCK;
680#else /* O_NONBLOCK */
681#ifdef F_NDELAY
682 status |= F_NDELAY;
683#endif /* F_NDELAY */
684#endif /* !O_NONBLOCK */
685 status = fcntl(s, F_SETFL, status);
686 }
687 if (status < 0) {
688#ifdef DEBUG_HTTP
689 perror("nonblocking");
690#endif
691 closesocket(s);
692 return(-1);
693 }
694#endif /* !VMS */
695#endif /* !_WINSOCKAPI_ */
696
697
698 if ((connect(s, addr, sizeof(*addr))==-1)) {
699 switch (socket_errno()) {
700 case EINPROGRESS:
701 case EWOULDBLOCK:
702 break;
703 default:
704 perror("connect");
705 closesocket(s);
706 return(-1);
707 }
708 }
709
710 tv.tv_sec = timeout;
711 tv.tv_usec = 0;
712
713 FD_ZERO(&wfd);
714 FD_SET(s, &wfd);
715
716 switch(select(s+1, NULL, &wfd, NULL, &tv))
717 {
718 case 0:
719 /* Time out */
720 closesocket(s);
721 return(-1);
722 case -1:
723 /* Ermm.. ?? */
724#ifdef DEBUG_HTTP
725 perror("select");
726#endif
727 closesocket(s);
728 return(-1);
729 }
730
731 if ( FD_ISSET(s, &wfd) ) {
732 SOCKLEN_T len;
733 len = sizeof(status);
734 if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&status, &len) < 0 ) {
735 /* Solaris error code */
736 return (-1);
737 }
738 if ( status ) {
739 closesocket(s);
740 errno = status;
741 return (-1);
742 }
743 } else {
744 /* pbm */
745 return (-1);
746 }
747
748 return(s);
749}
750
751/**
752 * xmlNanoHTTPConnectHost:
753 * @host: the host name
754 * @port: the port number
755 *
756 * Attempt a connection to the given host:port endpoint. It tries
757 * the multiple IP provided by the DNS if available.
758 *
759 * Returns -1 in case of failure, the file descriptor number otherwise
760 */
761
762static int
763xmlNanoHTTPConnectHost(const char *host, int port)
764{
765 struct hostent *h;
766 struct sockaddr *addr;
767 struct in_addr ia;
768 struct sockaddr_in sin;
769#ifdef SUPPORT_IP6
770 struct in6_addr ia6;
771 struct sockaddr_in6 sin6;
772#endif
773 int i;
774 int s;
775
776#if defined(SUPPORT_IP6) && defined(RES_USE_INET6)
777 if (!(_res.options & RES_INIT))
778 res_init();
779 _res.options |= RES_USE_INET6;
780#endif
781 h=gethostbyname(host);
782 if (h==NULL)
783 {
784#ifdef DEBUG_HTTP
785 xmlGenericError(xmlGenericErrorContext,"unable to resolve '%s'.\n", host);
786#endif
787 return(-1);
788 }
789
790 for(i=0; h->h_addr_list[i]; i++)
791 {
792 if (h->h_addrtype == AF_INET) {
793 /* A records (IPv4) */
794 memcpy(&ia, h->h_addr_list[i], h->h_length);
795 sin.sin_family = h->h_addrtype;
796 sin.sin_addr = ia;
797 sin.sin_port = htons(port);
798 addr = (struct sockaddr *)&sin;
799#ifdef SUPPORT_IP6
800 } else if (h->h_addrtype == AF_INET6) {
801 /* AAAA records (IPv6) */
802 memcpy(&ia6, h->h_addr_list[i], h->h_length);
803 sin6.sin_family = h->h_addrtype;
804 sin6.sin_addr = ia6;
805 sin6.sin_port = htons(port);
806 addr = (struct sockaddr *)&sin6;
807#endif
808 } else
809 break; /* for */
810
811 s = xmlNanoHTTPConnectAttempt(addr, port);
812 if (s != -1)
813 return(s);
814 }
815
816#ifdef DEBUG_HTTP
817 xmlGenericError(xmlGenericErrorContext,
818 "unable to connect to '%s'.\n", host);
819#endif
820 return(-1);
821}
822
823
824/**
825 * xmlNanoHTTPOpen:
826 * @URL: The URL to load
827 * @contentType: if available the Content-Type information will be
828 * returned at that location
829 *
830 * This function try to open a connection to the indicated resource
831 * via HTTP GET.
832 *
833 * Returns NULL in case of failure, otherwise a request handler.
834 * The contentType, if provided must be freed by the caller
835 */
836
837void*
838xmlNanoHTTPOpen(const char *URL, char **contentType) {
839 if (contentType != NULL) *contentType = NULL;
840 return xmlNanoHTTPMethod(URL, NULL, NULL, contentType, NULL);
841}
842
843/**
844 * xmlNanoHTTPRead:
845 * @ctx: the HTTP context
846 * @dest: a buffer
847 * @len: the buffer length
848 *
849 * This function tries to read @len bytes from the existing HTTP connection
850 * and saves them in @dest. This is a blocking call.
851 *
852 * Returns the number of byte read. 0 is an indication of an end of connection.
853 * -1 indicates a parameter error.
854 */
855int
856xmlNanoHTTPRead(void *ctx, void *dest, int len) {
857 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
858
859 if (ctx == NULL) return(-1);
860 if (dest == NULL) return(-1);
861 if (len <= 0) return(0);
862
863 while (ctxt->inptr - ctxt->inrptr < len) {
864 if (xmlNanoHTTPRecv(ctxt) == 0) break;
865 }
866 if (ctxt->inptr - ctxt->inrptr < len)
867 len = ctxt->inptr - ctxt->inrptr;
868 memcpy(dest, ctxt->inrptr, len);
869 ctxt->inrptr += len;
870 return(len);
871}
872
873/**
874 * xmlNanoHTTPClose:
875 * @ctx: the HTTP context
876 *
877 * This function closes an HTTP context, it ends up the connection and
878 * free all data related to it.
879 */
880void
881xmlNanoHTTPClose(void *ctx) {
882 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
883
884 if (ctx == NULL) return;
885
886 xmlNanoHTTPFreeCtxt(ctxt);
887}
888
889/**
890 * xmlNanoHTTPMethod:
891 * @URL: The URL to load
892 * @method: the HTTP method to use
893 * @input: the input string if any
894 * @contentType: the Content-Type information IN and OUT
895 * @headers: the extra headers
896 *
897 * This function try to open a connection to the indicated resource
898 * via HTTP using the given @method, adding the given extra headers
899 * and the input buffer for the request content.
900 *
901 * Returns NULL in case of failure, otherwise a request handler.
902 * The contentType, if provided must be freed by the caller
903 */
904
905void*
906xmlNanoHTTPMethod(const char *URL, const char *method, const char *input,
907 char **contentType, const char *headers) {
908 xmlNanoHTTPCtxtPtr ctxt;
909 char *bp, *p;
910 int blen, ilen, ret;
911 int head;
912 int nbRedirects = 0;
913 char *redirURL = NULL;
914
915 if (URL == NULL) return(NULL);
916 if (method == NULL) method = "GET";
917 xmlNanoHTTPInit();
918
919retry:
920 if (redirURL == NULL)
921 ctxt = xmlNanoHTTPNewCtxt(URL);
922 else {
923 ctxt = xmlNanoHTTPNewCtxt(redirURL);
924 xmlFree(redirURL);
925 redirURL = NULL;
926 }
927
928 if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
929 xmlNanoHTTPFreeCtxt(ctxt);
930 if (redirURL != NULL) xmlFree(redirURL);
931 return(NULL);
932 }
933 if (ctxt->hostname == NULL) {
934 xmlNanoHTTPFreeCtxt(ctxt);
935 return(NULL);
936 }
937 if (proxy) {
938 blen = strlen(ctxt->hostname) * 2 + 16;
939 ret = xmlNanoHTTPConnectHost(proxy, proxyPort);
940 }
941 else {
942 blen = strlen(ctxt->hostname);
943 ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
944 }
945 if (ret < 0) {
946 xmlNanoHTTPFreeCtxt(ctxt);
947 return(NULL);
948 }
949 ctxt->fd = ret;
950
951 if (input != NULL) {
952 ilen = strlen(input);
953 blen += ilen + 32;
954 }
955 else
956 ilen = 0;
957 if (headers != NULL)
958 blen += strlen(headers);
959 if (contentType && *contentType)
960 blen += strlen(*contentType) + 16;
961 blen += strlen(method) + strlen(ctxt->path) + 23;
962 bp = xmlMalloc(blen);
963 if (proxy) {
964 if (ctxt->port != 80) {
965 sprintf(bp, "%s http://%s:%d%s", method, ctxt->hostname,
966 ctxt->port, ctxt->path);
967 }
968 else
969 sprintf(bp, "%s http://%s%s", method, ctxt->hostname, ctxt->path);
970 }
971 else
972 sprintf(bp, "%s %s", method, ctxt->path);
973 p = bp + strlen(bp);
974 sprintf(p, " HTTP/1.0\r\nHost: %s\r\n", ctxt->hostname);
975 p += strlen(p);
976 if (contentType != NULL && *contentType) {
977 sprintf(p, "Content-Type: %s\r\n", *contentType);
978 p += strlen(p);
979 }
980 if (headers != NULL) {
981 strcpy(p, headers);
982 p += strlen(p);
983 }
984 if (input != NULL)
985 sprintf(p, "Content-Length: %d\r\n\r\n%s", ilen, input);
986 else
987 strcpy(p, "\r\n");
988#ifdef DEBUG_HTTP
989 xmlGenericError(xmlGenericErrorContext,
990 "-> %s%s", proxy? "(Proxy) " : "", bp);
991 if ((blen -= strlen(bp)+1) < 0)
992 xmlGenericError(xmlGenericErrorContext,
993 "ERROR: overflowed buffer by %d bytes\n", -blen);
994#endif
995 ctxt->outptr = ctxt->out = bp;
996 ctxt->state = XML_NANO_HTTP_WRITE;
997 xmlNanoHTTPSend(ctxt);
998 ctxt->state = XML_NANO_HTTP_READ;
999 head = 1;
1000
1001 while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
1002 if (head && (*p == 0)) {
1003 head = 0;
1004 ctxt->content = ctxt->inrptr;
1005 xmlFree(p);
1006 break;
1007 }
1008 xmlNanoHTTPScanAnswer(ctxt, p);
1009
1010#ifdef DEBUG_HTTP
1011 xmlGenericError(xmlGenericErrorContext, "<- %s\n", p);
1012#endif
1013 xmlFree(p);
1014 }
1015
1016 if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
1017 (ctxt->returnValue < 400)) {
1018#ifdef DEBUG_HTTP
1019 xmlGenericError(xmlGenericErrorContext,
1020 "\nRedirect to: %s\n", ctxt->location);
1021#endif
1022 while (xmlNanoHTTPRecv(ctxt)) ;
1023 if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
1024 nbRedirects++;
1025 redirURL = xmlMemStrdup(ctxt->location);
1026 xmlNanoHTTPFreeCtxt(ctxt);
1027 goto retry;
1028 }
1029 xmlNanoHTTPFreeCtxt(ctxt);
1030#ifdef DEBUG_HTTP
1031 xmlGenericError(xmlGenericErrorContext,
1032 "Too many redirects, aborting ...\n");
1033#endif
1034 return(NULL);
1035
1036 }
1037
1038 if (contentType != NULL) {
1039 if (ctxt->contentType != NULL)
1040 *contentType = xmlMemStrdup(ctxt->contentType);
1041 else
1042 *contentType = NULL;
1043 }
1044
1045#ifdef DEBUG_HTTP
1046 if (ctxt->contentType != NULL)
1047 xmlGenericError(xmlGenericErrorContext,
1048 "\nCode %d, content-type '%s'\n\n",
1049 ctxt->returnValue, ctxt->contentType);
1050 else
1051 xmlGenericError(xmlGenericErrorContext,
1052 "\nCode %d, no content-type\n\n",
1053 ctxt->returnValue);
1054#endif
1055
1056 return((void *) ctxt);
1057}
1058
1059/**
1060 * xmlNanoHTTPFetch:
1061 * @URL: The URL to load
1062 * @filename: the filename where the content should be saved
1063 * @contentType: if available the Content-Type information will be
1064 * returned at that location
1065 *
1066 * This function try to fetch the indicated resource via HTTP GET
1067 * and save it's content in the file.
1068 *
1069 * Returns -1 in case of failure, 0 incase of success. The contentType,
1070 * if provided must be freed by the caller
1071 */
1072int
1073xmlNanoHTTPFetch(const char *URL, const char *filename, char **contentType) {
1074 void *ctxt;
1075 char buf[4096];
1076 int fd;
1077 int len;
1078
1079 ctxt = xmlNanoHTTPOpen(URL, contentType);
1080 if (ctxt == NULL) return(-1);
1081
1082 if (!strcmp(filename, "-"))
1083 fd = 0;
1084 else {
1085 fd = open(filename, O_CREAT | O_WRONLY, 00644);
1086 if (fd < 0) {
1087 xmlNanoHTTPClose(ctxt);
1088 if ((contentType != NULL) && (*contentType != NULL)) {
1089 xmlFree(*contentType);
1090 *contentType = NULL;
1091 }
1092 return(-1);
1093 }
1094 }
1095
1096 while ((len = xmlNanoHTTPRead(ctxt, buf, sizeof(buf))) > 0) {
1097 write(fd, buf, len);
1098 }
1099
1100 xmlNanoHTTPClose(ctxt);
1101 close(fd);
1102 return(0);
1103}
1104
1105/**
1106 * xmlNanoHTTPSave:
1107 * @ctxt: the HTTP context
1108 * @filename: the filename where the content should be saved
1109 *
1110 * This function saves the output of the HTTP transaction to a file
1111 * It closes and free the context at the end
1112 *
1113 * Returns -1 in case of failure, 0 incase of success.
1114 */
1115int
1116xmlNanoHTTPSave(void *ctxt, const char *filename) {
1117 char buf[4096];
1118 int fd;
1119 int len;
1120
1121 if (ctxt == NULL) return(-1);
1122
1123 if (!strcmp(filename, "-"))
1124 fd = 0;
1125 else {
1126 fd = open(filename, O_CREAT | O_WRONLY);
1127 if (fd < 0) {
1128 xmlNanoHTTPClose(ctxt);
1129 return(-1);
1130 }
1131 }
1132
1133 while ((len = xmlNanoHTTPRead(ctxt, buf, sizeof(buf))) > 0) {
1134 write(fd, buf, len);
1135 }
1136
1137 xmlNanoHTTPClose(ctxt);
1138 return(0);
1139}
1140
1141/**
1142 * xmlNanoHTTPReturnCode:
1143 * @ctx: the HTTP context
1144 *
1145 * Returns the HTTP return code for the request.
1146 */
1147int
1148xmlNanoHTTPReturnCode(void *ctx) {
1149 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1150
1151 if (ctxt == NULL) return(-1);
1152
1153 return(ctxt->returnValue);
1154}
1155
1156/**
1157 * xmlNanoHTTPAuthHeader:
1158 * @ctx: the HTTP context
1159 *
1160 * Returns the stashed value of the WWW-Authenticate or Proxy-Authenticate
1161 * header.
1162 */
1163const char *
1164xmlNanoHTTPAuthHeader(void *ctx) {
1165 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1166
1167 if (ctxt == NULL) return(NULL);
1168
1169 return(ctxt->authHeader);
1170}
1171
1172#ifdef STANDALONE
1173int main(int argc, char **argv) {
1174 char *contentType = NULL;
1175
1176 if (argv[1] != NULL) {
1177 if (argv[2] != NULL)
1178 xmlNanoHTTPFetch(argv[1], argv[2], &contentType);
1179 else
1180 xmlNanoHTTPFetch(argv[1], "-", &contentType);
1181 if (contentType != NULL) xmlFree(contentType);
1182 } else {
1183 xmlGenericError(xmlGenericErrorContext,
1184 "%s: minimal HTTP GET implementation\n", argv[0]);
1185 xmlGenericError(xmlGenericErrorContext,
1186 "\tusage %s [ URL [ filename ] ]\n", argv[0]);
1187 }
1188 xmlNanoHTTPCleanup();
1189 xmlMemoryDump();
1190 return(0);
1191}
1192#endif /* STANDALONE */
1193#else /* !LIBXML_HTTP_ENABLED */
1194#ifdef STANDALONE
1195#include <stdio.h>
1196int main(int argc, char **argv) {
1197 xmlGenericError(xmlGenericErrorContext,
1198 "%s : HTTP support not compiled in\n", argv[0]);
1199 return(0);
1200}
1201#endif /* STANDALONE */
1202#endif /* LIBXML_HTTP_ENABLED */