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