Coverage Report

Created: 2023-06-07 06:05

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