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