blob: 3502e195fd17dd535670f9c232461c114ce92c1d [file] [log] [blame]
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001/*
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@w3.org
12 */
13
14/* TODO add compression support, Send the Accept- , and decompress on the
15 fly with ZLIB if found at compile-time */
16
Daniel Veillard3c558c31999-12-22 11:30:41 +000017#ifdef WIN32
Daniel Veillard0142b842000-01-14 14:45:24 +000018#define INCLUDE_WINSOCK
Daniel Veillard3c558c31999-12-22 11:30:41 +000019#include "win32config.h"
20#else
Daniel Veillard4ecf39f1999-09-22 12:14:03 +000021#include "config.h"
22#endif
23
Daniel Veillardb71379b2000-10-09 12:30:39 +000024#include <libxml/xmlversion.h>
Daniel Veillard3c558c31999-12-22 11:30:41 +000025
Daniel Veillard361d8452000-04-03 19:48:13 +000026#ifdef LIBXML_HTTP_ENABLED
Daniel Veillard4ecf39f1999-09-22 12:14:03 +000027#include <stdio.h>
28#include <string.h>
29
30#ifdef HAVE_STDLIB_H
31#include <stdlib.h>
32#endif
33#ifdef HAVE_UNISTD_H
34#include <unistd.h>
35#endif
36#ifdef HAVE_SYS_SOCKET_H
37#include <sys/socket.h>
38#endif
39#ifdef HAVE_NETINET_IN_H
40#include <netinet/in.h>
41#endif
42#ifdef HAVE_ARPA_INET_H
43#include <arpa/inet.h>
44#endif
45#ifdef HAVE_NETDB_H
46#include <netdb.h>
47#endif
48#ifdef HAVE_FCNTL_H
49#include <fcntl.h>
50#endif
51#ifdef HAVE_ERRNO_H
52#include <errno.h>
53#endif
54#ifdef HAVE_SYS_TIME_H
55#include <sys/time.h>
56#endif
57#ifdef HAVE_SYS_SELECT_H
58#include <sys/select.h>
59#endif
Daniel Veillard5feb8492000-02-02 17:15:36 +000060#ifdef HAVE_STRINGS_H
61#include <strings.h>
62#endif
Daniel Veillard45cff692001-01-03 18:02:04 +000063#ifdef SUPPORT_IP6
64#include <resolv.h>
65#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +000066
Daniel Veillardce6e98d2000-11-25 09:54:49 +000067#ifdef VMS
68#include <stropts>
69#define SOCKLEN_T unsigned int
70#define SOCKET int
71#endif
72
Daniel Veillard361d8452000-04-03 19:48:13 +000073#include <libxml/xmlmemory.h>
Daniel Veillardb656ebe2000-09-22 13:51:48 +000074#include <libxml/parser.h> /* for xmlStr(n)casecmp() */
Daniel Veillard361d8452000-04-03 19:48:13 +000075#include <libxml/nanohttp.h>
Daniel Veillard4ecf39f1999-09-22 12:14:03 +000076
Daniel Veillard2f971a22000-10-12 23:26:32 +000077/**
78 * A couple portability macros
79 */
80#ifndef _WINSOCKAPI_
81#define closesocket(s) close(s)
82#define SOCKET int
83#endif
84
Daniel Veillard4ecf39f1999-09-22 12:14:03 +000085#ifdef STANDALONE
86#define DEBUG_HTTP
Daniel Veillardb656ebe2000-09-22 13:51:48 +000087#define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n)
Daniel Veillard8ddb5a72000-09-23 10:28:52 +000088#define xmlStrcasecmpi(a, b) strcasecmp((char *)a, (char *)b)
Daniel Veillard4ecf39f1999-09-22 12:14:03 +000089#endif
90
91#define XML_NANO_HTTP_MAX_REDIR 10
92
93#define XML_NANO_HTTP_CHUNK 4096
94
95#define XML_NANO_HTTP_CLOSED 0
96#define XML_NANO_HTTP_WRITE 1
97#define XML_NANO_HTTP_READ 2
98#define XML_NANO_HTTP_NONE 4
99
100typedef struct xmlNanoHTTPCtxt {
101 char *protocol; /* the protocol name */
102 char *hostname; /* the host name */
103 int port; /* the port */
104 char *path; /* the path within the URL */
Daniel Veillard2f971a22000-10-12 23:26:32 +0000105 SOCKET fd; /* the file descriptor for the socket */
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000106 int state; /* WRITE / READ / CLOSED */
107 char *out; /* buffer sent (zero terminated) */
108 char *outptr; /* index within the buffer sent */
109 char *in; /* the receiving buffer */
110 char *content; /* the start of the content */
111 char *inptr; /* the next byte to read from network */
112 char *inrptr; /* the next byte to give back to the client */
113 int inlen; /* len of the input buffer */
114 int last; /* return code for last operation */
115 int returnValue; /* the protocol return value */
116 char *contentType; /* the MIME type for the input */
117 char *location; /* the new URL in case of redirect */
Daniel Veillardc2def842000-11-07 14:21:01 +0000118 char *authHeader; /* contents of {WWW,Proxy}-Authenticate header */
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000119} xmlNanoHTTPCtxt, *xmlNanoHTTPCtxtPtr;
120
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000121static int initialized = 0;
Daniel Veillard19d61112000-10-11 23:50:35 +0000122static char *proxy = NULL; /* the proxy name if any */
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000123static int proxyPort; /* the proxy port if any */
Daniel Veillard19d61112000-10-11 23:50:35 +0000124static unsigned int timeout = 60;/* the select() timeout in seconds */
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000125
126/**
Daniel Veillard2f971a22000-10-12 23:26:32 +0000127 * A portability function
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000128 */
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000129int socket_errno(void) {
130#ifdef _WINSOCKAPI_
131 return(WSAGetLastError());
132#else
133 return(errno);
134#endif
135}
136
137/**
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000138 * xmlNanoHTTPInit:
139 *
140 * Initialize the HTTP protocol layer.
141 * Currently it just checks for proxy informations
142 */
143
144void
145xmlNanoHTTPInit(void) {
146 const char *env;
Daniel Veillard2f971a22000-10-12 23:26:32 +0000147#ifdef _WINSOCKAPI_
148 WSADATA wsaData;
149#endif
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000150
151 if (initialized)
152 return;
153
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000154#ifdef _WINSOCKAPI_
Daniel Veillard2f971a22000-10-12 23:26:32 +0000155 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
156 return;
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000157#endif
158
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000159 if (proxy == NULL) {
160 proxyPort = 80;
161 env = getenv("no_proxy");
162 if (env != NULL)
163 goto done;
164 env = getenv("http_proxy");
165 if (env != NULL) {
166 xmlNanoHTTPScanProxy(env);
167 goto done;
168 }
169 env = getenv("HTTP_PROXY");
170 if (env != NULL) {
171 xmlNanoHTTPScanProxy(env);
172 goto done;
173 }
174 }
175done:
176 initialized = 1;
177}
178
179/**
180 * xmlNanoHTTPClenup:
181 *
182 * Cleanup the HTTP protocol layer.
183 */
184
185void
186xmlNanoHTTPCleanup(void) {
187 if (proxy != NULL)
188 xmlFree(proxy);
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000189#ifdef _WINSOCKAPI_
Daniel Veillard2f971a22000-10-12 23:26:32 +0000190 if (initialized)
191 WSACleanup();
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000192#endif
Daniel Veillard2f971a22000-10-12 23:26:32 +0000193 initialized = 0;
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000194 return;
195}
196
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000197/**
Daniel Veillard19d61112000-10-11 23:50:35 +0000198 * xmlNanoHTTPTimeout:
199 * @delay: the delay in seconds
200 *
201 * Set the HTTP timeout, (default is 60secs). 0 means immediate
202 * return, while -1 infinite.
203 */
204
205void
206xmlNanoHTTPTimeout(int delay) {
207 timeout = (unsigned int) delay;
208}
209
210/**
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000211 * xmlNanoHTTPScanURL:
212 * @ctxt: an HTTP context
213 * @URL: The URL used to initialize the context
214 *
215 * (Re)Initialize an HTTP context by parsing the URL and finding
216 * the protocol host port and path it indicates.
217 */
218
219static void
220xmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt, const char *URL) {
221 const char *cur = URL;
222 char buf[4096];
223 int index = 0;
224 int port = 0;
225
226 if (ctxt->protocol != NULL) {
227 xmlFree(ctxt->protocol);
228 ctxt->protocol = NULL;
229 }
230 if (ctxt->hostname != NULL) {
231 xmlFree(ctxt->hostname);
232 ctxt->hostname = NULL;
233 }
234 if (ctxt->path != NULL) {
235 xmlFree(ctxt->path);
236 ctxt->path = NULL;
237 }
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000238 if (URL == NULL) return;
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000239 buf[index] = 0;
240 while (*cur != 0) {
241 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
242 buf[index] = 0;
243 ctxt->protocol = xmlMemStrdup(buf);
244 index = 0;
245 cur += 3;
246 break;
247 }
248 buf[index++] = *cur++;
249 }
250 if (*cur == 0) return;
251
252 buf[index] = 0;
253 while (1) {
254 if (cur[0] == ':') {
255 buf[index] = 0;
256 ctxt->hostname = xmlMemStrdup(buf);
257 index = 0;
258 cur += 1;
259 while ((*cur >= '0') && (*cur <= '9')) {
260 port *= 10;
261 port += *cur - '0';
262 cur++;
263 }
264 if (port != 0) ctxt->port = port;
265 while ((cur[0] != '/') && (*cur != 0))
266 cur++;
267 break;
268 }
269 if ((*cur == '/') || (*cur == 0)) {
270 buf[index] = 0;
271 ctxt->hostname = xmlMemStrdup(buf);
272 index = 0;
273 break;
274 }
275 buf[index++] = *cur++;
276 }
277 if (*cur == 0)
278 ctxt->path = xmlMemStrdup("/");
279 else {
Daniel Veillard726e8792000-01-30 20:04:29 +0000280 index = 0;
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000281 buf[index] = 0;
Daniel Veillard726e8792000-01-30 20:04:29 +0000282 while (*cur != 0)
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000283 buf[index++] = *cur++;
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000284 buf[index] = 0;
285 ctxt->path = xmlMemStrdup(buf);
286 }
287}
288
289/**
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000290 * xmlNanoHTTPScanProxy:
291 * @URL: The proxy URL used to initialize the proxy context
292 *
293 * (Re)Initialize the HTTP Proxy context by parsing the URL and finding
294 * the protocol host port it indicates.
295 * Should be like http://myproxy/ or http://myproxy:3128/
296 * A NULL URL cleans up proxy informations.
297 */
298
299void
300xmlNanoHTTPScanProxy(const char *URL) {
301 const char *cur = URL;
302 char buf[4096];
303 int index = 0;
304 int port = 0;
305
306 if (proxy != NULL) {
307 xmlFree(proxy);
308 proxy = NULL;
309 }
310 if (proxyPort != 0) {
311 proxyPort = 0;
312 }
313#ifdef DEBUG_HTTP
314 if (URL == NULL)
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000315 xmlGenericError(xmlGenericErrorContext,
316 "Removing HTTP proxy info\n");
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000317 else
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000318 xmlGenericError(xmlGenericErrorContext,
319 "Using HTTP proxy %s\n", URL);
Daniel Veillarde41f2b72000-01-30 20:00:07 +0000320#endif
321 if (URL == NULL) return;
322 buf[index] = 0;
323 while (*cur != 0) {
324 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
325 buf[index] = 0;
326 index = 0;
327 cur += 3;
328 break;
329 }
330 buf[index++] = *cur++;
331 }
332 if (*cur == 0) return;
333
334 buf[index] = 0;
335 while (1) {
336 if (cur[0] == ':') {
337 buf[index] = 0;
338 proxy = xmlMemStrdup(buf);
339 index = 0;
340 cur += 1;
341 while ((*cur >= '0') && (*cur <= '9')) {
342 port *= 10;
343 port += *cur - '0';
344 cur++;
345 }
346 if (port != 0) proxyPort = port;
347 while ((cur[0] != '/') && (*cur != 0))
348 cur++;
349 break;
350 }
351 if ((*cur == '/') || (*cur == 0)) {
352 buf[index] = 0;
353 proxy = xmlMemStrdup(buf);
354 index = 0;
355 break;
356 }
357 buf[index++] = *cur++;
358 }
359}
360
361/**
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000362 * xmlNanoHTTPNewCtxt:
363 * @URL: The URL used to initialize the context
364 *
365 * Allocate and initialize a new HTTP context.
366 *
367 * Returns an HTTP context or NULL in case of error.
368 */
369
370static xmlNanoHTTPCtxtPtr
371xmlNanoHTTPNewCtxt(const char *URL) {
372 xmlNanoHTTPCtxtPtr ret;
373
374 ret = (xmlNanoHTTPCtxtPtr) xmlMalloc(sizeof(xmlNanoHTTPCtxt));
375 if (ret == NULL) return(NULL);
376
377 memset(ret, 0, sizeof(xmlNanoHTTPCtxt));
378 ret->port = 80;
379 ret->returnValue = 0;
380
381 xmlNanoHTTPScanURL(ret, URL);
382
383 return(ret);
384}
385
386/**
387 * xmlNanoHTTPFreeCtxt:
388 * @ctxt: an HTTP context
389 *
390 * Frees the context after closing the connection.
391 */
392
393static void
394xmlNanoHTTPFreeCtxt(xmlNanoHTTPCtxtPtr ctxt) {
395 if (ctxt == NULL) return;
396 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
397 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
398 if (ctxt->path != NULL) xmlFree(ctxt->path);
399 if (ctxt->out != NULL) xmlFree(ctxt->out);
400 if (ctxt->in != NULL) xmlFree(ctxt->in);
401 if (ctxt->contentType != NULL) xmlFree(ctxt->contentType);
402 if (ctxt->location != NULL) xmlFree(ctxt->location);
Daniel Veillardc2def842000-11-07 14:21:01 +0000403 if (ctxt->authHeader != NULL) xmlFree(ctxt->authHeader);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000404 ctxt->state = XML_NANO_HTTP_NONE;
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000405 if (ctxt->fd >= 0) closesocket(ctxt->fd);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000406 ctxt->fd = -1;
407 xmlFree(ctxt);
408}
409
410/**
411 * xmlNanoHTTPSend:
412 * @ctxt: an HTTP context
413 *
414 * Send the input needed to initiate the processing on the server side
415 */
416
417static void
418xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt) {
Daniel Veillard683cb022000-10-22 12:04:13 +0000419 if (ctxt->state & XML_NANO_HTTP_WRITE) {
420 int total_sent = 0;
421 while (total_sent <strlen(ctxt->outptr)) {
422 int nsent = send(ctxt->fd, ctxt->outptr+total_sent,
423 strlen(ctxt->outptr)-total_sent, 0);
424 if (nsent>0)
425 total_sent += nsent;
426}
427
428 ctxt->last = total_sent;
429 }
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000430}
431
432/**
433 * xmlNanoHTTPRecv:
434 * @ctxt: an HTTP context
435 *
436 * Read information coming from the HTTP connection.
437 * This is a blocking call (but it blocks in select(), not read()).
438 *
439 * Returns the number of byte read or -1 in case of error.
440 */
441
442static int
443xmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt) {
444 fd_set rfd;
445 struct timeval tv;
446
447
448 while (ctxt->state & XML_NANO_HTTP_READ) {
449 if (ctxt->in == NULL) {
450 ctxt->in = (char *) xmlMalloc(65000 * sizeof(char));
451 if (ctxt->in == NULL) {
452 ctxt->last = -1;
453 return(-1);
454 }
455 ctxt->inlen = 65000;
456 ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in;
457 }
458 if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) {
459 int delta = ctxt->inrptr - ctxt->in;
460 int len = ctxt->inptr - ctxt->inrptr;
461
462 memmove(ctxt->in, ctxt->inrptr, len);
463 ctxt->inrptr -= delta;
464 ctxt->content -= delta;
465 ctxt->inptr -= delta;
466 }
467 if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) {
468 int d_inptr = ctxt->inptr - ctxt->in;
469 int d_content = ctxt->content - ctxt->in;
470 int d_inrptr = ctxt->inrptr - ctxt->in;
471
472 ctxt->inlen *= 2;
473 ctxt->in = (char *) xmlRealloc(ctxt->in, ctxt->inlen);
474 if (ctxt->in == NULL) {
475 ctxt->last = -1;
476 return(-1);
477 }
478 ctxt->inptr = ctxt->in + d_inptr;
479 ctxt->content = ctxt->in + d_content;
480 ctxt->inrptr = ctxt->in + d_inrptr;
481 }
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000482 ctxt->last = recv(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK, 0);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000483 if (ctxt->last > 0) {
484 ctxt->inptr += ctxt->last;
485 return(ctxt->last);
486 }
487 if (ctxt->last == 0) {
488 return(0);
489 }
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000490 if (ctxt->last == -1) {
491 switch (socket_errno()) {
492 case EINPROGRESS:
493 case EWOULDBLOCK:
494#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
495 case EAGAIN:
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000496#endif
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000497 break;
498 default:
499 return(0);
500 }
501 }
502
Daniel Veillard19d61112000-10-11 23:50:35 +0000503 tv.tv_sec = timeout;
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000504 tv.tv_usec = 0;
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000505 FD_ZERO(&rfd);
506 FD_SET(ctxt->fd, &rfd);
507
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000508 if (select(ctxt->fd+1, &rfd, NULL, NULL, &tv)<1)
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000509 return(0);
510 }
511 return(0);
512}
513
514/**
515 * xmlNanoHTTPReadLine:
516 * @ctxt: an HTTP context
517 *
518 * Read one line in the HTTP server output, usually for extracting
519 * the HTTP protocol informations from the answer header.
520 *
521 * Returns a newly allocated string with a copy of the line, or NULL
522 * which indicate the end of the input.
523 */
524
525static char *
526xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt) {
527 char buf[4096];
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000528 char *bp = buf;
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000529
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000530 while (bp - buf < 4095) {
531 if (ctxt->inrptr == ctxt->inptr) {
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000532 if (xmlNanoHTTPRecv(ctxt) == 0) {
533 if (bp == buf)
534 return(NULL);
535 else
536 *bp = 0;
537 return(xmlMemStrdup(buf));
538 }
539 }
540 *bp = *ctxt->inrptr++;
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000541 if (*bp == '\n') {
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000542 *bp = 0;
543 return(xmlMemStrdup(buf));
544 }
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000545 if (*bp != '\r')
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000546 bp++;
547 }
548 buf[4095] = 0;
549 return(xmlMemStrdup(buf));
550}
551
552
553/**
554 * xmlNanoHTTPScanAnswer:
555 * @ctxt: an HTTP context
556 * @line: an HTTP header line
557 *
558 * Try to extract useful informations from the server answer.
559 * We currently parse and process:
560 * - The HTTP revision/ return code
561 * - The Content-Type
562 * - The Location for redirrect processing.
563 *
564 * Returns -1 in case of failure, the file descriptor number otherwise
565 */
566
567static void
568xmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt, const char *line) {
569 const char *cur = line;
570
571 if (line == NULL) return;
572
573 if (!strncmp(line, "HTTP/", 5)) {
574 int version = 0;
575 int ret = 0;
576
577 cur += 5;
578 while ((*cur >= '0') && (*cur <= '9')) {
579 version *= 10;
580 version += *cur - '0';
581 cur++;
582 }
583 if (*cur == '.') {
584 cur++;
585 if ((*cur >= '0') && (*cur <= '9')) {
586 version *= 10;
587 version += *cur - '0';
588 cur++;
589 }
590 while ((*cur >= '0') && (*cur <= '9'))
591 cur++;
592 } else
593 version *= 10;
594 if ((*cur != ' ') && (*cur != '\t')) return;
595 while ((*cur == ' ') || (*cur == '\t')) cur++;
596 if ((*cur < '0') || (*cur > '9')) return;
597 while ((*cur >= '0') && (*cur <= '9')) {
598 ret *= 10;
599 ret += *cur - '0';
600 cur++;
601 }
602 if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return;
603 ctxt->returnValue = ret;
Daniel Veillardb656ebe2000-09-22 13:51:48 +0000604 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Content-Type:", 13)) {
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000605 cur += 13;
606 while ((*cur == ' ') || (*cur == '\t')) cur++;
607 if (ctxt->contentType != NULL)
608 xmlFree(ctxt->contentType);
609 ctxt->contentType = xmlMemStrdup(cur);
Daniel Veillardb656ebe2000-09-22 13:51:48 +0000610 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"ContentType:", 12)) {
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000611 cur += 12;
612 if (ctxt->contentType != NULL) return;
613 while ((*cur == ' ') || (*cur == '\t')) cur++;
614 ctxt->contentType = xmlMemStrdup(cur);
Daniel Veillardb656ebe2000-09-22 13:51:48 +0000615 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Location:", 9)) {
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000616 cur += 9;
617 while ((*cur == ' ') || (*cur == '\t')) cur++;
618 if (ctxt->location != NULL)
619 xmlFree(ctxt->location);
620 ctxt->location = xmlMemStrdup(cur);
Daniel Veillardc2def842000-11-07 14:21:01 +0000621 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"WWW-Authenticate:", 17)) {
622 cur += 17;
623 while ((*cur == ' ') || (*cur == '\t')) cur++;
624 if (ctxt->authHeader != NULL)
625 xmlFree(ctxt->authHeader);
626 ctxt->authHeader = xmlMemStrdup(cur);
627 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Proxy-Authenticate:", 19)) {
628 cur += 19;
629 while ((*cur == ' ') || (*cur == '\t')) cur++;
630 if (ctxt->authHeader != NULL)
631 xmlFree(ctxt->authHeader);
632 ctxt->authHeader = xmlMemStrdup(cur);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000633 }
634}
635
636/**
637 * xmlNanoHTTPConnectAttempt:
638 * @ia: an internet adress structure
639 * @port: the port number
640 *
641 * Attempt a connection to the given IP:port endpoint. It forces
642 * non-blocking semantic on the socket, and allow 60 seconds for
643 * the host to answer.
644 *
645 * Returns -1 in case of failure, the file descriptor number otherwise
646 */
647
648static int
Daniel Veillard45cff692001-01-03 18:02:04 +0000649xmlNanoHTTPConnectAttempt(struct sockaddr *addr, int port)
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000650{
Daniel Veillard2f971a22000-10-12 23:26:32 +0000651 SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000652 fd_set wfd;
653 struct timeval tv;
654 int status;
655
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000656 if (s==-1) {
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000657#ifdef DEBUG_HTTP
658 perror("socket");
659#endif
660 return(-1);
661 }
662
663#ifdef _WINSOCKAPI_
664 {
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000665 u_long one = 1;
666
667 status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0;
668 }
669#else /* _WINSOCKAPI_ */
670#if defined(VMS)
671 {
672 int enable = 1;
Daniel Veillardce6e98d2000-11-25 09:54:49 +0000673 status = ioctl(s, FIONBIO, &enable);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000674 }
675#else /* VMS */
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000676 if ((status = fcntl(s, F_GETFL, 0)) != -1) {
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000677#ifdef O_NONBLOCK
678 status |= O_NONBLOCK;
679#else /* O_NONBLOCK */
680#ifdef F_NDELAY
681 status |= F_NDELAY;
682#endif /* F_NDELAY */
683#endif /* !O_NONBLOCK */
684 status = fcntl(s, F_SETFL, status);
685 }
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000686 if (status < 0) {
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000687#ifdef DEBUG_HTTP
688 perror("nonblocking");
689#endif
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000690 closesocket(s);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000691 return(-1);
692 }
693#endif /* !VMS */
694#endif /* !_WINSOCKAPI_ */
695
696
Daniel Veillard45cff692001-01-03 18:02:04 +0000697 if ((connect(s, addr, sizeof(*addr))==-1)) {
Daniel Veillard2f971a22000-10-12 23:26:32 +0000698 switch (socket_errno()) {
699 case EINPROGRESS:
700 case EWOULDBLOCK:
701 break;
702 default:
703 perror("connect");
704 closesocket(s);
705 return(-1);
706 }
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000707 }
708
Daniel Veillard19d61112000-10-11 23:50:35 +0000709 tv.tv_sec = timeout;
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000710 tv.tv_usec = 0;
711
712 FD_ZERO(&wfd);
713 FD_SET(s, &wfd);
714
715 switch(select(s+1, NULL, &wfd, NULL, &tv))
716 {
717 case 0:
718 /* Time out */
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000719 closesocket(s);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000720 return(-1);
721 case -1:
722 /* Ermm.. ?? */
723#ifdef DEBUG_HTTP
724 perror("select");
725#endif
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000726 closesocket(s);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000727 return(-1);
728 }
Daniel Veillardbe803962000-06-28 23:40:59 +0000729
730 if ( FD_ISSET(s, &wfd) ) {
Daniel Veillardb0426ca2000-10-11 23:39:43 +0000731 SOCKLEN_T len;
Daniel Veillardbe803962000-06-28 23:40:59 +0000732 len = sizeof(status);
Daniel Veillard2f971a22000-10-12 23:26:32 +0000733 if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&status, &len) < 0 ) {
Daniel Veillardbe803962000-06-28 23:40:59 +0000734 /* Solaris error code */
735 return (-1);
736 }
737 if ( status ) {
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000738 closesocket(s);
Daniel Veillardbe803962000-06-28 23:40:59 +0000739 errno = status;
740 return (-1);
741 }
742 } else {
743 /* pbm */
744 return (-1);
745 }
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000746
747 return(s);
748}
749
750/**
751 * xmlNanoHTTPConnectHost:
752 * @host: the host name
753 * @port: the port number
754 *
755 * Attempt a connection to the given host:port endpoint. It tries
756 * the multiple IP provided by the DNS if available.
757 *
758 * Returns -1 in case of failure, the file descriptor number otherwise
759 */
760
761static int
762xmlNanoHTTPConnectHost(const char *host, int port)
763{
764 struct hostent *h;
Daniel Veillard45cff692001-01-03 18:02:04 +0000765 struct sockaddr *addr;
766 struct in_addr ia;
767 struct sockaddr_in sin;
768#ifdef SUPPORT_IP6
769 struct in6_addr ia6;
770 struct sockaddr_in6 sin6;
771#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000772 int i;
773 int s;
774
Daniel Veillard45cff692001-01-03 18:02:04 +0000775#if defined(SUPPORT_IP6) && defined(RES_USE_INET6)
776 if (!(_res.options & RES_INIT))
777 res_init();
778 _res.options |= RES_USE_INET6;
779#endif
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000780 h=gethostbyname(host);
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000781 if (h==NULL)
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000782 {
783#ifdef DEBUG_HTTP
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000784 xmlGenericError(xmlGenericErrorContext,"unable to resolve '%s'.\n", host);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000785#endif
786 return(-1);
787 }
788
789 for(i=0; h->h_addr_list[i]; i++)
790 {
Daniel Veillard45cff692001-01-03 18:02:04 +0000791 if (h->h_addrtype == AF_INET) {
792 /* A records (IPv4) */
793 memcpy(&ia, h->h_addr_list[i], h->h_length);
794 sin.sin_family = h->h_addrtype;
795 sin.sin_addr = ia;
796 sin.sin_port = htons(port);
797 addr = (struct sockaddr *)&sin;
798#ifdef SUPPORT_IP6
799 } else if (h->h_addrtype == AF_INET6) {
800 /* AAAA records (IPv6) */
801 memcpy(&ia6, h->h_addr_list[i], h->h_length);
802 sin6.sin_family = h->h_addrtype;
803 sin6.sin_addr = ia6;
804 sin6.sin_port = htons(port);
805 addr = (struct sockaddr *)&sin6;
806#endif
807 } else
808 break; /* for */
809
810 s = xmlNanoHTTPConnectAttempt(addr, port);
Daniel Veillarde8282ed2000-10-10 23:01:31 +0000811 if (s != -1)
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000812 return(s);
813 }
814
815#ifdef DEBUG_HTTP
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000816 xmlGenericError(xmlGenericErrorContext,
817 "unable to connect to '%s'.\n", host);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000818#endif
819 return(-1);
820}
821
822
823/**
824 * xmlNanoHTTPOpen:
825 * @URL: The URL to load
826 * @contentType: if available the Content-Type information will be
827 * returned at that location
828 *
829 * This function try to open a connection to the indicated resource
830 * via HTTP GET.
831 *
832 * Returns NULL in case of failure, otherwise a request handler.
833 * The contentType, if provided must be freed by the caller
834 */
835
Daniel Veillard06047432000-04-24 11:33:38 +0000836void*
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000837xmlNanoHTTPOpen(const char *URL, char **contentType) {
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000838 if (contentType != NULL) *contentType = NULL;
Daniel Veillardbe9ec4b2000-10-25 11:01:53 +0000839 return xmlNanoHTTPMethod(URL, NULL, NULL, contentType, NULL);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000840}
841
842/**
843 * xmlNanoHTTPRead:
844 * @ctx: the HTTP context
845 * @dest: a buffer
846 * @len: the buffer length
847 *
848 * This function tries to read @len bytes from the existing HTTP connection
849 * and saves them in @dest. This is a blocking call.
850 *
851 * Returns the number of byte read. 0 is an indication of an end of connection.
852 * -1 indicates a parameter error.
853 */
854int
855xmlNanoHTTPRead(void *ctx, void *dest, int len) {
856 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
857
858 if (ctx == NULL) return(-1);
859 if (dest == NULL) return(-1);
860 if (len <= 0) return(0);
861
862 while (ctxt->inptr - ctxt->inrptr < len) {
863 if (xmlNanoHTTPRecv(ctxt) == 0) break;
864 }
865 if (ctxt->inptr - ctxt->inrptr < len)
866 len = ctxt->inptr - ctxt->inrptr;
867 memcpy(dest, ctxt->inrptr, len);
868 ctxt->inrptr += len;
869 return(len);
870}
871
872/**
873 * xmlNanoHTTPClose:
874 * @ctx: the HTTP context
875 *
876 * This function closes an HTTP context, it ends up the connection and
877 * free all data related to it.
878 */
879void
880xmlNanoHTTPClose(void *ctx) {
881 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
882
883 if (ctx == NULL) return;
884
885 xmlNanoHTTPFreeCtxt(ctxt);
886}
887
888/**
889 * xmlNanoHTTPMethod:
890 * @URL: The URL to load
891 * @method: the HTTP method to use
892 * @input: the input string if any
893 * @contentType: the Content-Type information IN and OUT
894 * @headers: the extra headers
895 *
896 * This function try to open a connection to the indicated resource
897 * via HTTP using the given @method, adding the given extra headers
898 * and the input buffer for the request content.
899 *
900 * Returns NULL in case of failure, otherwise a request handler.
901 * The contentType, if provided must be freed by the caller
902 */
903
Daniel Veillard06047432000-04-24 11:33:38 +0000904void*
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000905xmlNanoHTTPMethod(const char *URL, const char *method, const char *input,
906 char **contentType, const char *headers) {
907 xmlNanoHTTPCtxtPtr ctxt;
Daniel Veillardbe9ec4b2000-10-25 11:01:53 +0000908 char *bp, *p;
909 int blen, ilen, ret;
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000910 int head;
911 int nbRedirects = 0;
912 char *redirURL = NULL;
913
914 if (URL == NULL) return(NULL);
915 if (method == NULL) method = "GET";
Daniel Veillardbe9ec4b2000-10-25 11:01:53 +0000916 xmlNanoHTTPInit();
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000917
918retry:
919 if (redirURL == NULL)
920 ctxt = xmlNanoHTTPNewCtxt(URL);
921 else {
922 ctxt = xmlNanoHTTPNewCtxt(redirURL);
923 xmlFree(redirURL);
924 redirURL = NULL;
925 }
926
927 if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
928 xmlNanoHTTPFreeCtxt(ctxt);
929 if (redirURL != NULL) xmlFree(redirURL);
930 return(NULL);
931 }
932 if (ctxt->hostname == NULL) {
933 xmlNanoHTTPFreeCtxt(ctxt);
934 return(NULL);
935 }
Daniel Veillardbe9ec4b2000-10-25 11:01:53 +0000936 if (proxy) {
937 blen = strlen(ctxt->hostname) * 2 + 16;
938 ret = xmlNanoHTTPConnectHost(proxy, proxyPort);
939 }
940 else {
941 blen = strlen(ctxt->hostname);
942 ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
943 }
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000944 if (ret < 0) {
945 xmlNanoHTTPFreeCtxt(ctxt);
946 return(NULL);
947 }
948 ctxt->fd = ret;
949
Daniel Veillardbe9ec4b2000-10-25 11:01:53 +0000950 if (input != NULL) {
951 ilen = strlen(input);
952 blen += ilen + 32;
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000953 }
Daniel Veillardbe9ec4b2000-10-25 11:01:53 +0000954 else
955 ilen = 0;
956 if (headers != NULL)
957 blen += strlen(headers);
958 if (contentType && *contentType)
959 blen += strlen(*contentType) + 16;
960 blen += strlen(method) + strlen(ctxt->path) + 23;
961 bp = xmlMalloc(blen);
962 if (proxy) {
963 if (ctxt->port != 80) {
964 sprintf(bp, "%s http://%s:%d%s", method, ctxt->hostname,
965 ctxt->port, ctxt->path);
966 }
967 else
968 sprintf(bp, "%s http://%s%s", method, ctxt->hostname, ctxt->path);
969 }
970 else
971 sprintf(bp, "%s %s", method, ctxt->path);
972 p = bp + strlen(bp);
973 sprintf(p, " HTTP/1.0\r\nHost: %s\r\n", ctxt->hostname);
974 p += strlen(p);
975 if (contentType != NULL && *contentType) {
976 sprintf(p, "Content-Type: %s\r\n", *contentType);
977 p += strlen(p);
978 }
979 if (headers != NULL) {
980 strcpy(p, headers);
981 p += strlen(p);
982 }
983 if (input != NULL)
984 sprintf(p, "Content-Length: %d\r\n\r\n%s", ilen, input);
985 else
986 strcpy(p, "\r\n");
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000987#ifdef DEBUG_HTTP
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000988 xmlGenericError(xmlGenericErrorContext,
989 "-> %s%s", proxy? "(Proxy) " : "", bp);
Daniel Veillardbe9ec4b2000-10-25 11:01:53 +0000990 if ((blen -= strlen(bp)+1) < 0)
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000991 xmlGenericError(xmlGenericErrorContext,
992 "ERROR: overflowed buffer by %d bytes\n", -blen);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000993#endif
Daniel Veillardbe9ec4b2000-10-25 11:01:53 +0000994 ctxt->outptr = ctxt->out = bp;
Daniel Veillard4ecf39f1999-09-22 12:14:03 +0000995 ctxt->state = XML_NANO_HTTP_WRITE;
996 xmlNanoHTTPSend(ctxt);
997 ctxt->state = XML_NANO_HTTP_READ;
998 head = 1;
999
1000 while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
1001 if (head && (*p == 0)) {
1002 head = 0;
1003 ctxt->content = ctxt->inrptr;
Daniel Veillardbe9ec4b2000-10-25 11:01:53 +00001004 xmlFree(p);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001005 break;
1006 }
1007 xmlNanoHTTPScanAnswer(ctxt, p);
1008
1009#ifdef DEBUG_HTTP
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001010 xmlGenericError(xmlGenericErrorContext, "<- %s\n", p);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001011#endif
Daniel Veillardbe9ec4b2000-10-25 11:01:53 +00001012 xmlFree(p);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001013 }
1014
1015 if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
1016 (ctxt->returnValue < 400)) {
1017#ifdef DEBUG_HTTP
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001018 xmlGenericError(xmlGenericErrorContext,
1019 "\nRedirect to: %s\n", ctxt->location);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001020#endif
1021 while (xmlNanoHTTPRecv(ctxt)) ;
1022 if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
1023 nbRedirects++;
1024 redirURL = xmlMemStrdup(ctxt->location);
1025 xmlNanoHTTPFreeCtxt(ctxt);
1026 goto retry;
1027 }
1028 xmlNanoHTTPFreeCtxt(ctxt);
1029#ifdef DEBUG_HTTP
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001030 xmlGenericError(xmlGenericErrorContext,
1031 "Too many redirects, aborting ...\n");
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001032#endif
1033 return(NULL);
1034
1035 }
1036
Daniel Veillardbe9ec4b2000-10-25 11:01:53 +00001037 if (contentType != NULL) {
1038 if (ctxt->contentType != NULL)
1039 *contentType = xmlMemStrdup(ctxt->contentType);
1040 else
1041 *contentType = NULL;
1042 }
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001043
1044#ifdef DEBUG_HTTP
1045 if (ctxt->contentType != NULL)
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001046 xmlGenericError(xmlGenericErrorContext,
1047 "\nCode %d, content-type '%s'\n\n",
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001048 ctxt->returnValue, ctxt->contentType);
1049 else
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001050 xmlGenericError(xmlGenericErrorContext,
1051 "\nCode %d, no content-type\n\n",
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001052 ctxt->returnValue);
1053#endif
1054
1055 return((void *) ctxt);
1056}
1057
1058/**
1059 * xmlNanoHTTPFetch:
1060 * @URL: The URL to load
1061 * @filename: the filename where the content should be saved
1062 * @contentType: if available the Content-Type information will be
1063 * returned at that location
1064 *
1065 * This function try to fetch the indicated resource via HTTP GET
1066 * and save it's content in the file.
1067 *
1068 * Returns -1 in case of failure, 0 incase of success. The contentType,
1069 * if provided must be freed by the caller
1070 */
1071int
1072xmlNanoHTTPFetch(const char *URL, const char *filename, char **contentType) {
1073 void *ctxt;
1074 char buf[4096];
1075 int fd;
1076 int len;
1077
1078 ctxt = xmlNanoHTTPOpen(URL, contentType);
1079 if (ctxt == NULL) return(-1);
1080
1081 if (!strcmp(filename, "-"))
1082 fd = 0;
1083 else {
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001084 fd = open(filename, O_CREAT | O_WRONLY, 00644);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001085 if (fd < 0) {
1086 xmlNanoHTTPClose(ctxt);
1087 if ((contentType != NULL) && (*contentType != NULL)) {
1088 xmlFree(*contentType);
1089 *contentType = NULL;
1090 }
1091 return(-1);
1092 }
1093 }
1094
1095 while ((len = xmlNanoHTTPRead(ctxt, buf, sizeof(buf))) > 0) {
1096 write(fd, buf, len);
1097 }
1098
1099 xmlNanoHTTPClose(ctxt);
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001100 close(fd);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001101 return(0);
1102}
1103
1104/**
1105 * xmlNanoHTTPSave:
Daniel Veillard00fdf371999-10-08 09:40:39 +00001106 * @ctxt: the HTTP context
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001107 * @filename: the filename where the content should be saved
1108 *
1109 * This function saves the output of the HTTP transaction to a file
1110 * It closes and free the context at the end
1111 *
1112 * Returns -1 in case of failure, 0 incase of success.
1113 */
1114int
1115xmlNanoHTTPSave(void *ctxt, const char *filename) {
1116 char buf[4096];
1117 int fd;
1118 int len;
1119
1120 if (ctxt == NULL) return(-1);
1121
1122 if (!strcmp(filename, "-"))
1123 fd = 0;
1124 else {
1125 fd = open(filename, O_CREAT | O_WRONLY);
1126 if (fd < 0) {
1127 xmlNanoHTTPClose(ctxt);
1128 return(-1);
1129 }
1130 }
1131
1132 while ((len = xmlNanoHTTPRead(ctxt, buf, sizeof(buf))) > 0) {
1133 write(fd, buf, len);
1134 }
1135
1136 xmlNanoHTTPClose(ctxt);
1137 return(0);
1138}
1139
1140/**
1141 * xmlNanoHTTPReturnCode:
1142 * @ctx: the HTTP context
1143 *
1144 * Returns the HTTP return code for the request.
1145 */
1146int
1147xmlNanoHTTPReturnCode(void *ctx) {
1148 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1149
1150 if (ctxt == NULL) return(-1);
1151
1152 return(ctxt->returnValue);
1153}
1154
Daniel Veillardc2def842000-11-07 14:21:01 +00001155/**
1156 * xmlNanoHTTPAuthHeader:
1157 * @ctx: the HTTP context
1158 *
1159 * Returns the stashed value of the WWW-Authenticate or Proxy-Authenticate
1160 * header.
1161 */
1162const char *
1163xmlNanoHTTPAuthHeader(void *ctx) {
1164 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1165
1166 if (ctxt == NULL) return(NULL);
1167
1168 return(ctxt->authHeader);
1169}
1170
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001171#ifdef STANDALONE
1172int main(int argc, char **argv) {
1173 char *contentType = NULL;
1174
1175 if (argv[1] != NULL) {
1176 if (argv[2] != NULL)
1177 xmlNanoHTTPFetch(argv[1], argv[2], &contentType);
1178 else
1179 xmlNanoHTTPFetch(argv[1], "-", &contentType);
1180 if (contentType != NULL) xmlFree(contentType);
1181 } else {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001182 xmlGenericError(xmlGenericErrorContext,
1183 "%s: minimal HTTP GET implementation\n", argv[0]);
1184 xmlGenericError(xmlGenericErrorContext,
1185 "\tusage %s [ URL [ filename ] ]\n", argv[0]);
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001186 }
Daniel Veillarde41f2b72000-01-30 20:00:07 +00001187 xmlNanoHTTPCleanup();
1188 xmlMemoryDump();
Daniel Veillard4ecf39f1999-09-22 12:14:03 +00001189 return(0);
1190}
1191#endif /* STANDALONE */
Daniel Veillard361d8452000-04-03 19:48:13 +00001192#else /* !LIBXML_HTTP_ENABLED */
1193#ifdef STANDALONE
1194#include <stdio.h>
1195int main(int argc, char **argv) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001196 xmlGenericError(xmlGenericErrorContext,
1197 "%s : HTTP support not compiled in\n", argv[0]);
Daniel Veillard361d8452000-04-03 19:48:13 +00001198 return(0);
1199}
1200#endif /* STANDALONE */
1201#endif /* LIBXML_HTTP_ENABLED */