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