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