Annotation of XML/nanohttp.c, revision 1.22
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) ) {
1.21 veillard 659: unsigned int len; /* was socklen_t barfed on some systems :-( */
1.19 daniel 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.22 ! veillard 775: if (ctxt->port != 80)
1.16 daniel 776: #ifdef HAVE_SNPRINTF
1.12 daniel 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: sprintf(buf,
782: "GET http://%s:%d%s HTTP/1.0\r\nHost: %s\r\n\r\n",
783: ctxt->hostname, ctxt->port, ctxt->path, ctxt->hostname);
1.22 ! veillard 784: #endif
1.12 daniel 785: else
1.22 ! veillard 786: #ifdef HAVE_SNPRINTF
! 787: snprintf(buf, sizeof(buf),"GET http://%s%s HTTP/1.0\r\nHost: %s\r\n\r\n",
! 788: ctxt->hostname, ctxt->path, ctxt->hostname);
! 789: #else
1.12 daniel 790: sprintf(buf, "GET http://%s%s HTTP/1.0\r\nHost: %s\r\n\r\n",
791: ctxt->hostname, ctxt->path, ctxt->hostname);
792: #endif
793: #ifdef DEBUG_HTTP
794: if (ctxt->port != 80)
795: printf("-> Proxy GET http://%s:%d%s HTTP/1.0\n-> Host: %s\n\n",
796: ctxt->hostname, ctxt->port, ctxt->path, ctxt->hostname);
797: else
798: printf("-> Proxy GET http://%s%s HTTP/1.0\n-> Host: %s\n\n",
799: ctxt->hostname, ctxt->path, ctxt->hostname);
800: #endif
801: } else {
1.7 daniel 802: #ifdef HAVE_SNPRINTF
1.12 daniel 803: snprintf(buf, sizeof(buf),"GET %s HTTP/1.0\r\nHost: %s\r\n\r\n",
804: ctxt->path, ctxt->hostname);
1.7 daniel 805: #else
1.12 daniel 806: sprintf(buf, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n",
807: ctxt->path, ctxt->hostname);
1.7 daniel 808: #endif
1.5 daniel 809: #ifdef DEBUG_HTTP
1.12 daniel 810: printf("-> GET %s HTTP/1.0\n-> Host: %s\n\n",
811: ctxt->path, ctxt->hostname);
1.5 daniel 812: #endif
1.12 daniel 813: }
1.22 ! veillard 814: buf[sizeof(buf) - 1] = 0;
1.7 daniel 815: ctxt->outptr = ctxt->out = xmlMemStrdup(buf);
1.1 daniel 816: ctxt->state = XML_NANO_HTTP_WRITE;
817: xmlNanoHTTPSend(ctxt);
818: ctxt->state = XML_NANO_HTTP_READ;
819: head = 1;
820:
821: while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
822: if (head && (*p == 0)) {
823: head = 0;
824: ctxt->content = ctxt->inrptr;
1.12 daniel 825: xmlFree(p);
1.1 daniel 826: break;
827: }
828: xmlNanoHTTPScanAnswer(ctxt, p);
829:
1.5 daniel 830: #ifdef DEBUG_HTTP
831: if (p != NULL) printf("<- %s\n", p);
832: #endif
1.7 daniel 833: if (p != NULL) xmlFree(p);
1.1 daniel 834: }
835:
836: if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
837: (ctxt->returnValue < 400)) {
1.5 daniel 838: #ifdef DEBUG_HTTP
839: printf("\nRedirect to: %s\n", ctxt->location);
840: #endif
1.1 daniel 841: while (xmlNanoHTTPRecv(ctxt)) ;
842: if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
843: nbRedirects++;
1.7 daniel 844: redirURL = xmlMemStrdup(ctxt->location);
1.1 daniel 845: xmlNanoHTTPFreeCtxt(ctxt);
846: goto retry;
847: }
848: xmlNanoHTTPFreeCtxt(ctxt);
1.5 daniel 849: #ifdef DEBUG_HTTP
850: printf("Too many redirrects, aborting ...\n");
851: #endif
1.1 daniel 852: return(NULL);
853:
854: }
855:
1.5 daniel 856: if ((contentType != NULL) && (ctxt->contentType != NULL))
1.7 daniel 857: *contentType = xmlMemStrdup(ctxt->contentType);
1.5 daniel 858:
859: #ifdef DEBUG_HTTP
860: if (ctxt->contentType != NULL)
861: printf("\nCode %d, content-type '%s'\n\n",
862: ctxt->returnValue, ctxt->contentType);
863: else
864: printf("\nCode %d, no content-type\n\n",
865: ctxt->returnValue);
866: #endif
1.1 daniel 867:
868: return((void *) ctxt);
869: }
870:
1.5 daniel 871: /**
872: * xmlNanoHTTPRead:
873: * @ctx: the HTTP context
874: * @dest: a buffer
875: * @len: the buffer length
876: *
877: * This function tries to read @len bytes from the existing HTTP connection
878: * and saves them in @dest. This is a blocking call.
879: *
880: * Returns the number of byte read. 0 is an indication of an end of connection.
881: * -1 indicates a parameter error.
882: */
1.1 daniel 883: int
884: xmlNanoHTTPRead(void *ctx, void *dest, int len) {
885: xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
886:
887: if (ctx == NULL) return(-1);
888: if (dest == NULL) return(-1);
889: if (len <= 0) return(0);
890:
891: while (ctxt->inptr - ctxt->inrptr < len) {
892: if (xmlNanoHTTPRecv(ctxt) == 0) break;
893: }
894: if (ctxt->inptr - ctxt->inrptr < len)
895: len = ctxt->inptr - ctxt->inrptr;
896: memcpy(dest, ctxt->inrptr, len);
897: ctxt->inrptr += len;
898: return(len);
899: }
900:
1.5 daniel 901: /**
902: * xmlNanoHTTPClose:
903: * @ctx: the HTTP context
904: *
905: * This function closes an HTTP context, it ends up the connection and
906: * free all data related to it.
907: */
1.1 daniel 908: void
909: xmlNanoHTTPClose(void *ctx) {
910: xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
911:
912: if (ctx == NULL) return;
913:
914: xmlNanoHTTPFreeCtxt(ctxt);
915: }
916:
1.8 daniel 917: #ifndef DEBUG_HTTP
918: #define DEBUG_HTTP
919: #endif
1.5 daniel 920: /**
1.6 daniel 921: * xmlNanoHTTPMethod:
922: * @URL: The URL to load
923: * @method: the HTTP method to use
924: * @input: the input string if any
925: * @contentType: the Content-Type information IN and OUT
926: * @headers: the extra headers
927: *
928: * This function try to open a connection to the indicated resource
929: * via HTTP using the given @method, adding the given extra headers
930: * and the input buffer for the request content.
931: *
932: * Returns NULL in case of failure, otherwise a request handler.
933: * The contentType, if provided must be freed by the caller
934: */
935:
1.18 daniel 936: void*
1.6 daniel 937: xmlNanoHTTPMethod(const char *URL, const char *method, const char *input,
938: char **contentType, const char *headers) {
939: xmlNanoHTTPCtxtPtr ctxt;
940: char buf[20000];
941: int ret;
942: char *p;
943: int head;
944: int nbRedirects = 0;
945: char *redirURL = NULL;
946:
947: if (URL == NULL) return(NULL);
948: if (method == NULL) method = "GET";
949: if (contentType != NULL) *contentType = NULL;
950:
951: retry:
952: if (redirURL == NULL)
953: ctxt = xmlNanoHTTPNewCtxt(URL);
954: else {
955: ctxt = xmlNanoHTTPNewCtxt(redirURL);
1.7 daniel 956: xmlFree(redirURL);
1.6 daniel 957: redirURL = NULL;
958: }
959:
960: if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
961: xmlNanoHTTPFreeCtxt(ctxt);
1.7 daniel 962: if (redirURL != NULL) xmlFree(redirURL);
1.6 daniel 963: return(NULL);
964: }
965: if (ctxt->hostname == NULL) {
966: xmlNanoHTTPFreeCtxt(ctxt);
967: return(NULL);
968: }
969: ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
970: if (ret < 0) {
971: xmlNanoHTTPFreeCtxt(ctxt);
972: return(NULL);
973: }
974: ctxt->fd = ret;
975:
976: if (input == NULL) {
977: if (headers == NULL) {
978: if ((contentType == NULL) || (*contentType == NULL)) {
1.7 daniel 979: #ifdef HAVE_SNPRINTF
1.6 daniel 980: snprintf(buf, sizeof(buf),
981: "%s %s HTTP/1.0\r\nHost: %s\r\n\r\n",
982: method, ctxt->path, ctxt->hostname);
1.7 daniel 983: #else
984: sprintf(buf,
985: "%s %s HTTP/1.0\r\nHost: %s\r\n\r\n",
986: method, ctxt->path, ctxt->hostname);
987: #endif
1.6 daniel 988: } else {
1.7 daniel 989: #ifdef HAVE_SNPRINTF
1.6 daniel 990: snprintf(buf, sizeof(buf),
991: "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\n\r\n",
992: method, ctxt->path, ctxt->hostname, *contentType);
1.7 daniel 993: #else
994: sprintf(buf,
995: "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\n\r\n",
996: method, ctxt->path, ctxt->hostname, *contentType);
997: #endif
1.6 daniel 998: }
999: } else {
1000: if ((contentType == NULL) || (*contentType == NULL)) {
1.7 daniel 1001: #ifdef HAVE_SNPRINTF
1.6 daniel 1002: snprintf(buf, sizeof(buf),
1003: "%s %s HTTP/1.0\r\nHost: %s\r\n%s\r\n",
1004: method, ctxt->path, ctxt->hostname, headers);
1.7 daniel 1005: #else
1006: sprintf(buf,
1007: "%s %s HTTP/1.0\r\nHost: %s\r\n%s\r\n",
1008: method, ctxt->path, ctxt->hostname, headers);
1009: #endif
1.6 daniel 1010: } else {
1.7 daniel 1011: #ifdef HAVE_SNPRINTF
1.6 daniel 1012: snprintf(buf, sizeof(buf),
1013: "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\n%s\r\n",
1014: method, ctxt->path, ctxt->hostname, *contentType,
1015: headers);
1.7 daniel 1016: #else
1017: sprintf(buf,
1018: "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\n%s\r\n",
1019: method, ctxt->path, ctxt->hostname, *contentType,
1020: headers);
1021: #endif
1.6 daniel 1022: }
1023: }
1024: } else {
1025: int len = strlen(input);
1026: if (headers == NULL) {
1027: if ((contentType == NULL) || (*contentType == NULL)) {
1.7 daniel 1028: #ifdef HAVE_SNPRINTF
1.6 daniel 1029: snprintf(buf, sizeof(buf),
1030: "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s",
1031: method, ctxt->path, ctxt->hostname, len, input);
1.7 daniel 1032: #else
1033: sprintf(buf,
1034: "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s",
1035: method, ctxt->path, ctxt->hostname, len, input);
1036: #endif
1.6 daniel 1037: } else {
1.7 daniel 1038: #ifdef HAVE_SNPRINTF
1.6 daniel 1039: snprintf(buf, sizeof(buf),
1040: "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n\r\n%s",
1041: method, ctxt->path, ctxt->hostname, *contentType, len,
1042: input);
1.7 daniel 1043: #else
1044: sprintf(buf,
1045: "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n\r\n%s",
1046: method, ctxt->path, ctxt->hostname, *contentType, len,
1047: input);
1048: #endif
1.6 daniel 1049: }
1050: } else {
1051: if ((contentType == NULL) || (*contentType == NULL)) {
1.7 daniel 1052: #ifdef HAVE_SNPRINTF
1.6 daniel 1053: snprintf(buf, sizeof(buf),
1054: "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Length: %d\r\n%s\r\n%s",
1055: method, ctxt->path, ctxt->hostname, len,
1056: headers, input);
1.7 daniel 1057: #else
1058: sprintf(buf,
1059: "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Length: %d\r\n%s\r\n%s",
1060: method, ctxt->path, ctxt->hostname, len,
1061: headers, input);
1062: #endif
1.6 daniel 1063: } else {
1.7 daniel 1064: #ifdef HAVE_SNPRINTF
1.6 daniel 1065: snprintf(buf, sizeof(buf),
1066: "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n%s\r\n%s",
1067: method, ctxt->path, ctxt->hostname, *contentType,
1068: len, headers, input);
1.7 daniel 1069: #else
1070: sprintf(buf,
1071: "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n%s\r\n%s",
1072: method, ctxt->path, ctxt->hostname, *contentType,
1073: len, headers, input);
1074: #endif
1.6 daniel 1075: }
1076: }
1077: }
1.22 ! veillard 1078: buf[sizeof(buf) - 1] = 0;
1.6 daniel 1079: #ifdef DEBUG_HTTP
1080: printf("-> %s", buf);
1081: #endif
1.7 daniel 1082: ctxt->outptr = ctxt->out = xmlMemStrdup(buf);
1.6 daniel 1083: ctxt->state = XML_NANO_HTTP_WRITE;
1084: xmlNanoHTTPSend(ctxt);
1085: ctxt->state = XML_NANO_HTTP_READ;
1086: head = 1;
1087:
1088: while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
1089: if (head && (*p == 0)) {
1090: head = 0;
1091: ctxt->content = ctxt->inrptr;
1.7 daniel 1092: if (p != NULL) xmlFree(p);
1.6 daniel 1093: break;
1094: }
1095: xmlNanoHTTPScanAnswer(ctxt, p);
1096:
1097: #ifdef DEBUG_HTTP
1098: if (p != NULL) printf("<- %s\n", p);
1099: #endif
1.7 daniel 1100: if (p != NULL) xmlFree(p);
1.6 daniel 1101: }
1102:
1103: if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
1104: (ctxt->returnValue < 400)) {
1105: #ifdef DEBUG_HTTP
1106: printf("\nRedirect to: %s\n", ctxt->location);
1107: #endif
1108: while (xmlNanoHTTPRecv(ctxt)) ;
1109: if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
1110: nbRedirects++;
1.7 daniel 1111: redirURL = xmlMemStrdup(ctxt->location);
1.6 daniel 1112: xmlNanoHTTPFreeCtxt(ctxt);
1113: goto retry;
1114: }
1115: xmlNanoHTTPFreeCtxt(ctxt);
1116: #ifdef DEBUG_HTTP
1117: printf("Too many redirrects, aborting ...\n");
1118: #endif
1119: return(NULL);
1120:
1121: }
1122:
1123: if ((contentType != NULL) && (ctxt->contentType != NULL))
1.7 daniel 1124: *contentType = xmlMemStrdup(ctxt->contentType);
1125: else if (contentType != NULL)
1126: *contentType = NULL;
1.6 daniel 1127:
1128: #ifdef DEBUG_HTTP
1129: if (ctxt->contentType != NULL)
1130: printf("\nCode %d, content-type '%s'\n\n",
1131: ctxt->returnValue, ctxt->contentType);
1132: else
1133: printf("\nCode %d, no content-type\n\n",
1134: ctxt->returnValue);
1135: #endif
1136:
1137: return((void *) ctxt);
1138: }
1139:
1140: /**
1.5 daniel 1141: * xmlNanoHTTPFetch:
1142: * @URL: The URL to load
1143: * @filename: the filename where the content should be saved
1144: * @contentType: if available the Content-Type information will be
1145: * returned at that location
1146: *
1147: * This function try to fetch the indicated resource via HTTP GET
1148: * and save it's content in the file.
1149: *
1150: * Returns -1 in case of failure, 0 incase of success. The contentType,
1151: * if provided must be freed by the caller
1152: */
1153: int
1154: xmlNanoHTTPFetch(const char *URL, const char *filename, char **contentType) {
1.1 daniel 1155: void *ctxt;
1156: char buf[4096];
1157: int fd;
1158: int len;
1159:
1160: ctxt = xmlNanoHTTPOpen(URL, contentType);
1161: if (ctxt == NULL) return(-1);
1162:
1163: if (!strcmp(filename, "-"))
1164: fd = 0;
1165: else {
1.13 daniel 1166: fd = open(filename, O_CREAT | O_WRONLY, 00644);
1.1 daniel 1167: if (fd < 0) {
1168: xmlNanoHTTPClose(ctxt);
1.5 daniel 1169: if ((contentType != NULL) && (*contentType != NULL)) {
1.7 daniel 1170: xmlFree(*contentType);
1.5 daniel 1171: *contentType = NULL;
1172: }
1.1 daniel 1173: return(-1);
1174: }
1175: }
1176:
1177: while ((len = xmlNanoHTTPRead(ctxt, buf, sizeof(buf))) > 0) {
1178: write(fd, buf, len);
1179: }
1180:
1181: xmlNanoHTTPClose(ctxt);
1.13 daniel 1182: close(fd);
1.1 daniel 1183: return(0);
1.6 daniel 1184: }
1185:
1186: /**
1187: * xmlNanoHTTPSave:
1.8 daniel 1188: * @ctxt: the HTTP context
1.6 daniel 1189: * @filename: the filename where the content should be saved
1190: *
1191: * This function saves the output of the HTTP transaction to a file
1192: * It closes and free the context at the end
1193: *
1194: * Returns -1 in case of failure, 0 incase of success.
1195: */
1196: int
1197: xmlNanoHTTPSave(void *ctxt, const char *filename) {
1198: char buf[4096];
1199: int fd;
1200: int len;
1201:
1202: if (ctxt == NULL) return(-1);
1203:
1204: if (!strcmp(filename, "-"))
1205: fd = 0;
1206: else {
1207: fd = open(filename, O_CREAT | O_WRONLY);
1208: if (fd < 0) {
1209: xmlNanoHTTPClose(ctxt);
1210: return(-1);
1211: }
1212: }
1213:
1214: while ((len = xmlNanoHTTPRead(ctxt, buf, sizeof(buf))) > 0) {
1215: write(fd, buf, len);
1216: }
1217:
1218: xmlNanoHTTPClose(ctxt);
1219: return(0);
1220: }
1221:
1222: /**
1223: * xmlNanoHTTPReturnCode:
1224: * @ctx: the HTTP context
1225: *
1226: * Returns the HTTP return code for the request.
1227: */
1228: int
1229: xmlNanoHTTPReturnCode(void *ctx) {
1230: xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1231:
1232: if (ctxt == NULL) return(-1);
1233:
1234: return(ctxt->returnValue);
1.1 daniel 1235: }
1236:
1237: #ifdef STANDALONE
1238: int main(int argc, char **argv) {
1239: char *contentType = NULL;
1240:
1241: if (argv[1] != NULL) {
1242: if (argv[2] != NULL)
1243: xmlNanoHTTPFetch(argv[1], argv[2], &contentType);
1244: else
1245: xmlNanoHTTPFetch(argv[1], "-", &contentType);
1.7 daniel 1246: if (contentType != NULL) xmlFree(contentType);
1.1 daniel 1247: } else {
1248: printf("%s: minimal HTTP GET implementation\n", argv[0]);
1249: printf("\tusage %s [ URL [ filename ] ]\n", argv[0]);
1250: }
1.12 daniel 1251: xmlNanoHTTPCleanup();
1252: xmlMemoryDump();
1.1 daniel 1253: return(0);
1254: }
1255: #endif /* STANDALONE */
1.17 daniel 1256: #else /* !LIBXML_HTTP_ENABLED */
1257: #ifdef STANDALONE
1258: #include <stdio.h>
1259: int main(int argc, char **argv) {
1260: printf("%s : HTTP support not compiled in\n", argv[0]);
1261: return(0);
1262: }
1263: #endif /* STANDALONE */
1264: #endif /* LIBXML_HTTP_ENABLED */
Webmaster