Coverage Report

Created: 2025-07-23 08:13

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