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