nanohttp.c 47.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/*
 * nanohttp.c: minimalist HTTP GET implementation to fetch external subsets.
 *             focuses on size, streamability, reentrancy and portability
 *
 * This is clearly not a general purpose HTTP implementation
 * If you look for one, check:
 *         http://www.w3.org/Library/
 *
 * See Copyright for the status of this software.
 *
11
 * daniel@veillard.com
12
 */
13

14
#define NEED_SOCKETS
15
#define IN_LIBXML
16
#include "libxml.h"
17 18 19 20 21 22 23 24 25 26

#ifdef LIBXML_HTTP_ENABLED
#include <string.h>

#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
27 28 29
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
30 31 32 33 34 35 36 37 38 39 40 41
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
42
#ifdef HAVE_RESOLV_H
43 44 45
#ifdef HAVE_ARPA_NAMESER_H
#include <arpa/nameser.h>
#endif
46 47
#include <resolv.h>
#endif
48
#ifdef HAVE_FCNTL_H
49
#include <fcntl.h>
50 51 52 53 54 55 56
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
57
#ifndef HAVE_POLL_H
58 59 60
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
61 62 63
#else
#include <poll.h>
#endif
64 65 66
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
67 68 69 70
#ifdef HAVE_ZLIB_H
#include <zlib.h>
#endif

71 72 73

#ifdef VMS
#include <stropts>
74
#define XML_SOCKLEN_T unsigned int
75 76
#endif

77
#if defined(__MINGW32__) || defined(_WIN32_WCE)
78
#ifndef _WINSOCKAPI_
79
#define _WINSOCKAPI_
80
#endif
81 82
#include <wsockcompat.h>
#include <winsock2.h>
83 84
#undef XML_SOCKLEN_T
#define XML_SOCKLEN_T unsigned int
85 86
#endif

87
#include <libxml/globals.h>
88
#include <libxml/xmlerror.h>
89 90 91
#include <libxml/xmlmemory.h>
#include <libxml/parser.h> /* for xmlStr(n)casecmp() */
#include <libxml/nanohttp.h>
92
#include <libxml/globals.h>
93
#include <libxml/uri.h>
94 95 96 97 98

/**
 * A couple portability macros
 */
#ifndef _WINSOCKAPI_
99
#if !defined(__BEOS__) || defined(__HAIKU__)
100
#define closesocket(s) close(s)
101
#endif
102
#define SOCKET int
103
#define INVALID_SOCKET (-1)
104 105
#endif

106 107 108 109 110 111
#ifdef __BEOS__
#ifndef PF_INET
#define PF_INET AF_INET
#endif
#endif

112 113
#ifndef XML_SOCKLEN_T
#define XML_SOCKLEN_T unsigned int
114
#endif
115

116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
#ifdef STANDALONE
#define DEBUG_HTTP
#define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n)
#define xmlStrcasecmpi(a, b) strcasecmp((char *)a, (char *)b)
#endif

#define XML_NANO_HTTP_MAX_REDIR	10

#define XML_NANO_HTTP_CHUNK	4096

#define XML_NANO_HTTP_CLOSED	0
#define XML_NANO_HTTP_WRITE	1
#define XML_NANO_HTTP_READ	2
#define XML_NANO_HTTP_NONE	4

typedef struct xmlNanoHTTPCtxt {
    char *protocol;	/* the protocol name */
    char *hostname;	/* the host name */
    int port;		/* the port */
    char *path;		/* the path within the URL */
136
    char *query;	/* the query string */
137 138 139 140 141 142 143 144 145 146 147
    SOCKET fd;		/* the file descriptor for the socket */
    int state;		/* WRITE / READ / CLOSED */
    char *out;		/* buffer sent (zero terminated) */
    char *outptr;	/* index within the buffer sent */
    char *in;		/* the receiving buffer */
    char *content;	/* the start of the content */
    char *inptr;	/* the next byte to read from network */
    char *inrptr;	/* the next byte to give back to the client */
    int inlen;		/* len of the input buffer */
    int last;		/* return code for last operation */
    int returnValue;	/* the protocol return value */
148
    int version;        /* the protocol version */
149
    int ContentLength;  /* specified content length from HTTP header */
150 151 152
    char *contentType;	/* the MIME type for the input */
    char *location;	/* the new URL in case of redirect */
    char *authHeader;	/* contents of {WWW,Proxy}-Authenticate header */
153
    char *encoding;	/* encoding extracted from the contentType */
154
    char *mimeType;	/* Mime-Type extracted from the contentType */
155 156 157 158
#ifdef HAVE_ZLIB_H
    z_stream *strm;	/* Zlib stream object */
    int usesGzip;	/* "Content-Encoding: gzip" was detected */
#endif
159 160 161 162 163 164 165
} xmlNanoHTTPCtxt, *xmlNanoHTTPCtxtPtr;

static int initialized = 0;
static char *proxy = NULL;	 /* the proxy name if any */
static int proxyPort;	/* the proxy port if any */
static unsigned int timeout = 60;/* the select() timeout in seconds */

166
static int xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len );
167

168 169 170 171 172 173 174 175 176 177 178 179
/**
 * xmlHTTPErrMemory:
 * @extra:  extra informations
 *
 * Handle an out of memory condition
 */
static void
xmlHTTPErrMemory(const char *extra)
{
    __xmlSimpleError(XML_FROM_HTTP, XML_ERR_NO_MEMORY, NULL, NULL, extra);
}

180 181 182
/**
 * A portability function
 */
183
static int socket_errno(void) {
184 185 186 187 188 189 190
#ifdef _WINSOCKAPI_
    return(WSAGetLastError());
#else
    return(errno);
#endif
}

191
#ifdef SUPPORT_IP6
192 193
static
int have_ipv6(void) {
194
    SOCKET s;
195 196

    s = socket (AF_INET6, SOCK_STREAM, 0);
197
    if (s != INVALID_SOCKET) {
198 199 200 201 202 203 204
	close (s);
	return (1);
    }
    return (0);
}
#endif

205 206 207 208 209 210 211 212 213 214 215
/**
 * xmlNanoHTTPInit:
 *
 * Initialize the HTTP protocol layer.
 * Currently it just checks for proxy informations
 */

void
xmlNanoHTTPInit(void) {
    const char *env;
#ifdef _WINSOCKAPI_
216
    WSADATA wsaData;
217 218 219 220 221 222 223 224 225 226 227 228 229
#endif

    if (initialized)
	return;

#ifdef _WINSOCKAPI_
    if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
	return;
#endif

    if (proxy == NULL) {
	proxyPort = 80;
	env = getenv("no_proxy");
230
	if (env && ((env[0] == '*') && (env[1] == 0)))
231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
	    goto done;
	env = getenv("http_proxy");
	if (env != NULL) {
	    xmlNanoHTTPScanProxy(env);
	    goto done;
	}
	env = getenv("HTTP_PROXY");
	if (env != NULL) {
	    xmlNanoHTTPScanProxy(env);
	    goto done;
	}
    }
done:
    initialized = 1;
}

/**
248
 * xmlNanoHTTPCleanup:
249 250 251 252 253 254
 *
 * Cleanup the HTTP protocol layer.
 */

void
xmlNanoHTTPCleanup(void) {
255
    if (proxy != NULL) {
256
	xmlFree(proxy);
257 258
	proxy = NULL;
    }
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
#ifdef _WINSOCKAPI_
    if (initialized)
	WSACleanup();
#endif
    initialized = 0;
    return;
}

/**
 * xmlNanoHTTPScanURL:
 * @ctxt:  an HTTP context
 * @URL:  The URL used to initialize the context
 *
 * (Re)Initialize an HTTP context by parsing the URL and finding
 * the protocol host port and path it indicates.
 */

static void
xmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt, const char *URL) {
278
    xmlURIPtr uri;
279 280
    int len;

281 282 283
    /*
     * Clear any existing data from the context
     */
284
    if (ctxt->protocol != NULL) {
285 286 287
        xmlFree(ctxt->protocol);
	ctxt->protocol = NULL;
    }
288
    if (ctxt->hostname != NULL) {
289 290 291
        xmlFree(ctxt->hostname);
	ctxt->hostname = NULL;
    }
292
    if (ctxt->path != NULL) {
293 294 295
        xmlFree(ctxt->path);
	ctxt->path = NULL;
    }
296
    if (ctxt->query != NULL) {
297 298 299
        xmlFree(ctxt->query);
	ctxt->query = NULL;
    }
300
    if (URL == NULL) return;
301

302
    uri = xmlParseURIRaw(URL, 1);
303 304
    if (uri == NULL)
	return;
305

306 307 308
    if ((uri->scheme == NULL) || (uri->server == NULL)) {
	xmlFreeURI(uri);
	return;
309
    }
310

311
    ctxt->protocol = xmlMemStrdup(uri->scheme);
312 313 314 315 316 317 318 319 320
    /* special case of IPv6 addresses, the [] need to be removed */
    if ((uri->server != NULL) && (*uri->server == '[')) {
        len = strlen(uri->server);
	if ((len > 2) && (uri->server[len - 1] == ']')) {
	    ctxt->hostname = (char *) xmlCharStrndup(uri->server + 1, len -2);
	} else
	    ctxt->hostname = xmlMemStrdup(uri->server);
    } else
	ctxt->hostname = xmlMemStrdup(uri->server);
321 322 323 324
    if (uri->path != NULL)
	ctxt->path = xmlMemStrdup(uri->path);
    else
	ctxt->path = xmlMemStrdup("/");
325 326
    if (uri->query != NULL)
	ctxt->query = xmlMemStrdup(uri->query);
327 328 329 330
    if (uri->port != 0)
	ctxt->port = uri->port;

    xmlFreeURI(uri);
331 332 333 334 335 336 337 338 339 340 341 342 343 344
}

/**
 * xmlNanoHTTPScanProxy:
 * @URL:  The proxy URL used to initialize the proxy context
 *
 * (Re)Initialize the HTTP Proxy context by parsing the URL and finding
 * the protocol host port it indicates.
 * Should be like http://myproxy/ or http://myproxy:3128/
 * A NULL URL cleans up proxy informations.
 */

void
xmlNanoHTTPScanProxy(const char *URL) {
345
    xmlURIPtr uri;
346

347
    if (proxy != NULL) {
348 349 350
        xmlFree(proxy);
	proxy = NULL;
    }
351 352
    proxyPort = 0;

353 354 355 356 357 358 359 360 361
#ifdef DEBUG_HTTP
    if (URL == NULL)
	xmlGenericError(xmlGenericErrorContext,
		"Removing HTTP proxy info\n");
    else
	xmlGenericError(xmlGenericErrorContext,
		"Using HTTP proxy %s\n", URL);
#endif
    if (URL == NULL) return;
362

363
    uri = xmlParseURIRaw(URL, 1);
364 365 366 367 368 369
    if ((uri == NULL) || (uri->scheme == NULL) ||
	(strcmp(uri->scheme, "http")) || (uri->server == NULL)) {
	__xmlIOErr(XML_FROM_HTTP, XML_HTTP_URL_SYNTAX, "Syntax Error\n");
	if (uri != NULL)
	    xmlFreeURI(uri);
	return;
370
    }
371

372 373 374 375 376
    proxy = xmlMemStrdup(uri->server);
    if (uri->port != 0)
	proxyPort = uri->port;

    xmlFreeURI(uri);
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
}

/**
 * xmlNanoHTTPNewCtxt:
 * @URL:  The URL used to initialize the context
 *
 * Allocate and initialize a new HTTP context.
 *
 * Returns an HTTP context or NULL in case of error.
 */

static xmlNanoHTTPCtxtPtr
xmlNanoHTTPNewCtxt(const char *URL) {
    xmlNanoHTTPCtxtPtr ret;

    ret = (xmlNanoHTTPCtxtPtr) xmlMalloc(sizeof(xmlNanoHTTPCtxt));
393 394 395 396
    if (ret == NULL) {
        xmlHTTPErrMemory("allocating context");
        return(NULL);
    }
397 398 399 400

    memset(ret, 0, sizeof(xmlNanoHTTPCtxt));
    ret->port = 80;
    ret->returnValue = 0;
401
    ret->fd = INVALID_SOCKET;
402
    ret->ContentLength = -1;
403

404
    xmlNanoHTTPScanURL(ret, URL);
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421

    return(ret);
}

/**
 * xmlNanoHTTPFreeCtxt:
 * @ctxt:  an HTTP context
 *
 * Frees the context after closing the connection.
 */

static void
xmlNanoHTTPFreeCtxt(xmlNanoHTTPCtxtPtr ctxt) {
    if (ctxt == NULL) return;
    if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
    if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
    if (ctxt->path != NULL) xmlFree(ctxt->path);
422
    if (ctxt->query != NULL) xmlFree(ctxt->query);
423 424 425
    if (ctxt->out != NULL) xmlFree(ctxt->out);
    if (ctxt->in != NULL) xmlFree(ctxt->in);
    if (ctxt->contentType != NULL) xmlFree(ctxt->contentType);
426
    if (ctxt->encoding != NULL) xmlFree(ctxt->encoding);
427
    if (ctxt->mimeType != NULL) xmlFree(ctxt->mimeType);
428 429
    if (ctxt->location != NULL) xmlFree(ctxt->location);
    if (ctxt->authHeader != NULL) xmlFree(ctxt->authHeader);
430 431 432 433 434 435 436
#ifdef HAVE_ZLIB_H
    if (ctxt->strm != NULL) {
	inflateEnd(ctxt->strm);
	xmlFree(ctxt->strm);
    }
#endif

437
    ctxt->state = XML_NANO_HTTP_NONE;
438 439
    if (ctxt->fd != INVALID_SOCKET) closesocket(ctxt->fd);
    ctxt->fd = INVALID_SOCKET;
440 441 442 443 444 445 446 447
    xmlFree(ctxt);
}

/**
 * xmlNanoHTTPSend:
 * @ctxt:  an HTTP context
 *
 * Send the input needed to initiate the processing on the server side
448
 * Returns number of bytes sent or -1 on error.
449 450
 */

451
static int
452 453 454 455 456 457 458 459 460
xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt, const char *xmt_ptr, int outlen)
{
    int total_sent = 0;
#ifdef HAVE_POLL_H
    struct pollfd p;
#else
    struct timeval tv;
    fd_set wfd;
#endif
461

462
    if ((ctxt->state & XML_NANO_HTTP_WRITE) && (xmt_ptr != NULL)) {
463
        while (total_sent < outlen) {
464
            int nsent = send(ctxt->fd, SEND_ARG2_CAST (xmt_ptr + total_sent),
465 466 467
                             outlen - total_sent, 0);

            if (nsent > 0)
468
                total_sent += nsent;
469
            else if ((nsent == -1) &&
470
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
471
                     (socket_errno() != EAGAIN) &&
472
#endif
473 474 475 476 477 478 479 480 481 482 483 484 485
                     (socket_errno() != EWOULDBLOCK)) {
                __xmlIOErr(XML_FROM_HTTP, 0, "send failed\n");
                if (total_sent == 0)
                    total_sent = -1;
                break;
            } else {
                /*
                 * No data sent
                 * Since non-blocking sockets are used, wait for
                 * socket to be writable or default timeout prior
                 * to retrying.
                 */
#ifndef HAVE_POLL_H
spadix's avatar
spadix committed
486
#ifndef _WINSOCKAPI_
487 488
                if (ctxt->fd > FD_SETSIZE)
                    return -1;
spadix's avatar
spadix committed
489
#endif
490 491 492 493

                tv.tv_sec = timeout;
                tv.tv_usec = 0;
                FD_ZERO(&wfd);
494 495 496 497
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4018)
#endif
498
                FD_SET(ctxt->fd, &wfd);
499 500 501
#ifdef _MSC_VER
#pragma warning(pop)
#endif
502 503 504 505 506 507 508 509
                (void) select(ctxt->fd + 1, NULL, &wfd, NULL, &tv);
#else
                p.fd = ctxt->fd;
                p.events = POLLOUT;
                (void) poll(&p, 1, timeout * 1000);
#endif /* !HAVE_POLL_H */
            }
        }
510
    }
511 512

    return total_sent;
513 514 515 516 517 518 519 520 521 522 523 524 525
}

/**
 * xmlNanoHTTPRecv:
 * @ctxt:  an HTTP context
 *
 * Read information coming from the HTTP connection.
 * This is a blocking call (but it blocks in select(), not read()).
 *
 * Returns the number of byte read or -1 in case of error.
 */

static int
526 527 528 529 530
xmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt)
{
#ifdef HAVE_POLL_H
    struct pollfd p;
#else
531 532
    fd_set rfd;
    struct timeval tv;
533
#endif
534 535 536


    while (ctxt->state & XML_NANO_HTTP_READ) {
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
        if (ctxt->in == NULL) {
            ctxt->in = (char *) xmlMallocAtomic(65000 * sizeof(char));
            if (ctxt->in == NULL) {
                xmlHTTPErrMemory("allocating input");
                ctxt->last = -1;
                return (-1);
            }
            ctxt->inlen = 65000;
            ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in;
        }
        if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) {
            int delta = ctxt->inrptr - ctxt->in;
            int len = ctxt->inptr - ctxt->inrptr;

            memmove(ctxt->in, ctxt->inrptr, len);
            ctxt->inrptr -= delta;
            ctxt->content -= delta;
            ctxt->inptr -= delta;
        }
556
        if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) {
557 558 559 560
            int d_inptr = ctxt->inptr - ctxt->in;
            int d_content = ctxt->content - ctxt->in;
            int d_inrptr = ctxt->inrptr - ctxt->in;
            char *tmp_ptr = ctxt->in;
561

562
            ctxt->inlen *= 2;
563
            ctxt->in = (char *) xmlRealloc(tmp_ptr, ctxt->inlen);
564 565 566 567 568 569
            if (ctxt->in == NULL) {
                xmlHTTPErrMemory("allocating input buffer");
                xmlFree(tmp_ptr);
                ctxt->last = -1;
                return (-1);
            }
570 571 572
            ctxt->inptr = ctxt->in + d_inptr;
            ctxt->content = ctxt->in + d_content;
            ctxt->inrptr = ctxt->in + d_inrptr;
573 574 575 576 577 578 579 580 581 582 583 584 585
        }
        ctxt->last = recv(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK, 0);
        if (ctxt->last > 0) {
            ctxt->inptr += ctxt->last;
            return (ctxt->last);
        }
        if (ctxt->last == 0) {
            return (0);
        }
        if (ctxt->last == -1) {
            switch (socket_errno()) {
                case EINPROGRESS:
                case EWOULDBLOCK:
586
#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
587
                case EAGAIN:
588
#endif
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609
                    break;

                case ECONNRESET:
                case ESHUTDOWN:
                    return (0);

                default:
                    __xmlIOErr(XML_FROM_HTTP, 0, "recv failed\n");
                    return (-1);
            }
        }
#ifdef HAVE_POLL_H
        p.fd = ctxt->fd;
        p.events = POLLIN;
        if ((poll(&p, 1, timeout * 1000) < 1)
#if defined(EINTR)
            && (errno != EINTR)
#endif
            )
            return (0);
#else /* !HAVE_POLL_H */
spadix's avatar
spadix committed
610
#ifndef _WINSOCKAPI_
611 612
        if (ctxt->fd > FD_SETSIZE)
            return 0;
spadix's avatar
spadix committed
613
#endif
614

615 616 617
        tv.tv_sec = timeout;
        tv.tv_usec = 0;
        FD_ZERO(&rfd);
618

619 620 621 622
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4018)
#endif
623 624 625

        FD_SET(ctxt->fd, &rfd);

626 627 628
#ifdef _MSC_VER
#pragma warning(pop)
#endif
629 630

        if ((select(ctxt->fd + 1, &rfd, NULL, NULL, &tv) < 1)
631
#if defined(EINTR)
632
            && (errno != EINTR)
633
#endif
634 635 636
            )
            return (0);
#endif /* !HAVE_POLL_H */
637
    }
638
    return (0);
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655
}

/**
 * xmlNanoHTTPReadLine:
 * @ctxt:  an HTTP context
 *
 * Read one line in the HTTP server output, usually for extracting
 * the HTTP protocol informations from the answer header.
 *
 * Returns a newly allocated string with a copy of the line, or NULL
 *         which indicate the end of the input.
 */

static char *
xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt) {
    char buf[4096];
    char *bp = buf;
656
    int	rc;
657

658 659
    while (bp - buf < 4095) {
	if (ctxt->inrptr == ctxt->inptr) {
660
	    if ( (rc = xmlNanoHTTPRecv(ctxt)) == 0) {
661 662 663 664 665 666
		if (bp == buf)
		    return(NULL);
		else
		    *bp = 0;
		return(xmlMemStrdup(buf));
	    }
667 668 669
	    else if ( rc == -1 ) {
	        return ( NULL );
	    }
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
	}
	*bp = *ctxt->inrptr++;
	if (*bp == '\n') {
	    *bp = 0;
	    return(xmlMemStrdup(buf));
	}
	if (*bp != '\r')
	    bp++;
    }
    buf[4095] = 0;
    return(xmlMemStrdup(buf));
}


/**
 * xmlNanoHTTPScanAnswer:
 * @ctxt:  an HTTP context
 * @line:  an HTTP header line
 *
 * Try to extract useful informations from the server answer.
 * We currently parse and process:
 *  - The HTTP revision/ return code
692
 *  - The Content-Type, Mime-Type and charset used
693
 *  - The Location for redirect processing.
694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
 *
 * Returns -1 in case of failure, the file descriptor number otherwise
 */

static void
xmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt, const char *line) {
    const char *cur = line;

    if (line == NULL) return;

    if (!strncmp(line, "HTTP/", 5)) {
        int version = 0;
	int ret = 0;

	cur += 5;
	while ((*cur >= '0') && (*cur <= '9')) {
	    version *= 10;
	    version += *cur - '0';
	    cur++;
	}
	if (*cur == '.') {
	    cur++;
	    if ((*cur >= '0') && (*cur <= '9')) {
		version *= 10;
		version += *cur - '0';
		cur++;
	    }
	    while ((*cur >= '0') && (*cur <= '9'))
		cur++;
	} else
	    version *= 10;
	if ((*cur != ' ') && (*cur != '\t')) return;
	while ((*cur == ' ') || (*cur == '\t')) cur++;
	if ((*cur < '0') || (*cur > '9')) return;
	while ((*cur >= '0') && (*cur <= '9')) {
	    ret *= 10;
	    ret += *cur - '0';
	    cur++;
	}
	if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return;
	ctxt->returnValue = ret;
735
        ctxt->version = version;
736
    } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Content-Type:", 13)) {
737
        const xmlChar *charset, *last, *mime;
738 739 740 741 742
        cur += 13;
	while ((*cur == ' ') || (*cur == '\t')) cur++;
	if (ctxt->contentType != NULL)
	    xmlFree(ctxt->contentType);
	ctxt->contentType = xmlMemStrdup(cur);
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761
	mime = (const xmlChar *) cur;
	last = mime;
	while ((*last != 0) && (*last != ' ') && (*last != '\t') &&
	       (*last != ';') && (*last != ','))
	    last++;
	if (ctxt->mimeType != NULL)
	    xmlFree(ctxt->mimeType);
	ctxt->mimeType = (char *) xmlStrndup(mime, last - mime);
	charset = xmlStrstr(BAD_CAST ctxt->contentType, BAD_CAST "charset=");
	if (charset != NULL) {
	    charset += 8;
	    last = charset;
	    while ((*last != 0) && (*last != ' ') && (*last != '\t') &&
	           (*last != ';') && (*last != ','))
		last++;
	    if (ctxt->encoding != NULL)
	        xmlFree(ctxt->encoding);
	    ctxt->encoding = (char *) xmlStrndup(charset, last - charset);
	}
762
    } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"ContentType:", 12)) {
763
        const xmlChar *charset, *last, *mime;
764 765 766 767
        cur += 12;
	if (ctxt->contentType != NULL) return;
	while ((*cur == ' ') || (*cur == '\t')) cur++;
	ctxt->contentType = xmlMemStrdup(cur);
768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
	mime = (const xmlChar *) cur;
	last = mime;
	while ((*last != 0) && (*last != ' ') && (*last != '\t') &&
	       (*last != ';') && (*last != ','))
	    last++;
	if (ctxt->mimeType != NULL)
	    xmlFree(ctxt->mimeType);
	ctxt->mimeType = (char *) xmlStrndup(mime, last - mime);
	charset = xmlStrstr(BAD_CAST ctxt->contentType, BAD_CAST "charset=");
	if (charset != NULL) {
	    charset += 8;
	    last = charset;
	    while ((*last != 0) && (*last != ' ') && (*last != '\t') &&
	           (*last != ';') && (*last != ','))
		last++;
	    if (ctxt->encoding != NULL)
	        xmlFree(ctxt->encoding);
	    ctxt->encoding = (char *) xmlStrndup(charset, last - charset);
	}
787 788 789 790 791
    } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Location:", 9)) {
        cur += 9;
	while ((*cur == ' ') || (*cur == '\t')) cur++;
	if (ctxt->location != NULL)
	    xmlFree(ctxt->location);
792 793
	if (*cur == '/') {
	    xmlChar *tmp_http = xmlStrdup(BAD_CAST "http://");
794
	    xmlChar *tmp_loc =
795
	        xmlStrcat(tmp_http, (const xmlChar *) ctxt->hostname);
796
	    ctxt->location =
797 798 799 800
	        (char *) xmlStrcat (tmp_loc, (const xmlChar *) cur);
	} else {
	    ctxt->location = xmlMemStrdup(cur);
	}
801 802 803 804 805 806 807 808 809 810 811 812
    } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"WWW-Authenticate:", 17)) {
        cur += 17;
	while ((*cur == ' ') || (*cur == '\t')) cur++;
	if (ctxt->authHeader != NULL)
	    xmlFree(ctxt->authHeader);
	ctxt->authHeader = xmlMemStrdup(cur);
    } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Proxy-Authenticate:", 19)) {
        cur += 19;
	while ((*cur == ' ') || (*cur == '\t')) cur++;
	if (ctxt->authHeader != NULL)
	    xmlFree(ctxt->authHeader);
	ctxt->authHeader = xmlMemStrdup(cur);
813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832
#ifdef HAVE_ZLIB_H
    } else if ( !xmlStrncasecmp( BAD_CAST line, BAD_CAST"Content-Encoding:", 17) ) {
	cur += 17;
	while ((*cur == ' ') || (*cur == '\t')) cur++;
	if ( !xmlStrncasecmp( BAD_CAST cur, BAD_CAST"gzip", 4) ) {
	    ctxt->usesGzip = 1;

	    ctxt->strm = xmlMalloc(sizeof(z_stream));

	    if (ctxt->strm != NULL) {
		ctxt->strm->zalloc = Z_NULL;
		ctxt->strm->zfree = Z_NULL;
		ctxt->strm->opaque = Z_NULL;
		ctxt->strm->avail_in = 0;
		ctxt->strm->next_in = Z_NULL;

		inflateInit2( ctxt->strm, 31 );
	    }
	}
#endif
833 834 835
    } else if ( !xmlStrncasecmp( BAD_CAST line, BAD_CAST"Content-Length:", 15) ) {
	cur += 15;
	ctxt->ContentLength = strtol( cur, NULL, 10 );
836 837 838 839 840
    }
}

/**
 * xmlNanoHTTPConnectAttempt:
841
 * @addr:  a socket address structure
842 843 844 845 846 847 848 849
 *
 * Attempt a connection to the given IP:port endpoint. It forces
 * non-blocking semantic on the socket, and allow 60 seconds for
 * the host to answer.
 *
 * Returns -1 in case of failure, the file descriptor number otherwise
 */

850
static SOCKET
851
xmlNanoHTTPConnectAttempt(struct sockaddr *addr)
852
{
853
#ifndef HAVE_POLL_H
854
    fd_set wfd;
855 856 857
#ifdef _WINSOCKAPI_
    fd_set xfd;
#endif
858
    struct timeval tv;
859 860 861
#else /* !HAVE_POLL_H */
    struct pollfd p;
#endif /* !HAVE_POLL_H */
862
    int status;
863

864
    int addrlen;
865

866
    SOCKET s;
867

868 869
#ifdef SUPPORT_IP6
    if (addr->sa_family == AF_INET6) {
870 871 872
        s = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
        addrlen = sizeof(struct sockaddr_in6);
    } else
873 874
#endif
    {
875 876
        s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
        addrlen = sizeof(struct sockaddr_in);
877
    }
878
    if (s == INVALID_SOCKET) {
879
#ifdef DEBUG_HTTP
880
        perror("socket");
881
#endif
882
        __xmlIOErr(XML_FROM_HTTP, 0, "socket failed\n");
883
        return INVALID_SOCKET;
884 885 886
    }
#ifdef _WINSOCKAPI_
    {
887
        u_long one = 1;
888

889
        status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0;
890 891 892 893
    }
#else /* _WINSOCKAPI_ */
#if defined(VMS)
    {
894 895 896
        int enable = 1;

        status = ioctl(s, FIONBIO, &enable);
897 898
    }
#else /* VMS */
899
#if defined(__BEOS__) && !defined(__HAIKU__)
900 901 902 903 904 905 906
    {
        bool noblock = true;

        status =
            setsockopt(s, SOL_SOCKET, SO_NONBLOCK, &noblock,
                       sizeof(noblock));
    }
907
#else /* __BEOS__ */
908 909
    if ((status = fcntl(s, F_GETFL, 0)) != -1) {
#ifdef O_NONBLOCK
910
        status |= O_NONBLOCK;
911 912
#else /* O_NONBLOCK */
#ifdef F_NDELAY
913
        status |= F_NDELAY;
914 915
#endif /* F_NDELAY */
#endif /* !O_NONBLOCK */
916
        status = fcntl(s, F_SETFL, status);
917 918 919
    }
    if (status < 0) {
#ifdef DEBUG_HTTP
920
        perror("nonblocking");
921
#endif
922 923
        __xmlIOErr(XML_FROM_HTTP, 0, "error setting non-blocking IO\n");
        closesocket(s);
924
        return INVALID_SOCKET;
925
    }
926
#endif /* !__BEOS__ */
927 928 929
#endif /* !VMS */
#endif /* !_WINSOCKAPI_ */

930 931 932 933 934 935 936 937 938
    if (connect(s, addr, addrlen) == -1) {
        switch (socket_errno()) {
            case EINPROGRESS:
            case EWOULDBLOCK:
                break;
            default:
                __xmlIOErr(XML_FROM_HTTP, 0,
                           "error connecting to HTTP server");
                closesocket(s);
939
                return INVALID_SOCKET;
940 941 942
        }
    }
#ifndef HAVE_POLL_H
943 944
    tv.tv_sec = timeout;
    tv.tv_usec = 0;
945 946 947 948 949

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4018)
#endif
spadix's avatar
spadix committed
950
#ifndef _WINSOCKAPI_
951
    if (s > FD_SETSIZE)
952
        return INVALID_SOCKET;
spadix's avatar
spadix committed
953
#endif
954 955
    FD_ZERO(&wfd);
    FD_SET(s, &wfd);
956

957
#ifdef _WINSOCKAPI_
958 959
    FD_ZERO(&xfd);
    FD_SET(s, &xfd);
960 961

    switch (select(s + 1, NULL, &wfd, &xfd, &tv))
962
#else
963
    switch (select(s + 1, NULL, &wfd, NULL, &tv))
964 965 966
#endif
#ifdef _MSC_VER
#pragma warning(pop)
967
#endif
968 969 970 971 972 973 974

#else /* !HAVE_POLL_H */
    p.fd = s;
    p.events = POLLOUT;
    switch (poll(&p, 1, timeout * 1000))
#endif /* !HAVE_POLL_H */

975
    {
976 977 978 979
        case 0:
            /* Time out */
            __xmlIOErr(XML_FROM_HTTP, 0, "Connect attempt timed out");
            closesocket(s);
980
            return INVALID_SOCKET;
981 982 983 984
        case -1:
            /* Ermm.. ?? */
            __xmlIOErr(XML_FROM_HTTP, 0, "Connect failed");
            closesocket(s);
985
            return INVALID_SOCKET;
986 987
    }

988 989
#ifndef HAVE_POLL_H
    if (FD_ISSET(s, &wfd)
990
#ifdef _WINSOCKAPI_
991
        || FD_ISSET(s, &xfd)
992
#endif
993 994 995 996 997 998 999 1000
        )
#else /* !HAVE_POLL_H */
    if (p.revents == POLLOUT)
#endif /* !HAVE_POLL_H */
    {
        XML_SOCKLEN_T len;

        len = sizeof(status);
1001
#ifdef SO_ERROR
1002 1003 1004 1005
        if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char *) &status, &len) <
            0) {
            /* Solaris error code */
            __xmlIOErr(XML_FROM_HTTP, 0, "getsockopt failed\n");
1006
            closesocket(s);
1007
            return INVALID_SOCKET;
1008
        }
1009
#endif
1010 1011 1012 1013 1014
        if (status) {
            __xmlIOErr(XML_FROM_HTTP, 0,
                       "Error connecting to remote host");
            closesocket(s);
            errno = status;
1015
            return INVALID_SOCKET;
1016
        }
1017
    } else {
1018 1019 1020
        /* pbm */
        __xmlIOErr(XML_FROM_HTTP, 0, "select failed\n");
        closesocket(s);
1021
        return INVALID_SOCKET;
1022
    }
1023 1024

    return (s);
1025
}
1026

1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037
/**
 * xmlNanoHTTPConnectHost:
 * @host:  the host name
 * @port:  the port number
 *
 * Attempt a connection to the given host:port endpoint. It tries
 * the multiple IP provided by the DNS if available.
 *
 * Returns -1 in case of failure, the file descriptor number otherwise
 */

1038
static SOCKET
1039 1040 1041
xmlNanoHTTPConnectHost(const char *host, int port)
{
    struct hostent *h;
1042
    struct sockaddr *addr = NULL;
1043
    struct in_addr ia;
1044
    struct sockaddr_in sockin;
1045

1046 1047
#ifdef SUPPORT_IP6
    struct in6_addr ia6;
1048
    struct sockaddr_in6 sockin6;
1049 1050
#endif
    int i;
1051
    SOCKET s;
1052

1053 1054 1055
    memset (&sockin, 0, sizeof(sockin));
#ifdef SUPPORT_IP6
    memset (&sockin6, 0, sizeof(sockin6));
1056 1057 1058
#endif

#if !defined(HAVE_GETADDRINFO) && defined(SUPPORT_IP6) && defined(RES_USE_INET6)
1059
    if (have_ipv6 ())
1060
    {
1061 1062 1063 1064
	if (!(_res.options & RES_INIT))
	    res_init();
	_res.options |= RES_USE_INET6;
    }
1065 1066 1067 1068 1069 1070
#endif

#if defined(HAVE_GETADDRINFO) && defined(SUPPORT_IP6) && !defined(_WIN32)
    if (have_ipv6 ())
#endif
#if defined(HAVE_GETADDRINFO) && (defined(SUPPORT_IP6) || defined(_WIN32))
1071
    {
1072 1073 1074 1075 1076 1077 1078 1079 1080
	int status;
	struct addrinfo hints, *res, *result;

	result = NULL;
	memset (&hints, 0,sizeof(hints));
	hints.ai_socktype = SOCK_STREAM;

	status = getaddrinfo (host, NULL, &hints, &result);
	if (status) {
1081
	    __xmlIOErr(XML_FROM_HTTP, 0, "getaddrinfo failed\n");
1082
	    return INVALID_SOCKET;
1083 1084 1085
	}

	for (res = result; res; res = res->ai_next) {
1086 1087 1088 1089
	    if (res->ai_family == AF_INET) {
		if (res->ai_addrlen > sizeof(sockin)) {
		    __xmlIOErr(XML_FROM_HTTP, 0, "address size mismatch\n");
		    freeaddrinfo (result);
1090
		    return INVALID_SOCKET;
1091
		}
1092 1093 1094 1095 1096 1097 1098
		memcpy (&sockin, res->ai_addr, res->ai_addrlen);
		sockin.sin_port = htons (port);
		addr = (struct sockaddr *)&sockin;
#ifdef SUPPORT_IP6
	    } else if (have_ipv6 () && (res->ai_family == AF_INET6)) {
		if (res->ai_addrlen > sizeof(sockin6)) {
		    __xmlIOErr(XML_FROM_HTTP, 0, "address size mismatch\n");
1099
		    freeaddrinfo (result);
1100
		    return INVALID_SOCKET;
1101
		}
1102 1103 1104 1105 1106 1107 1108 1109
		memcpy (&sockin6, res->ai_addr, res->ai_addrlen);
		sockin6.sin6_port = htons (port);
		addr = (struct sockaddr *)&sockin6;
#endif
	    } else
		continue;              /* for */

	    s = xmlNanoHTTPConnectAttempt (addr);
1110
	    if (s != INVALID_SOCKET) {
1111 1112
		freeaddrinfo (result);
		return (s);
1113 1114
	    }
	}
1115

1116 1117
	if (result)
	    freeaddrinfo (result);
1118
    }
1119
#endif
1120 1121
#if defined(HAVE_GETADDRINFO) && defined(SUPPORT_IP6) && !defined(_WIN32)
    else
1122
#endif
1123 1124
#if !defined(HAVE_GETADDRINFO) || !defined(_WIN32)
    {
1125
	h = gethostbyname (GETHOSTBYNAME_ARG_CAST host);
1126
	if (h == NULL) {
1127 1128 1129 1130 1131 1132 1133

/*
 * Okay, I got fed up by the non-portability of this error message
 * extraction code. it work on Linux, if it work on your platform
 * and one want to enable it, send me the defined(foobar) needed
 */
#if defined(HAVE_NETDB_H) && defined(HOST_NOT_FOUND) && defined(linux)
1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150
	    const char *h_err_txt = "";

	    switch (h_errno) {
		case HOST_NOT_FOUND:
		    h_err_txt = "Authoritive host not found";
		    break;

		case TRY_AGAIN:
		    h_err_txt =
			"Non-authoritive host not found or server failure.";
		    break;

		case NO_RECOVERY:
		    h_err_txt =
			"Non-recoverable errors:  FORMERR, REFUSED, or NOTIMP.";
		    break;

1151
#ifdef NO_ADDRESS
1152 1153 1154 1155
		case NO_ADDRESS:
		    h_err_txt =
			"Valid name, no data record of requested type.";
		    break;
1156
#endif
1157 1158 1159 1160 1161

		default:
		    h_err_txt = "No error text defined.";
		    break;
	    }
1162
	    __xmlIOErr(XML_FROM_HTTP, 0, h_err_txt);
1163
#else
1164
	    __xmlIOErr(XML_FROM_HTTP, 0, "Failed to resolve host");
1165
#endif
1166
	    return INVALID_SOCKET;
1167
	}
1168

1169 1170 1171
	for (i = 0; h->h_addr_list[i]; i++) {
	    if (h->h_addrtype == AF_INET) {
		/* A records (IPv4) */
1172 1173
		if ((unsigned int) h->h_length > sizeof(ia)) {
		    __xmlIOErr(XML_FROM_HTTP, 0, "address size mismatch\n");
1174
		    return INVALID_SOCKET;
1175
		}
1176 1177 1178
		memcpy (&ia, h->h_addr_list[i], h->h_length);
		sockin.sin_family = h->h_addrtype;
		sockin.sin_addr = ia;
1179
		sockin.sin_port = (unsigned short)htons ((unsigned short)port);
1180
		addr = (struct sockaddr *) &sockin;
1181
#ifdef SUPPORT_IP6
1182 1183
	    } else if (have_ipv6 () && (h->h_addrtype == AF_INET6)) {
		/* AAAA records (IPv6) */
1184 1185
		if ((unsigned int) h->h_length > sizeof(ia6)) {
		    __xmlIOErr(XML_FROM_HTTP, 0, "address size mismatch\n");
1186
		    return INVALID_SOCKET;
1187
		}
1188 1189 1190 1191 1192 1193 1194 1195
		memcpy (&ia6, h->h_addr_list[i], h->h_length);
		sockin6.sin6_family = h->h_addrtype;
		sockin6.sin6_addr = ia6;
		sockin6.sin6_port = htons (port);
		addr = (struct sockaddr *) &sockin6;
#endif
	    } else
		break;              /* for */
1196

1197
	    s = xmlNanoHTTPConnectAttempt (addr);
1198
	    if (s != INVALID_SOCKET)
1199 1200 1201
		return (s);
	}
    }
1202 1203
#endif

1204 1205
#ifdef DEBUG_HTTP
    xmlGenericError(xmlGenericErrorContext,
1206 1207
                    "xmlNanoHTTPConnectHost:  unable to connect to '%s'.\n",
                    host);
1208
#endif
1209
    return INVALID_SOCKET;
1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228
}


/**
 * xmlNanoHTTPOpen:
 * @URL:  The URL to load
 * @contentType:  if available the Content-Type information will be
 *                returned at that location
 *
 * This function try to open a connection to the indicated resource
 * via HTTP GET.
 *
 * Returns NULL in case of failure, otherwise a request handler.
 *     The contentType, if provided must be freed by the caller
 */

void*
xmlNanoHTTPOpen(const char *URL, char **contentType) {
    if (contentType != NULL) *contentType = NULL;
1229
    return(xmlNanoHTTPMethod(URL, NULL, NULL, contentType, NULL, 0));
1230 1231 1232 1233 1234 1235 1236
}

/**
 * xmlNanoHTTPOpenRedir:
 * @URL:  The URL to load
 * @contentType:  if available the Content-Type information will be
 *                returned at that location
1237
 * @redir: if available the redirected URL will be returned
1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249
 *
 * This function try to open a connection to the indicated resource
 * via HTTP GET.
 *
 * Returns NULL in case of failure, otherwise a request handler.
 *     The contentType, if provided must be freed by the caller
 */

void*
xmlNanoHTTPOpenRedir(const char *URL, char **contentType, char **redir) {
    if (contentType != NULL) *contentType = NULL;
    if (redir != NULL) *redir = NULL;
1250
    return(xmlNanoHTTPMethodRedir(URL, NULL, NULL, contentType, redir, NULL,0));
1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267
}

/**
 * xmlNanoHTTPRead:
 * @ctx:  the HTTP context
 * @dest:  a buffer
 * @len:  the buffer length
 *
 * This function tries to read @len bytes from the existing HTTP connection
 * and saves them in @dest. This is a blocking call.
 *
 * Returns the number of byte read. 0 is an indication of an end of connection.
 *         -1 indicates a parameter error.
 */
int
xmlNanoHTTPRead(void *ctx, void *dest, int len) {
    xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1268 1269 1270 1271 1272
#ifdef HAVE_ZLIB_H
    int bytes_read = 0;
    int orig_avail_in;
    int z_ret;
#endif
1273 1274 1275 1276 1277

    if (ctx == NULL) return(-1);
    if (dest == NULL) return(-1);
    if (len <= 0) return(0);

1278 1279 1280
#ifdef HAVE_ZLIB_H
    if (ctxt->usesGzip == 1) {
        if (ctxt->strm == NULL) return(0);
1281

1282 1283
        ctxt->strm->next_out = dest;
        ctxt->strm->avail_out = len;
1284
	ctxt->strm->avail_in = ctxt->inptr - ctxt->inrptr;
1285

1286 1287
        while (ctxt->strm->avail_out > 0 &&
	       (ctxt->strm->avail_in > 0 || xmlNanoHTTPRecv(ctxt) > 0)) {
1288 1289
            orig_avail_in = ctxt->strm->avail_in =
			    ctxt->inptr - ctxt->inrptr - bytes_read;
1290 1291 1292 1293 1294 1295
            ctxt->strm->next_in = BAD_CAST (ctxt->inrptr + bytes_read);

            z_ret = inflate(ctxt->strm, Z_NO_FLUSH);
            bytes_read += orig_avail_in - ctxt->strm->avail_in;

            if (z_ret != Z_OK) break;
1296
	}
1297 1298 1299 1300 1301 1302

        ctxt->inrptr += bytes_read;
        return(len - ctxt->strm->avail_out);
    }
#endif

1303
    while (ctxt->inptr - ctxt->inrptr < len) {
1304
        if (xmlNanoHTTPRecv(ctxt) <= 0) break;
1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329
    }
    if (ctxt->inptr - ctxt->inrptr < len)
        len = ctxt->inptr - ctxt->inrptr;
    memcpy(dest, ctxt->inrptr, len);
    ctxt->inrptr += len;
    return(len);
}

/**
 * xmlNanoHTTPClose:
 * @ctx:  the HTTP context
 *
 * This function closes an HTTP context, it ends up the connection and
 * free all data related to it.
 */
void
xmlNanoHTTPClose(void *ctx) {
    xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;

    if (ctx == NULL) return;

    xmlNanoHTTPFreeCtxt(ctxt);
}

/**
1330
 * xmlNanoHTTPMethodRedir:
1331 1332 1333 1334
 * @URL:  The URL to load
 * @method:  the HTTP method to use
 * @input:  the input string if any
 * @contentType:  the Content-Type information IN and OUT
1335
 * @redir:  the redirected URL OUT
1336
 * @headers:  the extra headers
1337
 * @ilen:  input length
1338 1339 1340 1341 1342 1343
 *
 * This function try to open a connection to the indicated resource
 * via HTTP using the given @method, adding the given extra headers
 * and the input buffer for the request content.
 *
 * Returns NULL in case of failure, otherwise a request handler.
1344
 *     The contentType, or redir, if provided must be freed by the caller
1345 1346 1347
 */

void*
1348
xmlNanoHTTPMethodRedir(const char *URL, const char *method, const char *input,
1349 1350
                  char **contentType, char **redir,
		  const char *headers, int ilen ) {
1351 1352
    xmlNanoHTTPCtxtPtr ctxt;
    char *bp, *p;
1353 1354
    int blen;
    SOCKET ret;
1355 1356
    int nbRedirects = 0;
    char *redirURL = NULL;
William M. Brack's avatar
William M. Brack committed
1357 1358 1359
#ifdef DEBUG_HTTP
    int xmt_bytes;
#endif
1360

1361 1362 1363 1364 1365
    if (URL == NULL) return(NULL);
    if (method == NULL) method = "GET";
    xmlNanoHTTPInit();

retry:
1366
    if (redirURL == NULL) {
1367
	ctxt = xmlNanoHTTPNewCtxt(URL);
1368 1369 1370
	if (ctxt == NULL)
	    return(NULL);
    } else {
1371
	ctxt = xmlNanoHTTPNewCtxt(redirURL);
1372 1373
	if (ctxt == NULL)
	    return(NULL);
1374
	ctxt->location = xmlMemStrdup(redirURL);
1375 1376 1377
    }

    if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
1378
	__xmlIOErr(XML_FROM_HTTP, XML_HTTP_URL_SYNTAX, "Not a valid HTTP URI");
1379 1380 1381 1382 1383
        xmlNanoHTTPFreeCtxt(ctxt);
	if (redirURL != NULL) xmlFree(redirURL);
        return(NULL);
    }
    if (ctxt->hostname == NULL) {
1384 1385
	__xmlIOErr(XML_FROM_HTTP, XML_HTTP_UNKNOWN_HOST,
	           "Failed to identify host in URI");
1386
        xmlNanoHTTPFreeCtxt(ctxt);
1387
	if (redirURL != NULL) xmlFree(redirURL);
1388 1389 1390 1391 1392 1393 1394 1395 1396 1397
        return(NULL);
    }
    if (proxy) {
	blen = strlen(ctxt->hostname) * 2 + 16;
	ret = xmlNanoHTTPConnectHost(proxy, proxyPort);
    }
    else {
	blen = strlen(ctxt->hostname);
	ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
    }
1398
    if (ret == INVALID_SOCKET) {
1399
        xmlNanoHTTPFreeCtxt(ctxt);
1400
	if (redirURL != NULL) xmlFree(redirURL);
1401 1402 1403 1404
        return(NULL);
    }
    ctxt->fd = ret;

1405
    if (input == NULL)
1406
	ilen = 0;
1407 1408 1409
    else
	blen += 36;

1410
    if (headers != NULL)
1411
	blen += strlen(headers) + 2;
1412
    if (contentType && *contentType)
1413
	/* reserve for string plus 'Content-Type: \r\n" */
1414
	blen += strlen(*contentType) + 16;
1415
    if (ctxt->query != NULL)
1416
	/* 1 for '?' */
1417
	blen += strlen(ctxt->query) + 1;
1418
    blen += strlen(method) + strlen(ctxt->path) + 24;
1419
#ifdef HAVE_ZLIB_H
1420
    /* reserve for possible 'Accept-Encoding: gzip' string */
1421 1422
    blen += 23;
#endif
1423 1424 1425 1426 1427 1428 1429
    if (ctxt->port != 80) {
	/* reserve space for ':xxxxx', incl. potential proxy */
	if (proxy)
	    blen += 12;
	else
	    blen += 6;
    }
1430
    bp = (char*)xmlMallocAtomic(blen);
1431 1432
    if ( bp == NULL ) {
        xmlNanoHTTPFreeCtxt( ctxt );
1433
	xmlHTTPErrMemory("allocating header buffer");
1434 1435 1436 1437 1438
	return ( NULL );
    }

    p = bp;

1439 1440
    if (proxy) {
	if (ctxt->port != 80) {