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