blob: f3fa51a81f7a1c509389fdbabb2bddd4dae079c4 [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) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001140 p += sprintf( p, "%s http://%s:%d%s", method, ctxt->hostname,
1141 ctxt->port, ctxt->path );
Owen Taylor3473f882001-02-23 17:55:21 +00001142 }
1143 else
Daniel Veillardf012a642001-07-23 19:10:52 +00001144 p += sprintf( p, "%s http://%s%s", method,
1145 ctxt->hostname, ctxt->path);
Owen Taylor3473f882001-02-23 17:55:21 +00001146 }
1147 else
Daniel Veillardf012a642001-07-23 19:10:52 +00001148 p += sprintf( p, "%s %s", method, ctxt->path);
1149
1150 p += sprintf(p, " HTTP/1.0\r\nHost: %s\r\n", ctxt->hostname);
1151
1152 if (contentType != NULL && *contentType)
1153 p += sprintf(p, "Content-Type: %s\r\n", *contentType);
1154
1155 if (headers != NULL)
1156 p += sprintf( p, "%s", headers );
1157
Owen Taylor3473f882001-02-23 17:55:21 +00001158 if (input != NULL)
Daniel Veillardf012a642001-07-23 19:10:52 +00001159 sprintf(p, "Content-Length: %d\r\n\r\n", ilen );
Owen Taylor3473f882001-02-23 17:55:21 +00001160 else
1161 strcpy(p, "\r\n");
Daniel Veillardf012a642001-07-23 19:10:52 +00001162
Owen Taylor3473f882001-02-23 17:55:21 +00001163#ifdef DEBUG_HTTP
1164 xmlGenericError(xmlGenericErrorContext,
1165 "-> %s%s", proxy? "(Proxy) " : "", bp);
1166 if ((blen -= strlen(bp)+1) < 0)
1167 xmlGenericError(xmlGenericErrorContext,
1168 "ERROR: overflowed buffer by %d bytes\n", -blen);
1169#endif
1170 ctxt->outptr = ctxt->out = bp;
1171 ctxt->state = XML_NANO_HTTP_WRITE;
Daniel Veillardf012a642001-07-23 19:10:52 +00001172 blen = strlen( ctxt->out );
1173 xmt_bytes = xmlNanoHTTPSend(ctxt, ctxt->out, blen );
1174#ifdef DEBUG_HTTP
1175 if ( xmt_bytes != blen )
1176 xmlGenericError( xmlGenericErrorContext,
1177 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
1178 xmt_bytes, blen,
1179 "bytes of HTTP headers sent to host",
1180 ctxt->hostname );
1181#endif
1182
1183 if ( input != NULL ) {
1184 xmt_bytes = xmlNanoHTTPSend( ctxt, input, ilen );
1185
1186#ifdef DEBUG_HTTP
1187 if ( xmt_bytes != ilen )
1188 xmlGenericError( xmlGenericErrorContext,
1189 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
1190 xmt_bytes, ilen,
1191 "bytes of HTTP content sent to host",
1192 ctxt->hostname );
1193#endif
1194 }
1195
Owen Taylor3473f882001-02-23 17:55:21 +00001196 ctxt->state = XML_NANO_HTTP_READ;
1197 head = 1;
1198
1199 while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
1200 if (head && (*p == 0)) {
1201 head = 0;
1202 ctxt->content = ctxt->inrptr;
1203 xmlFree(p);
1204 break;
1205 }
1206 xmlNanoHTTPScanAnswer(ctxt, p);
1207
1208#ifdef DEBUG_HTTP
1209 xmlGenericError(xmlGenericErrorContext, "<- %s\n", p);
1210#endif
1211 xmlFree(p);
1212 }
1213
1214 if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
1215 (ctxt->returnValue < 400)) {
1216#ifdef DEBUG_HTTP
1217 xmlGenericError(xmlGenericErrorContext,
1218 "\nRedirect to: %s\n", ctxt->location);
1219#endif
Daniel Veillardf012a642001-07-23 19:10:52 +00001220 while ( xmlNanoHTTPRecv(ctxt) > 0 ) ;
Owen Taylor3473f882001-02-23 17:55:21 +00001221 if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
1222 nbRedirects++;
Daniel Veillard9403a042001-05-28 11:00:53 +00001223 if (redirURL != NULL)
1224 xmlFree(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001225 redirURL = xmlMemStrdup(ctxt->location);
1226 xmlNanoHTTPFreeCtxt(ctxt);
1227 goto retry;
1228 }
1229 xmlNanoHTTPFreeCtxt(ctxt);
Daniel Veillard9403a042001-05-28 11:00:53 +00001230 if (redirURL != NULL) xmlFree(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001231#ifdef DEBUG_HTTP
1232 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf012a642001-07-23 19:10:52 +00001233 "xmlNanoHTTPMethodRedir: Too many redirects, aborting ...\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001234#endif
1235 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001236 }
1237
1238 if (contentType != NULL) {
1239 if (ctxt->contentType != NULL)
1240 *contentType = xmlMemStrdup(ctxt->contentType);
1241 else
1242 *contentType = NULL;
1243 }
1244
Daniel Veillard9403a042001-05-28 11:00:53 +00001245 if ((redir != NULL) && (redirURL != NULL)) {
1246 *redir = redirURL;
1247 } else {
1248 if (redirURL != NULL)
1249 xmlFree(redirURL);
1250 if (redir != NULL)
1251 *redir = NULL;
1252 }
1253
Owen Taylor3473f882001-02-23 17:55:21 +00001254#ifdef DEBUG_HTTP
1255 if (ctxt->contentType != NULL)
1256 xmlGenericError(xmlGenericErrorContext,
1257 "\nCode %d, content-type '%s'\n\n",
1258 ctxt->returnValue, ctxt->contentType);
1259 else
1260 xmlGenericError(xmlGenericErrorContext,
1261 "\nCode %d, no content-type\n\n",
1262 ctxt->returnValue);
1263#endif
1264
1265 return((void *) ctxt);
1266}
1267
1268/**
Daniel Veillard9403a042001-05-28 11:00:53 +00001269 * xmlNanoHTTPMethod:
1270 * @URL: The URL to load
1271 * @method: the HTTP method to use
1272 * @input: the input string if any
1273 * @contentType: the Content-Type information IN and OUT
1274 * @headers: the extra headers
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001275 * @ilen: input length
Daniel Veillard9403a042001-05-28 11:00:53 +00001276 *
1277 * This function try to open a connection to the indicated resource
1278 * via HTTP using the given @method, adding the given extra headers
1279 * and the input buffer for the request content.
1280 *
1281 * Returns NULL in case of failure, otherwise a request handler.
1282 * The contentType, if provided must be freed by the caller
1283 */
1284
1285void*
1286xmlNanoHTTPMethod(const char *URL, const char *method, const char *input,
Daniel Veillardf012a642001-07-23 19:10:52 +00001287 char **contentType, const char *headers, int ilen) {
Daniel Veillard9403a042001-05-28 11:00:53 +00001288 return(xmlNanoHTTPMethodRedir(URL, method, input, contentType,
Daniel Veillardf012a642001-07-23 19:10:52 +00001289 NULL, headers, ilen));
Daniel Veillard9403a042001-05-28 11:00:53 +00001290}
1291
1292/**
Owen Taylor3473f882001-02-23 17:55:21 +00001293 * xmlNanoHTTPFetch:
1294 * @URL: The URL to load
1295 * @filename: the filename where the content should be saved
1296 * @contentType: if available the Content-Type information will be
1297 * returned at that location
1298 *
1299 * This function try to fetch the indicated resource via HTTP GET
1300 * and save it's content in the file.
1301 *
1302 * Returns -1 in case of failure, 0 incase of success. The contentType,
1303 * if provided must be freed by the caller
1304 */
1305int
1306xmlNanoHTTPFetch(const char *URL, const char *filename, char **contentType) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001307 void *ctxt = NULL;
1308 char *buf = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001309 int fd;
1310 int len;
1311
1312 ctxt = xmlNanoHTTPOpen(URL, contentType);
1313 if (ctxt == NULL) return(-1);
1314
1315 if (!strcmp(filename, "-"))
1316 fd = 0;
1317 else {
1318 fd = open(filename, O_CREAT | O_WRONLY, 00644);
1319 if (fd < 0) {
1320 xmlNanoHTTPClose(ctxt);
1321 if ((contentType != NULL) && (*contentType != NULL)) {
1322 xmlFree(*contentType);
1323 *contentType = NULL;
1324 }
1325 return(-1);
1326 }
1327 }
1328
Daniel Veillardf012a642001-07-23 19:10:52 +00001329 xmlNanoHTTPFetchContent( ctxt, &buf, &len );
1330 if ( len > 0 ) {
Owen Taylor3473f882001-02-23 17:55:21 +00001331 write(fd, buf, len);
1332 }
1333
1334 xmlNanoHTTPClose(ctxt);
1335 close(fd);
1336 return(0);
1337}
1338
1339/**
1340 * xmlNanoHTTPSave:
1341 * @ctxt: the HTTP context
1342 * @filename: the filename where the content should be saved
1343 *
1344 * This function saves the output of the HTTP transaction to a file
1345 * It closes and free the context at the end
1346 *
1347 * Returns -1 in case of failure, 0 incase of success.
1348 */
1349int
1350xmlNanoHTTPSave(void *ctxt, const char *filename) {
Daniel Veillarde3924972001-07-25 20:25:21 +00001351 char *buf = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001352 int fd;
1353 int len;
1354
1355 if (ctxt == NULL) return(-1);
1356
1357 if (!strcmp(filename, "-"))
1358 fd = 0;
1359 else {
1360 fd = open(filename, O_CREAT | O_WRONLY);
1361 if (fd < 0) {
1362 xmlNanoHTTPClose(ctxt);
1363 return(-1);
1364 }
1365 }
1366
Daniel Veillardf012a642001-07-23 19:10:52 +00001367 xmlNanoHTTPFetchContent( ctxt, &buf, &len );
1368 if ( len > 0 ) {
Owen Taylor3473f882001-02-23 17:55:21 +00001369 write(fd, buf, len);
1370 }
1371
1372 xmlNanoHTTPClose(ctxt);
1373 return(0);
1374}
1375
1376/**
1377 * xmlNanoHTTPReturnCode:
1378 * @ctx: the HTTP context
1379 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001380 * Get the latest HTTP return code received
1381 *
Owen Taylor3473f882001-02-23 17:55:21 +00001382 * Returns the HTTP return code for the request.
1383 */
1384int
1385xmlNanoHTTPReturnCode(void *ctx) {
1386 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1387
1388 if (ctxt == NULL) return(-1);
1389
1390 return(ctxt->returnValue);
1391}
1392
1393/**
1394 * xmlNanoHTTPAuthHeader:
1395 * @ctx: the HTTP context
1396 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001397 * Get the authentication header of an HTTP context
1398 *
Owen Taylor3473f882001-02-23 17:55:21 +00001399 * Returns the stashed value of the WWW-Authenticate or Proxy-Authenticate
1400 * header.
1401 */
1402const char *
1403xmlNanoHTTPAuthHeader(void *ctx) {
1404 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1405
1406 if (ctxt == NULL) return(NULL);
1407
1408 return(ctxt->authHeader);
1409}
1410
Daniel Veillardf012a642001-07-23 19:10:52 +00001411/**
1412 * xmlNanoHTTPContentLength
1413 * @ctx: the HTTP context
1414 *
1415 * Return the specified content length from the HTTP header. Note that
1416 * a value of -1 indicates that the content length element was not included in
1417 * the response header.
1418 */
1419int
1420xmlNanoHTTPContentLength( void * ctx ) {
1421 xmlNanoHTTPCtxtPtr ctxt = ctx;
1422
1423 return ( ( ctxt == NULL ) ? -1 : ctxt->ContentLength );
1424}
1425
1426/**
1427 * xmlNanoHTTPFetchContent
1428 * @ctx: the HTTP context
1429 * @ptr: pointer to set to the content buffer.
1430 * @len: integer pointer to hold the length of the content
1431 *
1432 * Returns 0 if all the content was read and available, returns
1433 * -1 if received content length was less than specified or an error
1434 * occurred.
1435 */
1436int
1437xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len ) {
1438 xmlNanoHTTPCtxtPtr ctxt = ctx;
1439
1440 int rc = 0;
1441 int cur_lgth;
1442 int rcvd_lgth;
1443 int dummy_int;
1444 char * dummy_ptr = NULL;
1445
1446 /* Dummy up return input parameters if not provided */
1447
1448 if ( len == NULL )
1449 len = &dummy_int;
1450
1451 if ( ptr == NULL )
1452 ptr = &dummy_ptr;
1453
1454 /* But can't work without the context pointer */
1455
1456 if ( ( ctxt == NULL ) || ( ctxt->content == NULL ) ) {
1457 *len = 0;
1458 *ptr = NULL;
1459 return ( -1 );
1460 }
1461
1462 rcvd_lgth = ctxt->inptr - ctxt->content;
1463
1464 while ( (cur_lgth = xmlNanoHTTPRecv( ctxt )) > 0 ) {
1465
1466 rcvd_lgth += cur_lgth;
1467 if ( (ctxt->ContentLength > 0) && (rcvd_lgth >= ctxt->ContentLength) )
1468 break;
1469 }
1470
1471 *ptr = ctxt->content;
1472 *len = rcvd_lgth;
1473
1474 if ( ( ctxt->ContentLength > 0 ) && ( rcvd_lgth < ctxt->ContentLength ) )
1475 rc = -1;
1476 else if ( rcvd_lgth == 0 )
1477 rc = -1;
1478
1479 return ( rc );
1480}
1481
Owen Taylor3473f882001-02-23 17:55:21 +00001482#ifdef STANDALONE
1483int main(int argc, char **argv) {
1484 char *contentType = NULL;
1485
1486 if (argv[1] != NULL) {
1487 if (argv[2] != NULL)
1488 xmlNanoHTTPFetch(argv[1], argv[2], &contentType);
1489 else
1490 xmlNanoHTTPFetch(argv[1], "-", &contentType);
1491 if (contentType != NULL) xmlFree(contentType);
1492 } else {
1493 xmlGenericError(xmlGenericErrorContext,
1494 "%s: minimal HTTP GET implementation\n", argv[0]);
1495 xmlGenericError(xmlGenericErrorContext,
1496 "\tusage %s [ URL [ filename ] ]\n", argv[0]);
1497 }
1498 xmlNanoHTTPCleanup();
1499 xmlMemoryDump();
1500 return(0);
1501}
1502#endif /* STANDALONE */
1503#else /* !LIBXML_HTTP_ENABLED */
1504#ifdef STANDALONE
1505#include <stdio.h>
1506int main(int argc, char **argv) {
1507 xmlGenericError(xmlGenericErrorContext,
1508 "%s : HTTP support not compiled in\n", argv[0]);
1509 return(0);
1510}
1511#endif /* STANDALONE */
1512#endif /* LIBXML_HTTP_ENABLED */