blob: c99f6e13145fc5edb14e25aee6d4e1cb8ee24111 [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +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 *
Daniel Veillardc5d64342001-06-24 12:13:24 +000011 * daniel@veillard.com
Owen Taylor3473f882001-02-23 17:55:21 +000012 */
13
14/* TODO add compression support, Send the Accept- , and decompress on the
15 fly with ZLIB if found at compile-time */
16
Daniel Veillardf3afa7d2001-06-09 13:52:58 +000017#define NEED_SOCKETS
Daniel Veillard34ce8be2002-03-18 19:37:11 +000018#define IN_LIBXML
Bjorn Reese70a9da52001-04-21 16:57:29 +000019#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000020
21#ifdef LIBXML_HTTP_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +000022#include <string.h>
23
24#ifdef HAVE_STDLIB_H
25#include <stdlib.h>
26#endif
27#ifdef HAVE_UNISTD_H
28#include <unistd.h>
29#endif
30#ifdef HAVE_SYS_SOCKET_H
31#include <sys/socket.h>
32#endif
33#ifdef HAVE_NETINET_IN_H
34#include <netinet/in.h>
35#endif
36#ifdef HAVE_ARPA_INET_H
37#include <arpa/inet.h>
38#endif
39#ifdef HAVE_NETDB_H
40#include <netdb.h>
41#endif
Daniel Veillardd85f4f42002-03-25 10:48:46 +000042#ifdef HAVE_RESOLV_H
Daniel Veillard9b731d72002-04-14 12:56:08 +000043#ifdef HAVE_ARPA_NAMESER_H
44#include <arpa/nameser.h>
45#endif
Daniel Veillardd85f4f42002-03-25 10:48:46 +000046#include <resolv.h>
47#endif
Owen Taylor3473f882001-02-23 17:55:21 +000048#ifdef HAVE_FCNTL_H
49#include <fcntl.h>
50#endif
51#ifdef HAVE_ERRNO_H
52#include <errno.h>
53#endif
54#ifdef HAVE_SYS_TIME_H
55#include <sys/time.h>
56#endif
57#ifdef HAVE_SYS_SELECT_H
58#include <sys/select.h>
59#endif
60#ifdef HAVE_STRINGS_H
61#include <strings.h>
62#endif
63#ifdef SUPPORT_IP6
64#include <resolv.h>
65#endif
66
67#ifdef VMS
68#include <stropts>
69#define SOCKLEN_T unsigned int
70#define SOCKET int
71#endif
72
Daniel Veillardd0463562001-10-13 09:15:48 +000073#include <libxml/globals.h>
Daniel Veillardf012a642001-07-23 19:10:52 +000074#include <libxml/xmlerror.h>
Owen Taylor3473f882001-02-23 17:55:21 +000075#include <libxml/xmlmemory.h>
76#include <libxml/parser.h> /* for xmlStr(n)casecmp() */
77#include <libxml/nanohttp.h>
Daniel Veillard3c01b1d2001-10-17 15:58:35 +000078#include <libxml/globals.h>
Owen Taylor3473f882001-02-23 17:55:21 +000079
80/**
81 * A couple portability macros
82 */
83#ifndef _WINSOCKAPI_
84#define closesocket(s) close(s)
85#define SOCKET int
86#endif
87
Daniel Veillard75be0132002-03-13 10:03:35 +000088#ifndef SOCKLEN_T
89#define SOCKLEN_T unsigned int
90#endif
91#ifndef SOCKET
92#define SOCKET int
93#endif
Daniel Veillardf012a642001-07-23 19:10:52 +000094
Owen Taylor3473f882001-02-23 17:55:21 +000095#ifdef STANDALONE
96#define DEBUG_HTTP
97#define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n)
98#define xmlStrcasecmpi(a, b) strcasecmp((char *)a, (char *)b)
99#endif
100
101#define XML_NANO_HTTP_MAX_REDIR 10
102
103#define XML_NANO_HTTP_CHUNK 4096
104
105#define XML_NANO_HTTP_CLOSED 0
106#define XML_NANO_HTTP_WRITE 1
107#define XML_NANO_HTTP_READ 2
108#define XML_NANO_HTTP_NONE 4
109
110typedef struct xmlNanoHTTPCtxt {
111 char *protocol; /* the protocol name */
112 char *hostname; /* the host name */
113 int port; /* the port */
114 char *path; /* the path within the URL */
115 SOCKET fd; /* the file descriptor for the socket */
116 int state; /* WRITE / READ / CLOSED */
117 char *out; /* buffer sent (zero terminated) */
118 char *outptr; /* index within the buffer sent */
119 char *in; /* the receiving buffer */
120 char *content; /* the start of the content */
121 char *inptr; /* the next byte to read from network */
122 char *inrptr; /* the next byte to give back to the client */
123 int inlen; /* len of the input buffer */
124 int last; /* return code for last operation */
125 int returnValue; /* the protocol return value */
Daniel Veillardf012a642001-07-23 19:10:52 +0000126 int ContentLength; /* specified content length from HTTP header */
Owen Taylor3473f882001-02-23 17:55:21 +0000127 char *contentType; /* the MIME type for the input */
128 char *location; /* the new URL in case of redirect */
129 char *authHeader; /* contents of {WWW,Proxy}-Authenticate header */
130} xmlNanoHTTPCtxt, *xmlNanoHTTPCtxtPtr;
131
132static int initialized = 0;
133static char *proxy = NULL; /* the proxy name if any */
134static int proxyPort; /* the proxy port if any */
135static unsigned int timeout = 60;/* the select() timeout in seconds */
136
Daniel Veillardf012a642001-07-23 19:10:52 +0000137int xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len );
138int xmlNanoHTTPContentLength( void * ctx );
139
Owen Taylor3473f882001-02-23 17:55:21 +0000140/**
141 * A portability function
142 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000143static int socket_errno(void) {
Owen Taylor3473f882001-02-23 17:55:21 +0000144#ifdef _WINSOCKAPI_
145 return(WSAGetLastError());
146#else
147 return(errno);
148#endif
149}
150
151/**
152 * xmlNanoHTTPInit:
153 *
154 * Initialize the HTTP protocol layer.
155 * Currently it just checks for proxy informations
156 */
157
158void
159xmlNanoHTTPInit(void) {
160 const char *env;
161#ifdef _WINSOCKAPI_
162 WSADATA wsaData;
163#endif
164
165 if (initialized)
166 return;
167
168#ifdef _WINSOCKAPI_
169 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
170 return;
171#endif
172
173 if (proxy == NULL) {
174 proxyPort = 80;
175 env = getenv("no_proxy");
176 if (env != NULL)
177 goto done;
178 env = getenv("http_proxy");
179 if (env != NULL) {
180 xmlNanoHTTPScanProxy(env);
181 goto done;
182 }
183 env = getenv("HTTP_PROXY");
184 if (env != NULL) {
185 xmlNanoHTTPScanProxy(env);
186 goto done;
187 }
188 }
189done:
190 initialized = 1;
191}
192
193/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000194 * xmlNanoHTTPCleanup:
Owen Taylor3473f882001-02-23 17:55:21 +0000195 *
196 * Cleanup the HTTP protocol layer.
197 */
198
199void
200xmlNanoHTTPCleanup(void) {
201 if (proxy != NULL)
202 xmlFree(proxy);
203#ifdef _WINSOCKAPI_
204 if (initialized)
205 WSACleanup();
206#endif
207 initialized = 0;
208 return;
209}
210
211/**
Owen Taylor3473f882001-02-23 17:55:21 +0000212 * xmlNanoHTTPScanURL:
213 * @ctxt: an HTTP context
214 * @URL: The URL used to initialize the context
215 *
216 * (Re)Initialize an HTTP context by parsing the URL and finding
217 * the protocol host port and path it indicates.
218 */
219
220static void
221xmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt, const char *URL) {
222 const char *cur = URL;
223 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000224 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000225 int port = 0;
226
227 if (ctxt->protocol != NULL) {
228 xmlFree(ctxt->protocol);
229 ctxt->protocol = NULL;
230 }
231 if (ctxt->hostname != NULL) {
232 xmlFree(ctxt->hostname);
233 ctxt->hostname = NULL;
234 }
235 if (ctxt->path != NULL) {
236 xmlFree(ctxt->path);
237 ctxt->path = NULL;
238 }
239 if (URL == NULL) return;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000240 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000241 while (*cur != 0) {
242 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000243 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000244 ctxt->protocol = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000245 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000246 cur += 3;
247 break;
248 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000249 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000250 }
251 if (*cur == 0) return;
252
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000253 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000254 while (1) {
255 if (cur[0] == ':') {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000256 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000257 ctxt->hostname = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000258 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000259 cur += 1;
260 while ((*cur >= '0') && (*cur <= '9')) {
261 port *= 10;
262 port += *cur - '0';
263 cur++;
264 }
265 if (port != 0) ctxt->port = port;
266 while ((cur[0] != '/') && (*cur != 0))
267 cur++;
268 break;
269 }
270 if ((*cur == '/') || (*cur == 0)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000271 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000272 ctxt->hostname = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000273 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000274 break;
275 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000276 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000277 }
278 if (*cur == 0)
279 ctxt->path = xmlMemStrdup("/");
280 else {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000281 indx = 0;
282 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000283 while (*cur != 0)
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000284 buf[indx++] = *cur++;
285 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000286 ctxt->path = xmlMemStrdup(buf);
287 }
288}
289
290/**
291 * xmlNanoHTTPScanProxy:
292 * @URL: The proxy URL used to initialize the proxy context
293 *
294 * (Re)Initialize the HTTP Proxy context by parsing the URL and finding
295 * the protocol host port it indicates.
296 * Should be like http://myproxy/ or http://myproxy:3128/
297 * A NULL URL cleans up proxy informations.
298 */
299
300void
301xmlNanoHTTPScanProxy(const char *URL) {
302 const char *cur = URL;
303 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000304 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000305 int port = 0;
306
307 if (proxy != NULL) {
308 xmlFree(proxy);
309 proxy = NULL;
310 }
311 if (proxyPort != 0) {
312 proxyPort = 0;
313 }
314#ifdef DEBUG_HTTP
315 if (URL == NULL)
316 xmlGenericError(xmlGenericErrorContext,
317 "Removing HTTP proxy info\n");
318 else
319 xmlGenericError(xmlGenericErrorContext,
320 "Using HTTP proxy %s\n", URL);
321#endif
322 if (URL == NULL) return;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000323 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000324 while (*cur != 0) {
325 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000326 buf[indx] = 0;
327 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000328 cur += 3;
329 break;
330 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000331 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000332 }
333 if (*cur == 0) return;
334
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000335 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000336 while (1) {
337 if (cur[0] == ':') {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000338 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000339 proxy = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000340 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000341 cur += 1;
342 while ((*cur >= '0') && (*cur <= '9')) {
343 port *= 10;
344 port += *cur - '0';
345 cur++;
346 }
347 if (port != 0) proxyPort = port;
348 while ((cur[0] != '/') && (*cur != 0))
349 cur++;
350 break;
351 }
352 if ((*cur == '/') || (*cur == 0)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000353 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000354 proxy = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000355 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000356 break;
357 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000358 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000359 }
360}
361
362/**
363 * xmlNanoHTTPNewCtxt:
364 * @URL: The URL used to initialize the context
365 *
366 * Allocate and initialize a new HTTP context.
367 *
368 * Returns an HTTP context or NULL in case of error.
369 */
370
371static xmlNanoHTTPCtxtPtr
372xmlNanoHTTPNewCtxt(const char *URL) {
373 xmlNanoHTTPCtxtPtr ret;
374
375 ret = (xmlNanoHTTPCtxtPtr) xmlMalloc(sizeof(xmlNanoHTTPCtxt));
376 if (ret == NULL) return(NULL);
377
378 memset(ret, 0, sizeof(xmlNanoHTTPCtxt));
379 ret->port = 80;
380 ret->returnValue = 0;
381 ret->fd = -1;
Daniel Veillardf012a642001-07-23 19:10:52 +0000382 ret->ContentLength = -1;
Owen Taylor3473f882001-02-23 17:55:21 +0000383
384 xmlNanoHTTPScanURL(ret, URL);
385
386 return(ret);
387}
388
389/**
390 * xmlNanoHTTPFreeCtxt:
391 * @ctxt: an HTTP context
392 *
393 * Frees the context after closing the connection.
394 */
395
396static void
397xmlNanoHTTPFreeCtxt(xmlNanoHTTPCtxtPtr ctxt) {
398 if (ctxt == NULL) return;
399 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
400 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
401 if (ctxt->path != NULL) xmlFree(ctxt->path);
402 if (ctxt->out != NULL) xmlFree(ctxt->out);
403 if (ctxt->in != NULL) xmlFree(ctxt->in);
404 if (ctxt->contentType != NULL) xmlFree(ctxt->contentType);
405 if (ctxt->location != NULL) xmlFree(ctxt->location);
406 if (ctxt->authHeader != NULL) xmlFree(ctxt->authHeader);
407 ctxt->state = XML_NANO_HTTP_NONE;
408 if (ctxt->fd >= 0) closesocket(ctxt->fd);
409 ctxt->fd = -1;
410 xmlFree(ctxt);
411}
412
413/**
414 * xmlNanoHTTPSend:
415 * @ctxt: an HTTP context
416 *
417 * Send the input needed to initiate the processing on the server side
Daniel Veillardf012a642001-07-23 19:10:52 +0000418 * Returns number of bytes sent or -1 on error.
Owen Taylor3473f882001-02-23 17:55:21 +0000419 */
420
Daniel Veillardf012a642001-07-23 19:10:52 +0000421static int
422xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt, const char * xmt_ptr, int outlen) {
423
424 int total_sent = 0;
425
426 if ( (ctxt->state & XML_NANO_HTTP_WRITE) && (xmt_ptr != NULL ) ) {
427 while (total_sent < outlen) {
428 int nsent = send(ctxt->fd, xmt_ptr + total_sent,
429 outlen - total_sent, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000430 if (nsent>0)
431 total_sent += nsent;
Daniel Veillardf012a642001-07-23 19:10:52 +0000432 else if ( ( nsent == -1 ) &&
Daniel Veillardba6db032001-07-31 16:25:45 +0000433#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
Daniel Veillardf012a642001-07-23 19:10:52 +0000434 ( socket_errno( ) != EAGAIN ) &&
Daniel Veillardba6db032001-07-31 16:25:45 +0000435#endif
Daniel Veillardf012a642001-07-23 19:10:52 +0000436 ( socket_errno( ) != EWOULDBLOCK ) ) {
437 xmlGenericError( xmlGenericErrorContext,
438 "xmlNanoHTTPSend error: %s",
439 strerror( socket_errno( ) ) );
440
441 if ( total_sent == 0 )
442 total_sent = -1;
443 break;
444 }
445 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
453 struct timeval tv;
454 fd_set wfd;
455
456 tv.tv_sec = timeout;
457 tv.tv_usec = 0;
458 FD_ZERO( &wfd );
459 FD_SET( ctxt->fd, &wfd );
460 (void)select( ctxt->fd + 1, NULL, &wfd, NULL, &tv );
461 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000462 }
Owen Taylor3473f882001-02-23 17:55:21 +0000463 }
Daniel Veillardf012a642001-07-23 19:10:52 +0000464
465 return total_sent;
Owen Taylor3473f882001-02-23 17:55:21 +0000466}
467
468/**
469 * xmlNanoHTTPRecv:
470 * @ctxt: an HTTP context
471 *
472 * Read information coming from the HTTP connection.
473 * This is a blocking call (but it blocks in select(), not read()).
474 *
475 * Returns the number of byte read or -1 in case of error.
476 */
477
478static int
479xmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt) {
480 fd_set rfd;
481 struct timeval tv;
482
483
484 while (ctxt->state & XML_NANO_HTTP_READ) {
485 if (ctxt->in == NULL) {
486 ctxt->in = (char *) xmlMalloc(65000 * sizeof(char));
487 if (ctxt->in == NULL) {
488 ctxt->last = -1;
Daniel Veillardf012a642001-07-23 19:10:52 +0000489 xmlGenericError( xmlGenericErrorContext,
490 "xmlNanoHTTPRecv: Error allocating input memory." );
Owen Taylor3473f882001-02-23 17:55:21 +0000491 return(-1);
492 }
493 ctxt->inlen = 65000;
494 ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in;
495 }
496 if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) {
497 int delta = ctxt->inrptr - ctxt->in;
498 int len = ctxt->inptr - ctxt->inrptr;
499
500 memmove(ctxt->in, ctxt->inrptr, len);
501 ctxt->inrptr -= delta;
502 ctxt->content -= delta;
503 ctxt->inptr -= delta;
504 }
505 if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) {
506 int d_inptr = ctxt->inptr - ctxt->in;
507 int d_content = ctxt->content - ctxt->in;
508 int d_inrptr = ctxt->inrptr - ctxt->in;
Daniel Veillardf012a642001-07-23 19:10:52 +0000509 char * tmp_ptr = ctxt->in;
Owen Taylor3473f882001-02-23 17:55:21 +0000510
511 ctxt->inlen *= 2;
Daniel Veillardf012a642001-07-23 19:10:52 +0000512 ctxt->in = (char *) xmlRealloc(tmp_ptr, ctxt->inlen);
Owen Taylor3473f882001-02-23 17:55:21 +0000513 if (ctxt->in == NULL) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000514 xmlGenericError( xmlGenericErrorContext,
515 "xmlNanoHTTPRecv: %s %d bytes.",
516 "Failed to realloc input buffer to",
517 ctxt->inlen );
518 xmlFree( tmp_ptr );
Owen Taylor3473f882001-02-23 17:55:21 +0000519 ctxt->last = -1;
520 return(-1);
521 }
522 ctxt->inptr = ctxt->in + d_inptr;
523 ctxt->content = ctxt->in + d_content;
524 ctxt->inrptr = ctxt->in + d_inrptr;
525 }
526 ctxt->last = recv(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK, 0);
527 if (ctxt->last > 0) {
528 ctxt->inptr += ctxt->last;
529 return(ctxt->last);
530 }
531 if (ctxt->last == 0) {
532 return(0);
533 }
534 if (ctxt->last == -1) {
535 switch (socket_errno()) {
536 case EINPROGRESS:
537 case EWOULDBLOCK:
538#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
539 case EAGAIN:
540#endif
541 break;
Daniel Veillardf012a642001-07-23 19:10:52 +0000542
543 case ECONNRESET:
544 case ESHUTDOWN:
545 return ( 0 );
546
Owen Taylor3473f882001-02-23 17:55:21 +0000547 default:
Daniel Veillardf012a642001-07-23 19:10:52 +0000548 xmlGenericError( xmlGenericErrorContext,
549 "xmlNanoHTTPRecv: recv( ) failure - %s",
550 strerror( socket_errno( ) ) );
551 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +0000552 }
553 }
554
555 tv.tv_sec = timeout;
556 tv.tv_usec = 0;
557 FD_ZERO(&rfd);
558 FD_SET(ctxt->fd, &rfd);
559
Daniel Veillard50f34372001-08-03 12:06:36 +0000560 if ( (select(ctxt->fd+1, &rfd, NULL, NULL, &tv)<1)
561#if defined(EINTR)
562 && (errno != EINTR)
563#endif
564 )
Owen Taylor3473f882001-02-23 17:55:21 +0000565 return(0);
566 }
567 return(0);
568}
569
570/**
571 * xmlNanoHTTPReadLine:
572 * @ctxt: an HTTP context
573 *
574 * Read one line in the HTTP server output, usually for extracting
575 * the HTTP protocol informations from the answer header.
576 *
577 * Returns a newly allocated string with a copy of the line, or NULL
578 * which indicate the end of the input.
579 */
580
581static char *
582xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt) {
583 char buf[4096];
584 char *bp = buf;
Daniel Veillardf012a642001-07-23 19:10:52 +0000585 int rc;
Owen Taylor3473f882001-02-23 17:55:21 +0000586
587 while (bp - buf < 4095) {
588 if (ctxt->inrptr == ctxt->inptr) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000589 if ( (rc = xmlNanoHTTPRecv(ctxt)) == 0) {
Owen Taylor3473f882001-02-23 17:55:21 +0000590 if (bp == buf)
591 return(NULL);
592 else
593 *bp = 0;
594 return(xmlMemStrdup(buf));
595 }
Daniel Veillardf012a642001-07-23 19:10:52 +0000596 else if ( rc == -1 ) {
597 return ( NULL );
598 }
Owen Taylor3473f882001-02-23 17:55:21 +0000599 }
600 *bp = *ctxt->inrptr++;
601 if (*bp == '\n') {
602 *bp = 0;
603 return(xmlMemStrdup(buf));
604 }
605 if (*bp != '\r')
606 bp++;
607 }
608 buf[4095] = 0;
609 return(xmlMemStrdup(buf));
610}
611
612
613/**
614 * xmlNanoHTTPScanAnswer:
615 * @ctxt: an HTTP context
616 * @line: an HTTP header line
617 *
618 * Try to extract useful informations from the server answer.
619 * We currently parse and process:
620 * - The HTTP revision/ return code
621 * - The Content-Type
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000622 * - The Location for redirect processing.
Owen Taylor3473f882001-02-23 17:55:21 +0000623 *
624 * Returns -1 in case of failure, the file descriptor number otherwise
625 */
626
627static void
628xmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt, const char *line) {
629 const char *cur = line;
630
631 if (line == NULL) return;
632
633 if (!strncmp(line, "HTTP/", 5)) {
634 int version = 0;
635 int ret = 0;
636
637 cur += 5;
638 while ((*cur >= '0') && (*cur <= '9')) {
639 version *= 10;
640 version += *cur - '0';
641 cur++;
642 }
643 if (*cur == '.') {
644 cur++;
645 if ((*cur >= '0') && (*cur <= '9')) {
646 version *= 10;
647 version += *cur - '0';
648 cur++;
649 }
650 while ((*cur >= '0') && (*cur <= '9'))
651 cur++;
652 } else
653 version *= 10;
654 if ((*cur != ' ') && (*cur != '\t')) return;
655 while ((*cur == ' ') || (*cur == '\t')) cur++;
656 if ((*cur < '0') || (*cur > '9')) return;
657 while ((*cur >= '0') && (*cur <= '9')) {
658 ret *= 10;
659 ret += *cur - '0';
660 cur++;
661 }
662 if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return;
663 ctxt->returnValue = ret;
664 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Content-Type:", 13)) {
665 cur += 13;
666 while ((*cur == ' ') || (*cur == '\t')) cur++;
667 if (ctxt->contentType != NULL)
668 xmlFree(ctxt->contentType);
669 ctxt->contentType = xmlMemStrdup(cur);
670 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"ContentType:", 12)) {
671 cur += 12;
672 if (ctxt->contentType != NULL) return;
673 while ((*cur == ' ') || (*cur == '\t')) cur++;
674 ctxt->contentType = xmlMemStrdup(cur);
675 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Location:", 9)) {
676 cur += 9;
677 while ((*cur == ' ') || (*cur == '\t')) cur++;
678 if (ctxt->location != NULL)
679 xmlFree(ctxt->location);
680 ctxt->location = xmlMemStrdup(cur);
681 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"WWW-Authenticate:", 17)) {
682 cur += 17;
683 while ((*cur == ' ') || (*cur == '\t')) cur++;
684 if (ctxt->authHeader != NULL)
685 xmlFree(ctxt->authHeader);
686 ctxt->authHeader = xmlMemStrdup(cur);
687 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Proxy-Authenticate:", 19)) {
688 cur += 19;
689 while ((*cur == ' ') || (*cur == '\t')) cur++;
690 if (ctxt->authHeader != NULL)
691 xmlFree(ctxt->authHeader);
692 ctxt->authHeader = xmlMemStrdup(cur);
Daniel Veillardf012a642001-07-23 19:10:52 +0000693 } else if ( !xmlStrncasecmp( BAD_CAST line, BAD_CAST"Content-Length:", 15) ) {
694 cur += 15;
695 ctxt->ContentLength = strtol( cur, NULL, 10 );
Owen Taylor3473f882001-02-23 17:55:21 +0000696 }
697}
698
699/**
700 * xmlNanoHTTPConnectAttempt:
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000701 * @addr: a socket address structure
Owen Taylor3473f882001-02-23 17:55:21 +0000702 *
703 * Attempt a connection to the given IP:port endpoint. It forces
704 * non-blocking semantic on the socket, and allow 60 seconds for
705 * the host to answer.
706 *
707 * Returns -1 in case of failure, the file descriptor number otherwise
708 */
709
710static int
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000711xmlNanoHTTPConnectAttempt(struct sockaddr *addr)
Owen Taylor3473f882001-02-23 17:55:21 +0000712{
713 SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
714 fd_set wfd;
715 struct timeval tv;
716 int status;
717
718 if (s==-1) {
719#ifdef DEBUG_HTTP
720 perror("socket");
721#endif
Daniel Veillardf012a642001-07-23 19:10:52 +0000722 xmlGenericError( xmlGenericErrorContext,
723 "xmlNanoHTTPConnectAttempt: %s - %s",
724 "socket creation failure",
725 strerror( socket_errno( ) ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000726 return(-1);
727 }
728
729#ifdef _WINSOCKAPI_
730 {
731 u_long one = 1;
732
733 status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0;
734 }
735#else /* _WINSOCKAPI_ */
736#if defined(VMS)
737 {
738 int enable = 1;
739 status = ioctl(s, FIONBIO, &enable);
740 }
741#else /* VMS */
742 if ((status = fcntl(s, F_GETFL, 0)) != -1) {
743#ifdef O_NONBLOCK
744 status |= O_NONBLOCK;
745#else /* O_NONBLOCK */
746#ifdef F_NDELAY
747 status |= F_NDELAY;
748#endif /* F_NDELAY */
749#endif /* !O_NONBLOCK */
750 status = fcntl(s, F_SETFL, status);
751 }
752 if (status < 0) {
753#ifdef DEBUG_HTTP
754 perror("nonblocking");
755#endif
Daniel Veillardf012a642001-07-23 19:10:52 +0000756 xmlGenericError( xmlGenericErrorContext,
757 "xmlNanoHTTPConnectAttempt: %s - %s",
758 "error setting non-blocking IO",
759 strerror( socket_errno( ) ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000760 closesocket(s);
761 return(-1);
762 }
763#endif /* !VMS */
764#endif /* !_WINSOCKAPI_ */
765
Owen Taylor3473f882001-02-23 17:55:21 +0000766 if ((connect(s, addr, sizeof(*addr))==-1)) {
767 switch (socket_errno()) {
768 case EINPROGRESS:
769 case EWOULDBLOCK:
770 break;
771 default:
Daniel Veillardf012a642001-07-23 19:10:52 +0000772 xmlGenericError( xmlGenericErrorContext,
773 "xmlNanoHTTPConnectAttempt: %s - %s",
774 "error connecting to HTTP server",
775 strerror( socket_errno( ) ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000776 closesocket(s);
777 return(-1);
778 }
779 }
780
781 tv.tv_sec = timeout;
782 tv.tv_usec = 0;
783
784 FD_ZERO(&wfd);
785 FD_SET(s, &wfd);
786
787 switch(select(s+1, NULL, &wfd, NULL, &tv))
788 {
789 case 0:
790 /* Time out */
Daniel Veillardf012a642001-07-23 19:10:52 +0000791 xmlGenericError( xmlGenericErrorContext,
792 "xmlNanoHTTPConnectAttempt: %s",
793 "Connect attempt timed out." );
Owen Taylor3473f882001-02-23 17:55:21 +0000794 closesocket(s);
795 return(-1);
796 case -1:
797 /* Ermm.. ?? */
Daniel Veillardf012a642001-07-23 19:10:52 +0000798 xmlGenericError( xmlGenericErrorContext,
799 "xmlNanoHTTPConnectAttempt: %s - %s",
800 "Error connecting to host",
801 strerror( socket_errno( ) ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000802 closesocket(s);
803 return(-1);
804 }
805
806 if ( FD_ISSET(s, &wfd) ) {
807 SOCKLEN_T len;
808 len = sizeof(status);
809 if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&status, &len) < 0 ) {
810 /* Solaris error code */
Daniel Veillardf012a642001-07-23 19:10:52 +0000811 xmlGenericError( xmlGenericErrorContext,
812 "xmlNanoHTTPConnectAttempt: %s - %s",
813 "Error retrieving pending socket errors",
814 strerror( socket_errno( ) ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000815 return (-1);
816 }
817 if ( status ) {
818 closesocket(s);
819 errno = status;
Daniel Veillardf012a642001-07-23 19:10:52 +0000820 xmlGenericError( xmlGenericErrorContext,
821 "xmlNanoHTTPConnectAttempt: %s - %s",
822 "Error connecting to remote host",
823 strerror( status ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000824 return (-1);
825 }
826 } else {
827 /* pbm */
Daniel Veillardf012a642001-07-23 19:10:52 +0000828 xmlGenericError( xmlGenericErrorContext,
829 "xmlNanoHTTPConnectAttempt: %s\n",
830 "Select returned, but descriptor not set for connection.\n" );
831 closesocket(s);
Owen Taylor3473f882001-02-23 17:55:21 +0000832 return (-1);
833 }
834
835 return(s);
836}
837
838/**
839 * xmlNanoHTTPConnectHost:
840 * @host: the host name
841 * @port: the port number
842 *
843 * Attempt a connection to the given host:port endpoint. It tries
844 * the multiple IP provided by the DNS if available.
845 *
846 * Returns -1 in case of failure, the file descriptor number otherwise
847 */
848
849static int
850xmlNanoHTTPConnectHost(const char *host, int port)
851{
852 struct hostent *h;
853 struct sockaddr *addr;
854 struct in_addr ia;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000855 struct sockaddr_in sockin;
Daniel Veillard5c396542002-03-15 07:57:50 +0000856
Owen Taylor3473f882001-02-23 17:55:21 +0000857#ifdef SUPPORT_IP6
858 struct in6_addr ia6;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000859 struct sockaddr_in6 sockin6;
Owen Taylor3473f882001-02-23 17:55:21 +0000860#endif
861 int i;
862 int s;
Daniel Veillard5c396542002-03-15 07:57:50 +0000863
Owen Taylor3473f882001-02-23 17:55:21 +0000864#if defined(SUPPORT_IP6) && defined(RES_USE_INET6)
865 if (!(_res.options & RES_INIT))
Daniel Veillard5c396542002-03-15 07:57:50 +0000866 res_init();
Owen Taylor3473f882001-02-23 17:55:21 +0000867 _res.options |= RES_USE_INET6;
868#endif
Daniel Veillard5c396542002-03-15 07:57:50 +0000869 h = gethostbyname(host);
870 if (h == NULL) {
Daniel Veillard56b2db72002-03-25 16:35:28 +0000871
872/*
873 * Okay, I got fed up by the non-portability of this error message
874 * extraction code. it work on Linux, if it work on your platform
875 * and one want to enable it, send me the defined(foobar) needed
876 */
877#if defined(HAVE_NETDB_H) && defined(HOST_NOT_FOUND) && defined(linux)
Daniel Veillard5c396542002-03-15 07:57:50 +0000878 const char *h_err_txt = "";
Daniel Veillardf012a642001-07-23 19:10:52 +0000879
Daniel Veillard5c396542002-03-15 07:57:50 +0000880 switch (h_errno) {
881 case HOST_NOT_FOUND:
882 h_err_txt = "Authoritive host not found";
883 break;
Daniel Veillardf012a642001-07-23 19:10:52 +0000884
Daniel Veillard5c396542002-03-15 07:57:50 +0000885 case TRY_AGAIN:
886 h_err_txt =
887 "Non-authoritive host not found or server failure.";
888 break;
Daniel Veillardf012a642001-07-23 19:10:52 +0000889
Daniel Veillard5c396542002-03-15 07:57:50 +0000890 case NO_RECOVERY:
891 h_err_txt =
892 "Non-recoverable errors: FORMERR, REFUSED, or NOTIMP.";
893 break;
894
895 case NO_ADDRESS:
896 h_err_txt =
897 "Valid name, no data record of requested type.";
898 break;
899
900 default:
901 h_err_txt = "No error text defined.";
902 break;
903 }
904 xmlGenericError(xmlGenericErrorContext,
905 "xmlNanoHTTPConnectHost: %s '%s' - %s",
906 "Failed to resolve host", host, h_err_txt);
907#else
908 xmlGenericError(xmlGenericErrorContext,
909 "xmlNanoHTTPConnectHost: %s '%s'",
910 "Failed to resolve host", host);
Owen Taylor3473f882001-02-23 17:55:21 +0000911#endif
Daniel Veillard5c396542002-03-15 07:57:50 +0000912 return (-1);
913 }
914
915 for (i = 0; h->h_addr_list[i]; i++) {
916 if (h->h_addrtype == AF_INET) {
917 /* A records (IPv4) */
918 memcpy(&ia, h->h_addr_list[i], h->h_length);
919 sockin.sin_family = h->h_addrtype;
920 sockin.sin_addr = ia;
921 sockin.sin_port = htons(port);
922 addr = (struct sockaddr *) &sockin;
923#ifdef SUPPORT_IP6
924 } else if (h->h_addrtype == AF_INET6) {
925 /* AAAA records (IPv6) */
926 memcpy(&ia6, h->h_addr_list[i], h->h_length);
927 sockin6.sin_family = h->h_addrtype;
928 sockin6.sin_addr = ia6;
929 sockin6.sin_port = htons(port);
930 addr = (struct sockaddr *) &sockin6;
931#endif
932 } else
933 break; /* for */
934
935 s = xmlNanoHTTPConnectAttempt(addr);
936 if (s != -1)
937 return (s);
Owen Taylor3473f882001-02-23 17:55:21 +0000938 }
939
940#ifdef DEBUG_HTTP
941 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard5c396542002-03-15 07:57:50 +0000942 "xmlNanoHTTPConnectHost: unable to connect to '%s'.\n",
943 host);
Owen Taylor3473f882001-02-23 17:55:21 +0000944#endif
Daniel Veillard5c396542002-03-15 07:57:50 +0000945 return (-1);
Owen Taylor3473f882001-02-23 17:55:21 +0000946}
947
948
949/**
950 * xmlNanoHTTPOpen:
951 * @URL: The URL to load
952 * @contentType: if available the Content-Type information will be
953 * returned at that location
954 *
955 * This function try to open a connection to the indicated resource
956 * via HTTP GET.
957 *
958 * Returns NULL in case of failure, otherwise a request handler.
959 * The contentType, if provided must be freed by the caller
960 */
961
962void*
963xmlNanoHTTPOpen(const char *URL, char **contentType) {
964 if (contentType != NULL) *contentType = NULL;
Daniel Veillardf012a642001-07-23 19:10:52 +0000965 return(xmlNanoHTTPMethod(URL, NULL, NULL, contentType, NULL, 0));
Daniel Veillard9403a042001-05-28 11:00:53 +0000966}
967
968/**
969 * xmlNanoHTTPOpenRedir:
970 * @URL: The URL to load
971 * @contentType: if available the Content-Type information will be
972 * returned at that location
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000973 * @redir: if available the redirected URL will be returned
Daniel Veillard9403a042001-05-28 11:00:53 +0000974 *
975 * This function try to open a connection to the indicated resource
976 * via HTTP GET.
977 *
978 * Returns NULL in case of failure, otherwise a request handler.
979 * The contentType, if provided must be freed by the caller
980 */
981
982void*
983xmlNanoHTTPOpenRedir(const char *URL, char **contentType, char **redir) {
984 if (contentType != NULL) *contentType = NULL;
985 if (redir != NULL) *redir = NULL;
Daniel Veillardf012a642001-07-23 19:10:52 +0000986 return(xmlNanoHTTPMethodRedir(URL, NULL, NULL, contentType, redir, NULL,0));
Owen Taylor3473f882001-02-23 17:55:21 +0000987}
988
989/**
990 * xmlNanoHTTPRead:
991 * @ctx: the HTTP context
992 * @dest: a buffer
993 * @len: the buffer length
994 *
995 * This function tries to read @len bytes from the existing HTTP connection
996 * and saves them in @dest. This is a blocking call.
997 *
998 * Returns the number of byte read. 0 is an indication of an end of connection.
999 * -1 indicates a parameter error.
1000 */
1001int
1002xmlNanoHTTPRead(void *ctx, void *dest, int len) {
1003 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1004
1005 if (ctx == NULL) return(-1);
1006 if (dest == NULL) return(-1);
1007 if (len <= 0) return(0);
1008
1009 while (ctxt->inptr - ctxt->inrptr < len) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001010 if (xmlNanoHTTPRecv(ctxt) <= 0) break;
Owen Taylor3473f882001-02-23 17:55:21 +00001011 }
1012 if (ctxt->inptr - ctxt->inrptr < len)
1013 len = ctxt->inptr - ctxt->inrptr;
1014 memcpy(dest, ctxt->inrptr, len);
1015 ctxt->inrptr += len;
1016 return(len);
1017}
1018
1019/**
1020 * xmlNanoHTTPClose:
1021 * @ctx: the HTTP context
1022 *
1023 * This function closes an HTTP context, it ends up the connection and
1024 * free all data related to it.
1025 */
1026void
1027xmlNanoHTTPClose(void *ctx) {
1028 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1029
1030 if (ctx == NULL) return;
1031
1032 xmlNanoHTTPFreeCtxt(ctxt);
1033}
1034
1035/**
Daniel Veillard9403a042001-05-28 11:00:53 +00001036 * xmlNanoHTTPMethodRedir:
Owen Taylor3473f882001-02-23 17:55:21 +00001037 * @URL: The URL to load
1038 * @method: the HTTP method to use
1039 * @input: the input string if any
1040 * @contentType: the Content-Type information IN and OUT
Daniel Veillard9403a042001-05-28 11:00:53 +00001041 * @redir: the redirected URL OUT
Owen Taylor3473f882001-02-23 17:55:21 +00001042 * @headers: the extra headers
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001043 * @ilen: input length
Owen Taylor3473f882001-02-23 17:55:21 +00001044 *
1045 * This function try to open a connection to the indicated resource
1046 * via HTTP using the given @method, adding the given extra headers
1047 * and the input buffer for the request content.
1048 *
1049 * Returns NULL in case of failure, otherwise a request handler.
Daniel Veillard9403a042001-05-28 11:00:53 +00001050 * The contentType, or redir, if provided must be freed by the caller
Owen Taylor3473f882001-02-23 17:55:21 +00001051 */
1052
1053void*
Daniel Veillard9403a042001-05-28 11:00:53 +00001054xmlNanoHTTPMethodRedir(const char *URL, const char *method, const char *input,
Daniel Veillardf012a642001-07-23 19:10:52 +00001055 char **contentType, char **redir,
1056 const char *headers, int ilen ) {
Owen Taylor3473f882001-02-23 17:55:21 +00001057 xmlNanoHTTPCtxtPtr ctxt;
1058 char *bp, *p;
Daniel Veillardf012a642001-07-23 19:10:52 +00001059 int blen, ret;
Owen Taylor3473f882001-02-23 17:55:21 +00001060 int head;
Daniel Veillardf012a642001-07-23 19:10:52 +00001061 int xmt_bytes;
Owen Taylor3473f882001-02-23 17:55:21 +00001062 int nbRedirects = 0;
1063 char *redirURL = NULL;
1064
1065 if (URL == NULL) return(NULL);
1066 if (method == NULL) method = "GET";
1067 xmlNanoHTTPInit();
1068
1069retry:
1070 if (redirURL == NULL)
1071 ctxt = xmlNanoHTTPNewCtxt(URL);
1072 else {
1073 ctxt = xmlNanoHTTPNewCtxt(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001074 }
1075
Daniel Veillardf012a642001-07-23 19:10:52 +00001076 if ( ctxt == NULL ) {
1077 xmlGenericError( xmlGenericErrorContext,
1078 "xmlNanoHTTPMethodRedir: %s %s.",
1079 "Unable to allocate HTTP context to URI",
1080 ( ( redirURL == NULL ) ? URL : redirURL ) );
1081 return ( NULL );
1082 }
1083
Owen Taylor3473f882001-02-23 17:55:21 +00001084 if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001085 xmlGenericError( xmlGenericErrorContext,
1086 "xmlNanoHTTPMethodRedir: %s - %s.",
1087 "Not a valid HTTP URI",
1088 ( ( redirURL == NULL ) ? URL : redirURL ) );
Owen Taylor3473f882001-02-23 17:55:21 +00001089 xmlNanoHTTPFreeCtxt(ctxt);
1090 if (redirURL != NULL) xmlFree(redirURL);
1091 return(NULL);
1092 }
1093 if (ctxt->hostname == NULL) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001094 xmlGenericError( xmlGenericErrorContext,
1095 "xmlNanoHTTPMethodRedir: %s - %s",
1096 "Failed to identify host in URI",
1097 ( ( redirURL == NULL ) ? URL : redirURL ) );
Owen Taylor3473f882001-02-23 17:55:21 +00001098 xmlNanoHTTPFreeCtxt(ctxt);
Daniel Veillard9403a042001-05-28 11:00:53 +00001099 if (redirURL != NULL) xmlFree(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001100 return(NULL);
1101 }
1102 if (proxy) {
1103 blen = strlen(ctxt->hostname) * 2 + 16;
1104 ret = xmlNanoHTTPConnectHost(proxy, proxyPort);
1105 }
1106 else {
1107 blen = strlen(ctxt->hostname);
1108 ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
1109 }
1110 if (ret < 0) {
1111 xmlNanoHTTPFreeCtxt(ctxt);
Daniel Veillard9403a042001-05-28 11:00:53 +00001112 if (redirURL != NULL) xmlFree(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001113 return(NULL);
1114 }
1115 ctxt->fd = ret;
1116
Daniel Veillardf012a642001-07-23 19:10:52 +00001117 if (input == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001118 ilen = 0;
Daniel Veillardf012a642001-07-23 19:10:52 +00001119 else
1120 blen += 36;
1121
Owen Taylor3473f882001-02-23 17:55:21 +00001122 if (headers != NULL)
Daniel Veillardf012a642001-07-23 19:10:52 +00001123 blen += strlen(headers) + 2;
Owen Taylor3473f882001-02-23 17:55:21 +00001124 if (contentType && *contentType)
1125 blen += strlen(*contentType) + 16;
Daniel Veillardf012a642001-07-23 19:10:52 +00001126 blen += strlen(method) + strlen(ctxt->path) + 24;
Owen Taylor3473f882001-02-23 17:55:21 +00001127 bp = xmlMalloc(blen);
Daniel Veillardf012a642001-07-23 19:10:52 +00001128 if ( bp == NULL ) {
1129 xmlNanoHTTPFreeCtxt( ctxt );
1130 xmlGenericError( xmlGenericErrorContext,
1131 "xmlNanoHTTPMethodRedir: %s",
1132 "Error allocating HTTP header buffer." );
1133 return ( NULL );
1134 }
1135
1136 p = bp;
1137
Owen Taylor3473f882001-02-23 17:55:21 +00001138 if (proxy) {
1139 if (ctxt->port != 80) {
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001140 p += snprintf( p, blen - (p - bp), "%s http://%s:%d%s",
1141 method, ctxt->hostname,
Daniel Veillardf012a642001-07-23 19:10:52 +00001142 ctxt->port, ctxt->path );
Owen Taylor3473f882001-02-23 17:55:21 +00001143 }
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001144 else
1145 p += snprintf( p, blen - (p - bp), "%s http://%s%s", method,
Daniel Veillardf012a642001-07-23 19:10:52 +00001146 ctxt->hostname, ctxt->path);
Owen Taylor3473f882001-02-23 17:55:21 +00001147 }
1148 else
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001149 p += snprintf( p, blen - (p - bp), "%s %s", method, ctxt->path);
Daniel Veillardf012a642001-07-23 19:10:52 +00001150
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001151 p += snprintf( p, blen - (p - bp), " HTTP/1.0\r\nHost: %s\r\n",
1152 ctxt->hostname);
Daniel Veillardf012a642001-07-23 19:10:52 +00001153
1154 if (contentType != NULL && *contentType)
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001155 p += snprintf(p, blen - (p - bp), "Content-Type: %s\r\n", *contentType);
Daniel Veillardf012a642001-07-23 19:10:52 +00001156
1157 if (headers != NULL)
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001158 p += snprintf( p, blen - (p - bp), "%s", headers );
Daniel Veillardf012a642001-07-23 19:10:52 +00001159
Owen Taylor3473f882001-02-23 17:55:21 +00001160 if (input != NULL)
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001161 snprintf(p, blen - (p - bp), "Content-Length: %d\r\n\r\n", ilen );
Owen Taylor3473f882001-02-23 17:55:21 +00001162 else
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001163 snprintf(p, blen - (p - bp), "\r\n");
Daniel Veillardf012a642001-07-23 19:10:52 +00001164
Owen Taylor3473f882001-02-23 17:55:21 +00001165#ifdef DEBUG_HTTP
1166 xmlGenericError(xmlGenericErrorContext,
1167 "-> %s%s", proxy? "(Proxy) " : "", bp);
1168 if ((blen -= strlen(bp)+1) < 0)
1169 xmlGenericError(xmlGenericErrorContext,
1170 "ERROR: overflowed buffer by %d bytes\n", -blen);
1171#endif
1172 ctxt->outptr = ctxt->out = bp;
1173 ctxt->state = XML_NANO_HTTP_WRITE;
Daniel Veillardf012a642001-07-23 19:10:52 +00001174 blen = strlen( ctxt->out );
1175 xmt_bytes = xmlNanoHTTPSend(ctxt, ctxt->out, blen );
1176#ifdef DEBUG_HTTP
1177 if ( xmt_bytes != blen )
1178 xmlGenericError( xmlGenericErrorContext,
1179 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
1180 xmt_bytes, blen,
1181 "bytes of HTTP headers sent to host",
1182 ctxt->hostname );
1183#endif
1184
1185 if ( input != NULL ) {
1186 xmt_bytes = xmlNanoHTTPSend( ctxt, input, ilen );
1187
1188#ifdef DEBUG_HTTP
1189 if ( xmt_bytes != ilen )
1190 xmlGenericError( xmlGenericErrorContext,
1191 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
1192 xmt_bytes, ilen,
1193 "bytes of HTTP content sent to host",
1194 ctxt->hostname );
1195#endif
1196 }
1197
Owen Taylor3473f882001-02-23 17:55:21 +00001198 ctxt->state = XML_NANO_HTTP_READ;
1199 head = 1;
1200
1201 while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
1202 if (head && (*p == 0)) {
1203 head = 0;
1204 ctxt->content = ctxt->inrptr;
1205 xmlFree(p);
1206 break;
1207 }
1208 xmlNanoHTTPScanAnswer(ctxt, p);
1209
1210#ifdef DEBUG_HTTP
1211 xmlGenericError(xmlGenericErrorContext, "<- %s\n", p);
1212#endif
1213 xmlFree(p);
1214 }
1215
1216 if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
1217 (ctxt->returnValue < 400)) {
1218#ifdef DEBUG_HTTP
1219 xmlGenericError(xmlGenericErrorContext,
1220 "\nRedirect to: %s\n", ctxt->location);
1221#endif
Daniel Veillardf012a642001-07-23 19:10:52 +00001222 while ( xmlNanoHTTPRecv(ctxt) > 0 ) ;
Owen Taylor3473f882001-02-23 17:55:21 +00001223 if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
1224 nbRedirects++;
Daniel Veillard9403a042001-05-28 11:00:53 +00001225 if (redirURL != NULL)
1226 xmlFree(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001227 redirURL = xmlMemStrdup(ctxt->location);
1228 xmlNanoHTTPFreeCtxt(ctxt);
1229 goto retry;
1230 }
1231 xmlNanoHTTPFreeCtxt(ctxt);
Daniel Veillard9403a042001-05-28 11:00:53 +00001232 if (redirURL != NULL) xmlFree(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001233#ifdef DEBUG_HTTP
1234 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf012a642001-07-23 19:10:52 +00001235 "xmlNanoHTTPMethodRedir: Too many redirects, aborting ...\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001236#endif
1237 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001238 }
1239
1240 if (contentType != NULL) {
1241 if (ctxt->contentType != NULL)
1242 *contentType = xmlMemStrdup(ctxt->contentType);
1243 else
1244 *contentType = NULL;
1245 }
1246
Daniel Veillard9403a042001-05-28 11:00:53 +00001247 if ((redir != NULL) && (redirURL != NULL)) {
1248 *redir = redirURL;
1249 } else {
1250 if (redirURL != NULL)
1251 xmlFree(redirURL);
1252 if (redir != NULL)
1253 *redir = NULL;
1254 }
1255
Owen Taylor3473f882001-02-23 17:55:21 +00001256#ifdef DEBUG_HTTP
1257 if (ctxt->contentType != NULL)
1258 xmlGenericError(xmlGenericErrorContext,
1259 "\nCode %d, content-type '%s'\n\n",
1260 ctxt->returnValue, ctxt->contentType);
1261 else
1262 xmlGenericError(xmlGenericErrorContext,
1263 "\nCode %d, no content-type\n\n",
1264 ctxt->returnValue);
1265#endif
1266
1267 return((void *) ctxt);
1268}
1269
1270/**
Daniel Veillard9403a042001-05-28 11:00:53 +00001271 * xmlNanoHTTPMethod:
1272 * @URL: The URL to load
1273 * @method: the HTTP method to use
1274 * @input: the input string if any
1275 * @contentType: the Content-Type information IN and OUT
1276 * @headers: the extra headers
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001277 * @ilen: input length
Daniel Veillard9403a042001-05-28 11:00:53 +00001278 *
1279 * This function try to open a connection to the indicated resource
1280 * via HTTP using the given @method, adding the given extra headers
1281 * and the input buffer for the request content.
1282 *
1283 * Returns NULL in case of failure, otherwise a request handler.
1284 * The contentType, if provided must be freed by the caller
1285 */
1286
1287void*
1288xmlNanoHTTPMethod(const char *URL, const char *method, const char *input,
Daniel Veillardf012a642001-07-23 19:10:52 +00001289 char **contentType, const char *headers, int ilen) {
Daniel Veillard9403a042001-05-28 11:00:53 +00001290 return(xmlNanoHTTPMethodRedir(URL, method, input, contentType,
Daniel Veillardf012a642001-07-23 19:10:52 +00001291 NULL, headers, ilen));
Daniel Veillard9403a042001-05-28 11:00:53 +00001292}
1293
1294/**
Owen Taylor3473f882001-02-23 17:55:21 +00001295 * xmlNanoHTTPFetch:
1296 * @URL: The URL to load
1297 * @filename: the filename where the content should be saved
1298 * @contentType: if available the Content-Type information will be
1299 * returned at that location
1300 *
1301 * This function try to fetch the indicated resource via HTTP GET
1302 * and save it's content in the file.
1303 *
1304 * Returns -1 in case of failure, 0 incase of success. The contentType,
1305 * if provided must be freed by the caller
1306 */
1307int
1308xmlNanoHTTPFetch(const char *URL, const char *filename, char **contentType) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001309 void *ctxt = NULL;
1310 char *buf = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001311 int fd;
1312 int len;
1313
1314 ctxt = xmlNanoHTTPOpen(URL, contentType);
1315 if (ctxt == NULL) return(-1);
1316
1317 if (!strcmp(filename, "-"))
1318 fd = 0;
1319 else {
1320 fd = open(filename, O_CREAT | O_WRONLY, 00644);
1321 if (fd < 0) {
1322 xmlNanoHTTPClose(ctxt);
1323 if ((contentType != NULL) && (*contentType != NULL)) {
1324 xmlFree(*contentType);
1325 *contentType = NULL;
1326 }
1327 return(-1);
1328 }
1329 }
1330
Daniel Veillardf012a642001-07-23 19:10:52 +00001331 xmlNanoHTTPFetchContent( ctxt, &buf, &len );
1332 if ( len > 0 ) {
Owen Taylor3473f882001-02-23 17:55:21 +00001333 write(fd, buf, len);
1334 }
1335
1336 xmlNanoHTTPClose(ctxt);
1337 close(fd);
1338 return(0);
1339}
1340
1341/**
1342 * xmlNanoHTTPSave:
1343 * @ctxt: the HTTP context
1344 * @filename: the filename where the content should be saved
1345 *
1346 * This function saves the output of the HTTP transaction to a file
1347 * It closes and free the context at the end
1348 *
1349 * Returns -1 in case of failure, 0 incase of success.
1350 */
1351int
1352xmlNanoHTTPSave(void *ctxt, const char *filename) {
Daniel Veillarde3924972001-07-25 20:25:21 +00001353 char *buf = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001354 int fd;
1355 int len;
1356
1357 if (ctxt == NULL) return(-1);
1358
1359 if (!strcmp(filename, "-"))
1360 fd = 0;
1361 else {
1362 fd = open(filename, O_CREAT | O_WRONLY);
1363 if (fd < 0) {
1364 xmlNanoHTTPClose(ctxt);
1365 return(-1);
1366 }
1367 }
1368
Daniel Veillardf012a642001-07-23 19:10:52 +00001369 xmlNanoHTTPFetchContent( ctxt, &buf, &len );
1370 if ( len > 0 ) {
Owen Taylor3473f882001-02-23 17:55:21 +00001371 write(fd, buf, len);
1372 }
1373
1374 xmlNanoHTTPClose(ctxt);
1375 return(0);
1376}
1377
1378/**
1379 * xmlNanoHTTPReturnCode:
1380 * @ctx: the HTTP context
1381 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001382 * Get the latest HTTP return code received
1383 *
Owen Taylor3473f882001-02-23 17:55:21 +00001384 * Returns the HTTP return code for the request.
1385 */
1386int
1387xmlNanoHTTPReturnCode(void *ctx) {
1388 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1389
1390 if (ctxt == NULL) return(-1);
1391
1392 return(ctxt->returnValue);
1393}
1394
1395/**
1396 * xmlNanoHTTPAuthHeader:
1397 * @ctx: the HTTP context
1398 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001399 * Get the authentication header of an HTTP context
1400 *
Owen Taylor3473f882001-02-23 17:55:21 +00001401 * Returns the stashed value of the WWW-Authenticate or Proxy-Authenticate
1402 * header.
1403 */
1404const char *
1405xmlNanoHTTPAuthHeader(void *ctx) {
1406 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1407
1408 if (ctxt == NULL) return(NULL);
1409
1410 return(ctxt->authHeader);
1411}
1412
Daniel Veillardf012a642001-07-23 19:10:52 +00001413/**
1414 * xmlNanoHTTPContentLength
1415 * @ctx: the HTTP context
1416 *
1417 * Return the specified content length from the HTTP header. Note that
1418 * a value of -1 indicates that the content length element was not included in
1419 * the response header.
1420 */
1421int
1422xmlNanoHTTPContentLength( void * ctx ) {
1423 xmlNanoHTTPCtxtPtr ctxt = ctx;
1424
1425 return ( ( ctxt == NULL ) ? -1 : ctxt->ContentLength );
1426}
1427
1428/**
1429 * xmlNanoHTTPFetchContent
1430 * @ctx: the HTTP context
1431 * @ptr: pointer to set to the content buffer.
1432 * @len: integer pointer to hold the length of the content
1433 *
1434 * Returns 0 if all the content was read and available, returns
1435 * -1 if received content length was less than specified or an error
1436 * occurred.
1437 */
1438int
1439xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len ) {
1440 xmlNanoHTTPCtxtPtr ctxt = ctx;
1441
1442 int rc = 0;
1443 int cur_lgth;
1444 int rcvd_lgth;
1445 int dummy_int;
1446 char * dummy_ptr = NULL;
1447
1448 /* Dummy up return input parameters if not provided */
1449
1450 if ( len == NULL )
1451 len = &dummy_int;
1452
1453 if ( ptr == NULL )
1454 ptr = &dummy_ptr;
1455
1456 /* But can't work without the context pointer */
1457
1458 if ( ( ctxt == NULL ) || ( ctxt->content == NULL ) ) {
1459 *len = 0;
1460 *ptr = NULL;
1461 return ( -1 );
1462 }
1463
1464 rcvd_lgth = ctxt->inptr - ctxt->content;
1465
1466 while ( (cur_lgth = xmlNanoHTTPRecv( ctxt )) > 0 ) {
1467
1468 rcvd_lgth += cur_lgth;
1469 if ( (ctxt->ContentLength > 0) && (rcvd_lgth >= ctxt->ContentLength) )
1470 break;
1471 }
1472
1473 *ptr = ctxt->content;
1474 *len = rcvd_lgth;
1475
1476 if ( ( ctxt->ContentLength > 0 ) && ( rcvd_lgth < ctxt->ContentLength ) )
1477 rc = -1;
1478 else if ( rcvd_lgth == 0 )
1479 rc = -1;
1480
1481 return ( rc );
1482}
1483
Owen Taylor3473f882001-02-23 17:55:21 +00001484#ifdef STANDALONE
1485int main(int argc, char **argv) {
1486 char *contentType = NULL;
1487
1488 if (argv[1] != NULL) {
1489 if (argv[2] != NULL)
1490 xmlNanoHTTPFetch(argv[1], argv[2], &contentType);
1491 else
1492 xmlNanoHTTPFetch(argv[1], "-", &contentType);
1493 if (contentType != NULL) xmlFree(contentType);
1494 } else {
1495 xmlGenericError(xmlGenericErrorContext,
1496 "%s: minimal HTTP GET implementation\n", argv[0]);
1497 xmlGenericError(xmlGenericErrorContext,
1498 "\tusage %s [ URL [ filename ] ]\n", argv[0]);
1499 }
1500 xmlNanoHTTPCleanup();
1501 xmlMemoryDump();
1502 return(0);
1503}
1504#endif /* STANDALONE */
1505#else /* !LIBXML_HTTP_ENABLED */
1506#ifdef STANDALONE
1507#include <stdio.h>
1508int main(int argc, char **argv) {
1509 xmlGenericError(xmlGenericErrorContext,
1510 "%s : HTTP support not compiled in\n", argv[0]);
1511 return(0);
1512}
1513#endif /* STANDALONE */
1514#endif /* LIBXML_HTTP_ENABLED */