blob: 614fab75896a262b7ac424b10b0bd6e2424439e0 [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>
Daniel Veillard8efff672002-12-04 11:44:48 +000079#include <libxml/uri.h>
Owen Taylor3473f882001-02-23 17:55:21 +000080
81/**
82 * A couple portability macros
83 */
84#ifndef _WINSOCKAPI_
85#define closesocket(s) close(s)
86#define SOCKET int
87#endif
88
Daniel Veillard75be0132002-03-13 10:03:35 +000089#ifndef SOCKLEN_T
90#define SOCKLEN_T unsigned int
91#endif
92#ifndef SOCKET
93#define SOCKET int
94#endif
Daniel Veillardf012a642001-07-23 19:10:52 +000095
Owen Taylor3473f882001-02-23 17:55:21 +000096#ifdef STANDALONE
97#define DEBUG_HTTP
98#define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n)
99#define xmlStrcasecmpi(a, b) strcasecmp((char *)a, (char *)b)
100#endif
101
102#define XML_NANO_HTTP_MAX_REDIR 10
103
104#define XML_NANO_HTTP_CHUNK 4096
105
106#define XML_NANO_HTTP_CLOSED 0
107#define XML_NANO_HTTP_WRITE 1
108#define XML_NANO_HTTP_READ 2
109#define XML_NANO_HTTP_NONE 4
110
111typedef struct xmlNanoHTTPCtxt {
112 char *protocol; /* the protocol name */
113 char *hostname; /* the host name */
114 int port; /* the port */
115 char *path; /* the path within the URL */
116 SOCKET fd; /* the file descriptor for the socket */
117 int state; /* WRITE / READ / CLOSED */
118 char *out; /* buffer sent (zero terminated) */
119 char *outptr; /* index within the buffer sent */
120 char *in; /* the receiving buffer */
121 char *content; /* the start of the content */
122 char *inptr; /* the next byte to read from network */
123 char *inrptr; /* the next byte to give back to the client */
124 int inlen; /* len of the input buffer */
125 int last; /* return code for last operation */
126 int returnValue; /* the protocol return value */
Daniel Veillardf012a642001-07-23 19:10:52 +0000127 int ContentLength; /* specified content length from HTTP header */
Owen Taylor3473f882001-02-23 17:55:21 +0000128 char *contentType; /* the MIME type for the input */
129 char *location; /* the new URL in case of redirect */
130 char *authHeader; /* contents of {WWW,Proxy}-Authenticate header */
131} xmlNanoHTTPCtxt, *xmlNanoHTTPCtxtPtr;
132
133static int initialized = 0;
134static char *proxy = NULL; /* the proxy name if any */
135static int proxyPort; /* the proxy port if any */
136static unsigned int timeout = 60;/* the select() timeout in seconds */
137
Daniel Veillardf012a642001-07-23 19:10:52 +0000138int xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len );
139int xmlNanoHTTPContentLength( void * ctx );
140
Owen Taylor3473f882001-02-23 17:55:21 +0000141/**
142 * A portability function
143 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000144static int socket_errno(void) {
Owen Taylor3473f882001-02-23 17:55:21 +0000145#ifdef _WINSOCKAPI_
146 return(WSAGetLastError());
147#else
148 return(errno);
149#endif
150}
151
152/**
153 * xmlNanoHTTPInit:
154 *
155 * Initialize the HTTP protocol layer.
156 * Currently it just checks for proxy informations
157 */
158
159void
160xmlNanoHTTPInit(void) {
161 const char *env;
162#ifdef _WINSOCKAPI_
163 WSADATA wsaData;
164#endif
165
166 if (initialized)
167 return;
168
169#ifdef _WINSOCKAPI_
170 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
171 return;
172#endif
173
174 if (proxy == NULL) {
175 proxyPort = 80;
176 env = getenv("no_proxy");
177 if (env != NULL)
178 goto done;
179 env = getenv("http_proxy");
180 if (env != NULL) {
181 xmlNanoHTTPScanProxy(env);
182 goto done;
183 }
184 env = getenv("HTTP_PROXY");
185 if (env != NULL) {
186 xmlNanoHTTPScanProxy(env);
187 goto done;
188 }
189 }
190done:
191 initialized = 1;
192}
193
194/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000195 * xmlNanoHTTPCleanup:
Owen Taylor3473f882001-02-23 17:55:21 +0000196 *
197 * Cleanup the HTTP protocol layer.
198 */
199
200void
201xmlNanoHTTPCleanup(void) {
202 if (proxy != NULL)
203 xmlFree(proxy);
204#ifdef _WINSOCKAPI_
205 if (initialized)
206 WSACleanup();
207#endif
208 initialized = 0;
209 return;
210}
211
212/**
Owen Taylor3473f882001-02-23 17:55:21 +0000213 * xmlNanoHTTPScanURL:
214 * @ctxt: an HTTP context
215 * @URL: The URL used to initialize the context
216 *
217 * (Re)Initialize an HTTP context by parsing the URL and finding
218 * the protocol host port and path it indicates.
219 */
220
221static void
222xmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt, const char *URL) {
223 const char *cur = URL;
224 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000225 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000226 int port = 0;
227
228 if (ctxt->protocol != NULL) {
229 xmlFree(ctxt->protocol);
230 ctxt->protocol = NULL;
231 }
232 if (ctxt->hostname != NULL) {
233 xmlFree(ctxt->hostname);
234 ctxt->hostname = NULL;
235 }
236 if (ctxt->path != NULL) {
237 xmlFree(ctxt->path);
238 ctxt->path = NULL;
239 }
240 if (URL == NULL) return;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000241 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000242 while (*cur != 0) {
243 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000244 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000245 ctxt->protocol = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000246 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000247 cur += 3;
248 break;
249 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000250 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000251 }
252 if (*cur == 0) return;
253
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000254 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000255 while (1) {
256 if (cur[0] == ':') {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000257 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000258 ctxt->hostname = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000259 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000260 cur += 1;
261 while ((*cur >= '0') && (*cur <= '9')) {
262 port *= 10;
263 port += *cur - '0';
264 cur++;
265 }
266 if (port != 0) ctxt->port = port;
267 while ((cur[0] != '/') && (*cur != 0))
268 cur++;
269 break;
270 }
271 if ((*cur == '/') || (*cur == 0)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000272 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000273 ctxt->hostname = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000274 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000275 break;
276 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000277 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000278 }
279 if (*cur == 0)
280 ctxt->path = xmlMemStrdup("/");
281 else {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000282 indx = 0;
283 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000284 while (*cur != 0)
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000285 buf[indx++] = *cur++;
286 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000287 ctxt->path = xmlMemStrdup(buf);
288 }
289}
290
291/**
292 * xmlNanoHTTPScanProxy:
293 * @URL: The proxy URL used to initialize the proxy context
294 *
295 * (Re)Initialize the HTTP Proxy context by parsing the URL and finding
296 * the protocol host port it indicates.
297 * Should be like http://myproxy/ or http://myproxy:3128/
298 * A NULL URL cleans up proxy informations.
299 */
300
301void
302xmlNanoHTTPScanProxy(const char *URL) {
303 const char *cur = URL;
304 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000305 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000306 int port = 0;
307
308 if (proxy != NULL) {
309 xmlFree(proxy);
310 proxy = NULL;
311 }
312 if (proxyPort != 0) {
313 proxyPort = 0;
314 }
315#ifdef DEBUG_HTTP
316 if (URL == NULL)
317 xmlGenericError(xmlGenericErrorContext,
318 "Removing HTTP proxy info\n");
319 else
320 xmlGenericError(xmlGenericErrorContext,
321 "Using HTTP proxy %s\n", URL);
322#endif
323 if (URL == NULL) return;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000324 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000325 while (*cur != 0) {
326 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000327 buf[indx] = 0;
328 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000329 cur += 3;
330 break;
331 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000332 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000333 }
334 if (*cur == 0) return;
335
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000336 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000337 while (1) {
338 if (cur[0] == ':') {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000339 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000340 proxy = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000341 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000342 cur += 1;
343 while ((*cur >= '0') && (*cur <= '9')) {
344 port *= 10;
345 port += *cur - '0';
346 cur++;
347 }
348 if (port != 0) proxyPort = port;
349 while ((cur[0] != '/') && (*cur != 0))
350 cur++;
351 break;
352 }
353 if ((*cur == '/') || (*cur == 0)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000354 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000355 proxy = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000356 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000357 break;
358 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000359 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000360 }
361}
362
363/**
364 * xmlNanoHTTPNewCtxt:
365 * @URL: The URL used to initialize the context
366 *
367 * Allocate and initialize a new HTTP context.
368 *
369 * Returns an HTTP context or NULL in case of error.
370 */
371
372static xmlNanoHTTPCtxtPtr
373xmlNanoHTTPNewCtxt(const char *URL) {
374 xmlNanoHTTPCtxtPtr ret;
Daniel Veillard8efff672002-12-04 11:44:48 +0000375 xmlChar *escaped;
Owen Taylor3473f882001-02-23 17:55:21 +0000376
377 ret = (xmlNanoHTTPCtxtPtr) xmlMalloc(sizeof(xmlNanoHTTPCtxt));
378 if (ret == NULL) return(NULL);
379
380 memset(ret, 0, sizeof(xmlNanoHTTPCtxt));
381 ret->port = 80;
382 ret->returnValue = 0;
383 ret->fd = -1;
Daniel Veillardf012a642001-07-23 19:10:52 +0000384 ret->ContentLength = -1;
Owen Taylor3473f882001-02-23 17:55:21 +0000385
Daniel Veillard8efff672002-12-04 11:44:48 +0000386 escaped = xmlURIEscapeStr(BAD_CAST URL, BAD_CAST"@/:=?;#%&");
387 if (escaped != NULL) {
388 xmlNanoHTTPScanURL(ret, (const char *) escaped);
389 xmlFree(escaped);
390 } else {
391 xmlNanoHTTPScanURL(ret, URL);
392 }
Owen Taylor3473f882001-02-23 17:55:21 +0000393
394 return(ret);
395}
396
397/**
398 * xmlNanoHTTPFreeCtxt:
399 * @ctxt: an HTTP context
400 *
401 * Frees the context after closing the connection.
402 */
403
404static void
405xmlNanoHTTPFreeCtxt(xmlNanoHTTPCtxtPtr ctxt) {
406 if (ctxt == NULL) return;
407 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
408 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
409 if (ctxt->path != NULL) xmlFree(ctxt->path);
410 if (ctxt->out != NULL) xmlFree(ctxt->out);
411 if (ctxt->in != NULL) xmlFree(ctxt->in);
412 if (ctxt->contentType != NULL) xmlFree(ctxt->contentType);
413 if (ctxt->location != NULL) xmlFree(ctxt->location);
414 if (ctxt->authHeader != NULL) xmlFree(ctxt->authHeader);
415 ctxt->state = XML_NANO_HTTP_NONE;
416 if (ctxt->fd >= 0) closesocket(ctxt->fd);
417 ctxt->fd = -1;
418 xmlFree(ctxt);
419}
420
421/**
422 * xmlNanoHTTPSend:
423 * @ctxt: an HTTP context
424 *
425 * Send the input needed to initiate the processing on the server side
Daniel Veillardf012a642001-07-23 19:10:52 +0000426 * Returns number of bytes sent or -1 on error.
Owen Taylor3473f882001-02-23 17:55:21 +0000427 */
428
Daniel Veillardf012a642001-07-23 19:10:52 +0000429static int
430xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt, const char * xmt_ptr, int outlen) {
431
432 int total_sent = 0;
433
434 if ( (ctxt->state & XML_NANO_HTTP_WRITE) && (xmt_ptr != NULL ) ) {
435 while (total_sent < outlen) {
436 int nsent = send(ctxt->fd, xmt_ptr + total_sent,
437 outlen - total_sent, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000438 if (nsent>0)
439 total_sent += nsent;
Daniel Veillardf012a642001-07-23 19:10:52 +0000440 else if ( ( nsent == -1 ) &&
Daniel Veillardba6db032001-07-31 16:25:45 +0000441#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
Daniel Veillardf012a642001-07-23 19:10:52 +0000442 ( socket_errno( ) != EAGAIN ) &&
Daniel Veillardba6db032001-07-31 16:25:45 +0000443#endif
Daniel Veillardf012a642001-07-23 19:10:52 +0000444 ( socket_errno( ) != EWOULDBLOCK ) ) {
445 xmlGenericError( xmlGenericErrorContext,
446 "xmlNanoHTTPSend error: %s",
447 strerror( socket_errno( ) ) );
448
449 if ( total_sent == 0 )
450 total_sent = -1;
451 break;
452 }
453 else {
454 /*
455 ** No data sent
456 ** Since non-blocking sockets are used, wait for
457 ** socket to be writable or default timeout prior
458 ** to retrying.
459 */
460
461 struct timeval tv;
462 fd_set wfd;
463
464 tv.tv_sec = timeout;
465 tv.tv_usec = 0;
466 FD_ZERO( &wfd );
467 FD_SET( ctxt->fd, &wfd );
468 (void)select( ctxt->fd + 1, NULL, &wfd, NULL, &tv );
469 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000470 }
Owen Taylor3473f882001-02-23 17:55:21 +0000471 }
Daniel Veillardf012a642001-07-23 19:10:52 +0000472
473 return total_sent;
Owen Taylor3473f882001-02-23 17:55:21 +0000474}
475
476/**
477 * xmlNanoHTTPRecv:
478 * @ctxt: an HTTP context
479 *
480 * Read information coming from the HTTP connection.
481 * This is a blocking call (but it blocks in select(), not read()).
482 *
483 * Returns the number of byte read or -1 in case of error.
484 */
485
486static int
487xmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt) {
488 fd_set rfd;
489 struct timeval tv;
490
491
492 while (ctxt->state & XML_NANO_HTTP_READ) {
493 if (ctxt->in == NULL) {
494 ctxt->in = (char *) xmlMalloc(65000 * sizeof(char));
495 if (ctxt->in == NULL) {
496 ctxt->last = -1;
Daniel Veillardf012a642001-07-23 19:10:52 +0000497 xmlGenericError( xmlGenericErrorContext,
498 "xmlNanoHTTPRecv: Error allocating input memory." );
Owen Taylor3473f882001-02-23 17:55:21 +0000499 return(-1);
500 }
501 ctxt->inlen = 65000;
502 ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in;
503 }
504 if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) {
505 int delta = ctxt->inrptr - ctxt->in;
506 int len = ctxt->inptr - ctxt->inrptr;
507
508 memmove(ctxt->in, ctxt->inrptr, len);
509 ctxt->inrptr -= delta;
510 ctxt->content -= delta;
511 ctxt->inptr -= delta;
512 }
513 if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) {
514 int d_inptr = ctxt->inptr - ctxt->in;
515 int d_content = ctxt->content - ctxt->in;
516 int d_inrptr = ctxt->inrptr - ctxt->in;
Daniel Veillardf012a642001-07-23 19:10:52 +0000517 char * tmp_ptr = ctxt->in;
Owen Taylor3473f882001-02-23 17:55:21 +0000518
519 ctxt->inlen *= 2;
Daniel Veillardf012a642001-07-23 19:10:52 +0000520 ctxt->in = (char *) xmlRealloc(tmp_ptr, ctxt->inlen);
Owen Taylor3473f882001-02-23 17:55:21 +0000521 if (ctxt->in == NULL) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000522 xmlGenericError( xmlGenericErrorContext,
523 "xmlNanoHTTPRecv: %s %d bytes.",
524 "Failed to realloc input buffer to",
525 ctxt->inlen );
526 xmlFree( tmp_ptr );
Owen Taylor3473f882001-02-23 17:55:21 +0000527 ctxt->last = -1;
528 return(-1);
529 }
530 ctxt->inptr = ctxt->in + d_inptr;
531 ctxt->content = ctxt->in + d_content;
532 ctxt->inrptr = ctxt->in + d_inrptr;
533 }
534 ctxt->last = recv(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK, 0);
535 if (ctxt->last > 0) {
536 ctxt->inptr += ctxt->last;
537 return(ctxt->last);
538 }
539 if (ctxt->last == 0) {
540 return(0);
541 }
542 if (ctxt->last == -1) {
543 switch (socket_errno()) {
544 case EINPROGRESS:
545 case EWOULDBLOCK:
546#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
547 case EAGAIN:
548#endif
549 break;
Daniel Veillardf012a642001-07-23 19:10:52 +0000550
551 case ECONNRESET:
552 case ESHUTDOWN:
553 return ( 0 );
554
Owen Taylor3473f882001-02-23 17:55:21 +0000555 default:
Daniel Veillardf012a642001-07-23 19:10:52 +0000556 xmlGenericError( xmlGenericErrorContext,
557 "xmlNanoHTTPRecv: recv( ) failure - %s",
558 strerror( socket_errno( ) ) );
559 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +0000560 }
561 }
562
563 tv.tv_sec = timeout;
564 tv.tv_usec = 0;
565 FD_ZERO(&rfd);
566 FD_SET(ctxt->fd, &rfd);
567
Daniel Veillard50f34372001-08-03 12:06:36 +0000568 if ( (select(ctxt->fd+1, &rfd, NULL, NULL, &tv)<1)
569#if defined(EINTR)
570 && (errno != EINTR)
571#endif
572 )
Owen Taylor3473f882001-02-23 17:55:21 +0000573 return(0);
574 }
575 return(0);
576}
577
578/**
579 * xmlNanoHTTPReadLine:
580 * @ctxt: an HTTP context
581 *
582 * Read one line in the HTTP server output, usually for extracting
583 * the HTTP protocol informations from the answer header.
584 *
585 * Returns a newly allocated string with a copy of the line, or NULL
586 * which indicate the end of the input.
587 */
588
589static char *
590xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt) {
591 char buf[4096];
592 char *bp = buf;
Daniel Veillardf012a642001-07-23 19:10:52 +0000593 int rc;
Owen Taylor3473f882001-02-23 17:55:21 +0000594
595 while (bp - buf < 4095) {
596 if (ctxt->inrptr == ctxt->inptr) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000597 if ( (rc = xmlNanoHTTPRecv(ctxt)) == 0) {
Owen Taylor3473f882001-02-23 17:55:21 +0000598 if (bp == buf)
599 return(NULL);
600 else
601 *bp = 0;
602 return(xmlMemStrdup(buf));
603 }
Daniel Veillardf012a642001-07-23 19:10:52 +0000604 else if ( rc == -1 ) {
605 return ( NULL );
606 }
Owen Taylor3473f882001-02-23 17:55:21 +0000607 }
608 *bp = *ctxt->inrptr++;
609 if (*bp == '\n') {
610 *bp = 0;
611 return(xmlMemStrdup(buf));
612 }
613 if (*bp != '\r')
614 bp++;
615 }
616 buf[4095] = 0;
617 return(xmlMemStrdup(buf));
618}
619
620
621/**
622 * xmlNanoHTTPScanAnswer:
623 * @ctxt: an HTTP context
624 * @line: an HTTP header line
625 *
626 * Try to extract useful informations from the server answer.
627 * We currently parse and process:
628 * - The HTTP revision/ return code
629 * - The Content-Type
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000630 * - The Location for redirect processing.
Owen Taylor3473f882001-02-23 17:55:21 +0000631 *
632 * Returns -1 in case of failure, the file descriptor number otherwise
633 */
634
635static void
636xmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt, const char *line) {
637 const char *cur = line;
638
639 if (line == NULL) return;
640
641 if (!strncmp(line, "HTTP/", 5)) {
642 int version = 0;
643 int ret = 0;
644
645 cur += 5;
646 while ((*cur >= '0') && (*cur <= '9')) {
647 version *= 10;
648 version += *cur - '0';
649 cur++;
650 }
651 if (*cur == '.') {
652 cur++;
653 if ((*cur >= '0') && (*cur <= '9')) {
654 version *= 10;
655 version += *cur - '0';
656 cur++;
657 }
658 while ((*cur >= '0') && (*cur <= '9'))
659 cur++;
660 } else
661 version *= 10;
662 if ((*cur != ' ') && (*cur != '\t')) return;
663 while ((*cur == ' ') || (*cur == '\t')) cur++;
664 if ((*cur < '0') || (*cur > '9')) return;
665 while ((*cur >= '0') && (*cur <= '9')) {
666 ret *= 10;
667 ret += *cur - '0';
668 cur++;
669 }
670 if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return;
671 ctxt->returnValue = ret;
672 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Content-Type:", 13)) {
673 cur += 13;
674 while ((*cur == ' ') || (*cur == '\t')) cur++;
675 if (ctxt->contentType != NULL)
676 xmlFree(ctxt->contentType);
677 ctxt->contentType = xmlMemStrdup(cur);
678 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"ContentType:", 12)) {
679 cur += 12;
680 if (ctxt->contentType != NULL) return;
681 while ((*cur == ' ') || (*cur == '\t')) cur++;
682 ctxt->contentType = xmlMemStrdup(cur);
683 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Location:", 9)) {
684 cur += 9;
685 while ((*cur == ' ') || (*cur == '\t')) cur++;
686 if (ctxt->location != NULL)
687 xmlFree(ctxt->location);
688 ctxt->location = xmlMemStrdup(cur);
689 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"WWW-Authenticate:", 17)) {
690 cur += 17;
691 while ((*cur == ' ') || (*cur == '\t')) cur++;
692 if (ctxt->authHeader != NULL)
693 xmlFree(ctxt->authHeader);
694 ctxt->authHeader = xmlMemStrdup(cur);
695 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Proxy-Authenticate:", 19)) {
696 cur += 19;
697 while ((*cur == ' ') || (*cur == '\t')) cur++;
698 if (ctxt->authHeader != NULL)
699 xmlFree(ctxt->authHeader);
700 ctxt->authHeader = xmlMemStrdup(cur);
Daniel Veillardf012a642001-07-23 19:10:52 +0000701 } else if ( !xmlStrncasecmp( BAD_CAST line, BAD_CAST"Content-Length:", 15) ) {
702 cur += 15;
703 ctxt->ContentLength = strtol( cur, NULL, 10 );
Owen Taylor3473f882001-02-23 17:55:21 +0000704 }
705}
706
707/**
708 * xmlNanoHTTPConnectAttempt:
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000709 * @addr: a socket address structure
Owen Taylor3473f882001-02-23 17:55:21 +0000710 *
711 * Attempt a connection to the given IP:port endpoint. It forces
712 * non-blocking semantic on the socket, and allow 60 seconds for
713 * the host to answer.
714 *
715 * Returns -1 in case of failure, the file descriptor number otherwise
716 */
717
718static int
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000719xmlNanoHTTPConnectAttempt(struct sockaddr *addr)
Owen Taylor3473f882001-02-23 17:55:21 +0000720{
721 SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
722 fd_set wfd;
723 struct timeval tv;
724 int status;
725
726 if (s==-1) {
727#ifdef DEBUG_HTTP
728 perror("socket");
729#endif
Daniel Veillardf012a642001-07-23 19:10:52 +0000730 xmlGenericError( xmlGenericErrorContext,
731 "xmlNanoHTTPConnectAttempt: %s - %s",
732 "socket creation failure",
733 strerror( socket_errno( ) ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000734 return(-1);
735 }
736
737#ifdef _WINSOCKAPI_
738 {
739 u_long one = 1;
740
741 status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0;
742 }
743#else /* _WINSOCKAPI_ */
744#if defined(VMS)
745 {
746 int enable = 1;
747 status = ioctl(s, FIONBIO, &enable);
748 }
749#else /* VMS */
750 if ((status = fcntl(s, F_GETFL, 0)) != -1) {
751#ifdef O_NONBLOCK
752 status |= O_NONBLOCK;
753#else /* O_NONBLOCK */
754#ifdef F_NDELAY
755 status |= F_NDELAY;
756#endif /* F_NDELAY */
757#endif /* !O_NONBLOCK */
758 status = fcntl(s, F_SETFL, status);
759 }
760 if (status < 0) {
761#ifdef DEBUG_HTTP
762 perror("nonblocking");
763#endif
Daniel Veillardf012a642001-07-23 19:10:52 +0000764 xmlGenericError( xmlGenericErrorContext,
765 "xmlNanoHTTPConnectAttempt: %s - %s",
766 "error setting non-blocking IO",
767 strerror( socket_errno( ) ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000768 closesocket(s);
769 return(-1);
770 }
771#endif /* !VMS */
772#endif /* !_WINSOCKAPI_ */
773
Owen Taylor3473f882001-02-23 17:55:21 +0000774 if ((connect(s, addr, sizeof(*addr))==-1)) {
775 switch (socket_errno()) {
776 case EINPROGRESS:
777 case EWOULDBLOCK:
778 break;
779 default:
Daniel Veillardf012a642001-07-23 19:10:52 +0000780 xmlGenericError( xmlGenericErrorContext,
781 "xmlNanoHTTPConnectAttempt: %s - %s",
782 "error connecting to HTTP server",
783 strerror( socket_errno( ) ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000784 closesocket(s);
785 return(-1);
786 }
787 }
788
789 tv.tv_sec = timeout;
790 tv.tv_usec = 0;
791
792 FD_ZERO(&wfd);
793 FD_SET(s, &wfd);
794
795 switch(select(s+1, NULL, &wfd, NULL, &tv))
796 {
797 case 0:
798 /* Time out */
Daniel Veillardf012a642001-07-23 19:10:52 +0000799 xmlGenericError( xmlGenericErrorContext,
800 "xmlNanoHTTPConnectAttempt: %s",
801 "Connect attempt timed out." );
Owen Taylor3473f882001-02-23 17:55:21 +0000802 closesocket(s);
803 return(-1);
804 case -1:
805 /* Ermm.. ?? */
Daniel Veillardf012a642001-07-23 19:10:52 +0000806 xmlGenericError( xmlGenericErrorContext,
807 "xmlNanoHTTPConnectAttempt: %s - %s",
808 "Error connecting to host",
809 strerror( socket_errno( ) ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000810 closesocket(s);
811 return(-1);
812 }
813
814 if ( FD_ISSET(s, &wfd) ) {
815 SOCKLEN_T len;
816 len = sizeof(status);
817 if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&status, &len) < 0 ) {
818 /* Solaris error code */
Daniel Veillardf012a642001-07-23 19:10:52 +0000819 xmlGenericError( xmlGenericErrorContext,
820 "xmlNanoHTTPConnectAttempt: %s - %s",
821 "Error retrieving pending socket errors",
822 strerror( socket_errno( ) ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000823 return (-1);
824 }
825 if ( status ) {
826 closesocket(s);
827 errno = status;
Daniel Veillardf012a642001-07-23 19:10:52 +0000828 xmlGenericError( xmlGenericErrorContext,
829 "xmlNanoHTTPConnectAttempt: %s - %s",
830 "Error connecting to remote host",
831 strerror( status ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000832 return (-1);
833 }
834 } else {
835 /* pbm */
Daniel Veillardf012a642001-07-23 19:10:52 +0000836 xmlGenericError( xmlGenericErrorContext,
837 "xmlNanoHTTPConnectAttempt: %s\n",
838 "Select returned, but descriptor not set for connection.\n" );
839 closesocket(s);
Owen Taylor3473f882001-02-23 17:55:21 +0000840 return (-1);
841 }
842
843 return(s);
844}
845
846/**
847 * xmlNanoHTTPConnectHost:
848 * @host: the host name
849 * @port: the port number
850 *
851 * Attempt a connection to the given host:port endpoint. It tries
852 * the multiple IP provided by the DNS if available.
853 *
854 * Returns -1 in case of failure, the file descriptor number otherwise
855 */
856
857static int
858xmlNanoHTTPConnectHost(const char *host, int port)
859{
860 struct hostent *h;
861 struct sockaddr *addr;
862 struct in_addr ia;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000863 struct sockaddr_in sockin;
Daniel Veillard5c396542002-03-15 07:57:50 +0000864
Owen Taylor3473f882001-02-23 17:55:21 +0000865#ifdef SUPPORT_IP6
866 struct in6_addr ia6;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000867 struct sockaddr_in6 sockin6;
Owen Taylor3473f882001-02-23 17:55:21 +0000868#endif
869 int i;
870 int s;
Daniel Veillard5c396542002-03-15 07:57:50 +0000871
Owen Taylor3473f882001-02-23 17:55:21 +0000872#if defined(SUPPORT_IP6) && defined(RES_USE_INET6)
873 if (!(_res.options & RES_INIT))
Daniel Veillard5c396542002-03-15 07:57:50 +0000874 res_init();
Owen Taylor3473f882001-02-23 17:55:21 +0000875 _res.options |= RES_USE_INET6;
876#endif
Daniel Veillard5c396542002-03-15 07:57:50 +0000877 h = gethostbyname(host);
878 if (h == NULL) {
Daniel Veillard56b2db72002-03-25 16:35:28 +0000879
880/*
881 * Okay, I got fed up by the non-portability of this error message
882 * extraction code. it work on Linux, if it work on your platform
883 * and one want to enable it, send me the defined(foobar) needed
884 */
885#if defined(HAVE_NETDB_H) && defined(HOST_NOT_FOUND) && defined(linux)
Daniel Veillard5c396542002-03-15 07:57:50 +0000886 const char *h_err_txt = "";
Daniel Veillardf012a642001-07-23 19:10:52 +0000887
Daniel Veillard5c396542002-03-15 07:57:50 +0000888 switch (h_errno) {
889 case HOST_NOT_FOUND:
890 h_err_txt = "Authoritive host not found";
891 break;
Daniel Veillardf012a642001-07-23 19:10:52 +0000892
Daniel Veillard5c396542002-03-15 07:57:50 +0000893 case TRY_AGAIN:
894 h_err_txt =
895 "Non-authoritive host not found or server failure.";
896 break;
Daniel Veillardf012a642001-07-23 19:10:52 +0000897
Daniel Veillard5c396542002-03-15 07:57:50 +0000898 case NO_RECOVERY:
899 h_err_txt =
900 "Non-recoverable errors: FORMERR, REFUSED, or NOTIMP.";
901 break;
902
903 case NO_ADDRESS:
904 h_err_txt =
905 "Valid name, no data record of requested type.";
906 break;
907
908 default:
909 h_err_txt = "No error text defined.";
910 break;
911 }
912 xmlGenericError(xmlGenericErrorContext,
913 "xmlNanoHTTPConnectHost: %s '%s' - %s",
914 "Failed to resolve host", host, h_err_txt);
915#else
916 xmlGenericError(xmlGenericErrorContext,
917 "xmlNanoHTTPConnectHost: %s '%s'",
918 "Failed to resolve host", host);
Owen Taylor3473f882001-02-23 17:55:21 +0000919#endif
Daniel Veillard5c396542002-03-15 07:57:50 +0000920 return (-1);
921 }
922
923 for (i = 0; h->h_addr_list[i]; i++) {
924 if (h->h_addrtype == AF_INET) {
925 /* A records (IPv4) */
926 memcpy(&ia, h->h_addr_list[i], h->h_length);
927 sockin.sin_family = h->h_addrtype;
928 sockin.sin_addr = ia;
929 sockin.sin_port = htons(port);
930 addr = (struct sockaddr *) &sockin;
931#ifdef SUPPORT_IP6
932 } else if (h->h_addrtype == AF_INET6) {
933 /* AAAA records (IPv6) */
934 memcpy(&ia6, h->h_addr_list[i], h->h_length);
935 sockin6.sin_family = h->h_addrtype;
936 sockin6.sin_addr = ia6;
937 sockin6.sin_port = htons(port);
938 addr = (struct sockaddr *) &sockin6;
939#endif
940 } else
941 break; /* for */
942
943 s = xmlNanoHTTPConnectAttempt(addr);
944 if (s != -1)
945 return (s);
Owen Taylor3473f882001-02-23 17:55:21 +0000946 }
947
948#ifdef DEBUG_HTTP
949 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard5c396542002-03-15 07:57:50 +0000950 "xmlNanoHTTPConnectHost: unable to connect to '%s'.\n",
951 host);
Owen Taylor3473f882001-02-23 17:55:21 +0000952#endif
Daniel Veillard5c396542002-03-15 07:57:50 +0000953 return (-1);
Owen Taylor3473f882001-02-23 17:55:21 +0000954}
955
956
957/**
958 * xmlNanoHTTPOpen:
959 * @URL: The URL to load
960 * @contentType: if available the Content-Type information will be
961 * returned at that location
962 *
963 * This function try to open a connection to the indicated resource
964 * via HTTP GET.
965 *
966 * Returns NULL in case of failure, otherwise a request handler.
967 * The contentType, if provided must be freed by the caller
968 */
969
970void*
971xmlNanoHTTPOpen(const char *URL, char **contentType) {
972 if (contentType != NULL) *contentType = NULL;
Daniel Veillardf012a642001-07-23 19:10:52 +0000973 return(xmlNanoHTTPMethod(URL, NULL, NULL, contentType, NULL, 0));
Daniel Veillard9403a042001-05-28 11:00:53 +0000974}
975
976/**
977 * xmlNanoHTTPOpenRedir:
978 * @URL: The URL to load
979 * @contentType: if available the Content-Type information will be
980 * returned at that location
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000981 * @redir: if available the redirected URL will be returned
Daniel Veillard9403a042001-05-28 11:00:53 +0000982 *
983 * This function try to open a connection to the indicated resource
984 * via HTTP GET.
985 *
986 * Returns NULL in case of failure, otherwise a request handler.
987 * The contentType, if provided must be freed by the caller
988 */
989
990void*
991xmlNanoHTTPOpenRedir(const char *URL, char **contentType, char **redir) {
992 if (contentType != NULL) *contentType = NULL;
993 if (redir != NULL) *redir = NULL;
Daniel Veillardf012a642001-07-23 19:10:52 +0000994 return(xmlNanoHTTPMethodRedir(URL, NULL, NULL, contentType, redir, NULL,0));
Owen Taylor3473f882001-02-23 17:55:21 +0000995}
996
997/**
998 * xmlNanoHTTPRead:
999 * @ctx: the HTTP context
1000 * @dest: a buffer
1001 * @len: the buffer length
1002 *
1003 * This function tries to read @len bytes from the existing HTTP connection
1004 * and saves them in @dest. This is a blocking call.
1005 *
1006 * Returns the number of byte read. 0 is an indication of an end of connection.
1007 * -1 indicates a parameter error.
1008 */
1009int
1010xmlNanoHTTPRead(void *ctx, void *dest, int len) {
1011 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1012
1013 if (ctx == NULL) return(-1);
1014 if (dest == NULL) return(-1);
1015 if (len <= 0) return(0);
1016
1017 while (ctxt->inptr - ctxt->inrptr < len) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001018 if (xmlNanoHTTPRecv(ctxt) <= 0) break;
Owen Taylor3473f882001-02-23 17:55:21 +00001019 }
1020 if (ctxt->inptr - ctxt->inrptr < len)
1021 len = ctxt->inptr - ctxt->inrptr;
1022 memcpy(dest, ctxt->inrptr, len);
1023 ctxt->inrptr += len;
1024 return(len);
1025}
1026
1027/**
1028 * xmlNanoHTTPClose:
1029 * @ctx: the HTTP context
1030 *
1031 * This function closes an HTTP context, it ends up the connection and
1032 * free all data related to it.
1033 */
1034void
1035xmlNanoHTTPClose(void *ctx) {
1036 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1037
1038 if (ctx == NULL) return;
1039
1040 xmlNanoHTTPFreeCtxt(ctxt);
1041}
1042
1043/**
Daniel Veillard9403a042001-05-28 11:00:53 +00001044 * xmlNanoHTTPMethodRedir:
Owen Taylor3473f882001-02-23 17:55:21 +00001045 * @URL: The URL to load
1046 * @method: the HTTP method to use
1047 * @input: the input string if any
1048 * @contentType: the Content-Type information IN and OUT
Daniel Veillard9403a042001-05-28 11:00:53 +00001049 * @redir: the redirected URL OUT
Owen Taylor3473f882001-02-23 17:55:21 +00001050 * @headers: the extra headers
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001051 * @ilen: input length
Owen Taylor3473f882001-02-23 17:55:21 +00001052 *
1053 * This function try to open a connection to the indicated resource
1054 * via HTTP using the given @method, adding the given extra headers
1055 * and the input buffer for the request content.
1056 *
1057 * Returns NULL in case of failure, otherwise a request handler.
Daniel Veillard9403a042001-05-28 11:00:53 +00001058 * The contentType, or redir, if provided must be freed by the caller
Owen Taylor3473f882001-02-23 17:55:21 +00001059 */
1060
1061void*
Daniel Veillard9403a042001-05-28 11:00:53 +00001062xmlNanoHTTPMethodRedir(const char *URL, const char *method, const char *input,
Daniel Veillardf012a642001-07-23 19:10:52 +00001063 char **contentType, char **redir,
1064 const char *headers, int ilen ) {
Owen Taylor3473f882001-02-23 17:55:21 +00001065 xmlNanoHTTPCtxtPtr ctxt;
1066 char *bp, *p;
Daniel Veillardf012a642001-07-23 19:10:52 +00001067 int blen, ret;
Owen Taylor3473f882001-02-23 17:55:21 +00001068 int head;
Daniel Veillardf012a642001-07-23 19:10:52 +00001069 int xmt_bytes;
Owen Taylor3473f882001-02-23 17:55:21 +00001070 int nbRedirects = 0;
1071 char *redirURL = NULL;
1072
1073 if (URL == NULL) return(NULL);
1074 if (method == NULL) method = "GET";
1075 xmlNanoHTTPInit();
1076
1077retry:
1078 if (redirURL == NULL)
1079 ctxt = xmlNanoHTTPNewCtxt(URL);
1080 else {
1081 ctxt = xmlNanoHTTPNewCtxt(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001082 }
1083
Daniel Veillardf012a642001-07-23 19:10:52 +00001084 if ( ctxt == NULL ) {
1085 xmlGenericError( xmlGenericErrorContext,
1086 "xmlNanoHTTPMethodRedir: %s %s.",
1087 "Unable to allocate HTTP context to URI",
1088 ( ( redirURL == NULL ) ? URL : redirURL ) );
1089 return ( NULL );
1090 }
1091
Owen Taylor3473f882001-02-23 17:55:21 +00001092 if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001093 xmlGenericError( xmlGenericErrorContext,
1094 "xmlNanoHTTPMethodRedir: %s - %s.",
1095 "Not a valid HTTP URI",
1096 ( ( redirURL == NULL ) ? URL : redirURL ) );
Owen Taylor3473f882001-02-23 17:55:21 +00001097 xmlNanoHTTPFreeCtxt(ctxt);
1098 if (redirURL != NULL) xmlFree(redirURL);
1099 return(NULL);
1100 }
1101 if (ctxt->hostname == NULL) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001102 xmlGenericError( xmlGenericErrorContext,
1103 "xmlNanoHTTPMethodRedir: %s - %s",
1104 "Failed to identify host in URI",
1105 ( ( redirURL == NULL ) ? URL : redirURL ) );
Owen Taylor3473f882001-02-23 17:55:21 +00001106 xmlNanoHTTPFreeCtxt(ctxt);
Daniel Veillard9403a042001-05-28 11:00:53 +00001107 if (redirURL != NULL) xmlFree(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001108 return(NULL);
1109 }
1110 if (proxy) {
1111 blen = strlen(ctxt->hostname) * 2 + 16;
1112 ret = xmlNanoHTTPConnectHost(proxy, proxyPort);
1113 }
1114 else {
1115 blen = strlen(ctxt->hostname);
1116 ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
1117 }
1118 if (ret < 0) {
1119 xmlNanoHTTPFreeCtxt(ctxt);
Daniel Veillard9403a042001-05-28 11:00:53 +00001120 if (redirURL != NULL) xmlFree(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001121 return(NULL);
1122 }
1123 ctxt->fd = ret;
1124
Daniel Veillardf012a642001-07-23 19:10:52 +00001125 if (input == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001126 ilen = 0;
Daniel Veillardf012a642001-07-23 19:10:52 +00001127 else
1128 blen += 36;
1129
Owen Taylor3473f882001-02-23 17:55:21 +00001130 if (headers != NULL)
Daniel Veillardf012a642001-07-23 19:10:52 +00001131 blen += strlen(headers) + 2;
Owen Taylor3473f882001-02-23 17:55:21 +00001132 if (contentType && *contentType)
1133 blen += strlen(*contentType) + 16;
Daniel Veillardf012a642001-07-23 19:10:52 +00001134 blen += strlen(method) + strlen(ctxt->path) + 24;
Owen Taylor3473f882001-02-23 17:55:21 +00001135 bp = xmlMalloc(blen);
Daniel Veillardf012a642001-07-23 19:10:52 +00001136 if ( bp == NULL ) {
1137 xmlNanoHTTPFreeCtxt( ctxt );
1138 xmlGenericError( xmlGenericErrorContext,
1139 "xmlNanoHTTPMethodRedir: %s",
1140 "Error allocating HTTP header buffer." );
1141 return ( NULL );
1142 }
1143
1144 p = bp;
1145
Owen Taylor3473f882001-02-23 17:55:21 +00001146 if (proxy) {
1147 if (ctxt->port != 80) {
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001148 p += snprintf( p, blen - (p - bp), "%s http://%s:%d%s",
1149 method, ctxt->hostname,
Daniel Veillardf012a642001-07-23 19:10:52 +00001150 ctxt->port, ctxt->path );
Owen Taylor3473f882001-02-23 17:55:21 +00001151 }
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001152 else
1153 p += snprintf( p, blen - (p - bp), "%s http://%s%s", method,
Daniel Veillardf012a642001-07-23 19:10:52 +00001154 ctxt->hostname, ctxt->path);
Owen Taylor3473f882001-02-23 17:55:21 +00001155 }
1156 else
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001157 p += snprintf( p, blen - (p - bp), "%s %s", method, ctxt->path);
Daniel Veillardf012a642001-07-23 19:10:52 +00001158
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001159 p += snprintf( p, blen - (p - bp), " HTTP/1.0\r\nHost: %s\r\n",
1160 ctxt->hostname);
Daniel Veillardf012a642001-07-23 19:10:52 +00001161
1162 if (contentType != NULL && *contentType)
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001163 p += snprintf(p, blen - (p - bp), "Content-Type: %s\r\n", *contentType);
Daniel Veillardf012a642001-07-23 19:10:52 +00001164
1165 if (headers != NULL)
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001166 p += snprintf( p, blen - (p - bp), "%s", headers );
Daniel Veillardf012a642001-07-23 19:10:52 +00001167
Owen Taylor3473f882001-02-23 17:55:21 +00001168 if (input != NULL)
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001169 snprintf(p, blen - (p - bp), "Content-Length: %d\r\n\r\n", ilen );
Owen Taylor3473f882001-02-23 17:55:21 +00001170 else
Aleksey Sanin49cc9752002-06-14 17:07:10 +00001171 snprintf(p, blen - (p - bp), "\r\n");
Daniel Veillardf012a642001-07-23 19:10:52 +00001172
Owen Taylor3473f882001-02-23 17:55:21 +00001173#ifdef DEBUG_HTTP
1174 xmlGenericError(xmlGenericErrorContext,
1175 "-> %s%s", proxy? "(Proxy) " : "", bp);
1176 if ((blen -= strlen(bp)+1) < 0)
1177 xmlGenericError(xmlGenericErrorContext,
1178 "ERROR: overflowed buffer by %d bytes\n", -blen);
1179#endif
1180 ctxt->outptr = ctxt->out = bp;
1181 ctxt->state = XML_NANO_HTTP_WRITE;
Daniel Veillardf012a642001-07-23 19:10:52 +00001182 blen = strlen( ctxt->out );
1183 xmt_bytes = xmlNanoHTTPSend(ctxt, ctxt->out, blen );
1184#ifdef DEBUG_HTTP
1185 if ( xmt_bytes != blen )
1186 xmlGenericError( xmlGenericErrorContext,
1187 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
1188 xmt_bytes, blen,
1189 "bytes of HTTP headers sent to host",
1190 ctxt->hostname );
1191#endif
1192
1193 if ( input != NULL ) {
1194 xmt_bytes = xmlNanoHTTPSend( ctxt, input, ilen );
1195
1196#ifdef DEBUG_HTTP
1197 if ( xmt_bytes != ilen )
1198 xmlGenericError( xmlGenericErrorContext,
1199 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
1200 xmt_bytes, ilen,
1201 "bytes of HTTP content sent to host",
1202 ctxt->hostname );
1203#endif
1204 }
1205
Owen Taylor3473f882001-02-23 17:55:21 +00001206 ctxt->state = XML_NANO_HTTP_READ;
1207 head = 1;
1208
1209 while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
1210 if (head && (*p == 0)) {
1211 head = 0;
1212 ctxt->content = ctxt->inrptr;
1213 xmlFree(p);
1214 break;
1215 }
1216 xmlNanoHTTPScanAnswer(ctxt, p);
1217
1218#ifdef DEBUG_HTTP
1219 xmlGenericError(xmlGenericErrorContext, "<- %s\n", p);
1220#endif
1221 xmlFree(p);
1222 }
1223
1224 if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
1225 (ctxt->returnValue < 400)) {
1226#ifdef DEBUG_HTTP
1227 xmlGenericError(xmlGenericErrorContext,
1228 "\nRedirect to: %s\n", ctxt->location);
1229#endif
Daniel Veillardf012a642001-07-23 19:10:52 +00001230 while ( xmlNanoHTTPRecv(ctxt) > 0 ) ;
Owen Taylor3473f882001-02-23 17:55:21 +00001231 if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
1232 nbRedirects++;
Daniel Veillard9403a042001-05-28 11:00:53 +00001233 if (redirURL != NULL)
1234 xmlFree(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001235 redirURL = xmlMemStrdup(ctxt->location);
1236 xmlNanoHTTPFreeCtxt(ctxt);
1237 goto retry;
1238 }
1239 xmlNanoHTTPFreeCtxt(ctxt);
Daniel Veillard9403a042001-05-28 11:00:53 +00001240 if (redirURL != NULL) xmlFree(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001241#ifdef DEBUG_HTTP
1242 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf012a642001-07-23 19:10:52 +00001243 "xmlNanoHTTPMethodRedir: Too many redirects, aborting ...\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001244#endif
1245 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001246 }
1247
1248 if (contentType != NULL) {
1249 if (ctxt->contentType != NULL)
1250 *contentType = xmlMemStrdup(ctxt->contentType);
1251 else
1252 *contentType = NULL;
1253 }
1254
Daniel Veillard9403a042001-05-28 11:00:53 +00001255 if ((redir != NULL) && (redirURL != NULL)) {
1256 *redir = redirURL;
1257 } else {
1258 if (redirURL != NULL)
1259 xmlFree(redirURL);
1260 if (redir != NULL)
1261 *redir = NULL;
1262 }
1263
Owen Taylor3473f882001-02-23 17:55:21 +00001264#ifdef DEBUG_HTTP
1265 if (ctxt->contentType != NULL)
1266 xmlGenericError(xmlGenericErrorContext,
1267 "\nCode %d, content-type '%s'\n\n",
1268 ctxt->returnValue, ctxt->contentType);
1269 else
1270 xmlGenericError(xmlGenericErrorContext,
1271 "\nCode %d, no content-type\n\n",
1272 ctxt->returnValue);
1273#endif
1274
1275 return((void *) ctxt);
1276}
1277
1278/**
Daniel Veillard9403a042001-05-28 11:00:53 +00001279 * xmlNanoHTTPMethod:
1280 * @URL: The URL to load
1281 * @method: the HTTP method to use
1282 * @input: the input string if any
1283 * @contentType: the Content-Type information IN and OUT
1284 * @headers: the extra headers
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001285 * @ilen: input length
Daniel Veillard9403a042001-05-28 11:00:53 +00001286 *
1287 * This function try to open a connection to the indicated resource
1288 * via HTTP using the given @method, adding the given extra headers
1289 * and the input buffer for the request content.
1290 *
1291 * Returns NULL in case of failure, otherwise a request handler.
1292 * The contentType, if provided must be freed by the caller
1293 */
1294
1295void*
1296xmlNanoHTTPMethod(const char *URL, const char *method, const char *input,
Daniel Veillardf012a642001-07-23 19:10:52 +00001297 char **contentType, const char *headers, int ilen) {
Daniel Veillard9403a042001-05-28 11:00:53 +00001298 return(xmlNanoHTTPMethodRedir(URL, method, input, contentType,
Daniel Veillardf012a642001-07-23 19:10:52 +00001299 NULL, headers, ilen));
Daniel Veillard9403a042001-05-28 11:00:53 +00001300}
1301
1302/**
Owen Taylor3473f882001-02-23 17:55:21 +00001303 * xmlNanoHTTPFetch:
1304 * @URL: The URL to load
1305 * @filename: the filename where the content should be saved
1306 * @contentType: if available the Content-Type information will be
1307 * returned at that location
1308 *
1309 * This function try to fetch the indicated resource via HTTP GET
1310 * and save it's content in the file.
1311 *
1312 * Returns -1 in case of failure, 0 incase of success. The contentType,
1313 * if provided must be freed by the caller
1314 */
1315int
1316xmlNanoHTTPFetch(const char *URL, const char *filename, char **contentType) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001317 void *ctxt = NULL;
1318 char *buf = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001319 int fd;
1320 int len;
1321
1322 ctxt = xmlNanoHTTPOpen(URL, contentType);
1323 if (ctxt == NULL) return(-1);
1324
1325 if (!strcmp(filename, "-"))
1326 fd = 0;
1327 else {
1328 fd = open(filename, O_CREAT | O_WRONLY, 00644);
1329 if (fd < 0) {
1330 xmlNanoHTTPClose(ctxt);
1331 if ((contentType != NULL) && (*contentType != NULL)) {
1332 xmlFree(*contentType);
1333 *contentType = NULL;
1334 }
1335 return(-1);
1336 }
1337 }
1338
Daniel Veillardf012a642001-07-23 19:10:52 +00001339 xmlNanoHTTPFetchContent( ctxt, &buf, &len );
1340 if ( len > 0 ) {
Owen Taylor3473f882001-02-23 17:55:21 +00001341 write(fd, buf, len);
1342 }
1343
1344 xmlNanoHTTPClose(ctxt);
1345 close(fd);
1346 return(0);
1347}
1348
1349/**
1350 * xmlNanoHTTPSave:
1351 * @ctxt: the HTTP context
1352 * @filename: the filename where the content should be saved
1353 *
1354 * This function saves the output of the HTTP transaction to a file
1355 * It closes and free the context at the end
1356 *
1357 * Returns -1 in case of failure, 0 incase of success.
1358 */
1359int
1360xmlNanoHTTPSave(void *ctxt, const char *filename) {
Daniel Veillarde3924972001-07-25 20:25:21 +00001361 char *buf = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001362 int fd;
1363 int len;
1364
1365 if (ctxt == NULL) return(-1);
1366
1367 if (!strcmp(filename, "-"))
1368 fd = 0;
1369 else {
1370 fd = open(filename, O_CREAT | O_WRONLY);
1371 if (fd < 0) {
1372 xmlNanoHTTPClose(ctxt);
1373 return(-1);
1374 }
1375 }
1376
Daniel Veillardf012a642001-07-23 19:10:52 +00001377 xmlNanoHTTPFetchContent( ctxt, &buf, &len );
1378 if ( len > 0 ) {
Owen Taylor3473f882001-02-23 17:55:21 +00001379 write(fd, buf, len);
1380 }
1381
1382 xmlNanoHTTPClose(ctxt);
1383 return(0);
1384}
1385
1386/**
1387 * xmlNanoHTTPReturnCode:
1388 * @ctx: the HTTP context
1389 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001390 * Get the latest HTTP return code received
1391 *
Owen Taylor3473f882001-02-23 17:55:21 +00001392 * Returns the HTTP return code for the request.
1393 */
1394int
1395xmlNanoHTTPReturnCode(void *ctx) {
1396 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1397
1398 if (ctxt == NULL) return(-1);
1399
1400 return(ctxt->returnValue);
1401}
1402
1403/**
1404 * xmlNanoHTTPAuthHeader:
1405 * @ctx: the HTTP context
1406 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001407 * Get the authentication header of an HTTP context
1408 *
Owen Taylor3473f882001-02-23 17:55:21 +00001409 * Returns the stashed value of the WWW-Authenticate or Proxy-Authenticate
1410 * header.
1411 */
1412const char *
1413xmlNanoHTTPAuthHeader(void *ctx) {
1414 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1415
1416 if (ctxt == NULL) return(NULL);
1417
1418 return(ctxt->authHeader);
1419}
1420
Daniel Veillardf012a642001-07-23 19:10:52 +00001421/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00001422 * xmlNanoHTTPContentLength:
Daniel Veillardf012a642001-07-23 19:10:52 +00001423 * @ctx: the HTTP context
1424 *
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001425 * Provides the specified content length from the HTTP header.
1426 *
Daniel Veillardf012a642001-07-23 19:10:52 +00001427 * Return the specified content length from the HTTP header. Note that
1428 * a value of -1 indicates that the content length element was not included in
1429 * the response header.
1430 */
1431int
1432xmlNanoHTTPContentLength( void * ctx ) {
1433 xmlNanoHTTPCtxtPtr ctxt = ctx;
1434
1435 return ( ( ctxt == NULL ) ? -1 : ctxt->ContentLength );
1436}
1437
1438/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00001439 * xmlNanoHTTPFetchContent:
Daniel Veillardf012a642001-07-23 19:10:52 +00001440 * @ctx: the HTTP context
1441 * @ptr: pointer to set to the content buffer.
1442 * @len: integer pointer to hold the length of the content
1443 *
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001444 * Check if all the content was read
1445 *
Daniel Veillardf012a642001-07-23 19:10:52 +00001446 * Returns 0 if all the content was read and available, returns
1447 * -1 if received content length was less than specified or an error
1448 * occurred.
1449 */
1450int
1451xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len ) {
1452 xmlNanoHTTPCtxtPtr ctxt = ctx;
1453
1454 int rc = 0;
1455 int cur_lgth;
1456 int rcvd_lgth;
1457 int dummy_int;
1458 char * dummy_ptr = NULL;
1459
1460 /* Dummy up return input parameters if not provided */
1461
1462 if ( len == NULL )
1463 len = &dummy_int;
1464
1465 if ( ptr == NULL )
1466 ptr = &dummy_ptr;
1467
1468 /* But can't work without the context pointer */
1469
1470 if ( ( ctxt == NULL ) || ( ctxt->content == NULL ) ) {
1471 *len = 0;
1472 *ptr = NULL;
1473 return ( -1 );
1474 }
1475
1476 rcvd_lgth = ctxt->inptr - ctxt->content;
1477
1478 while ( (cur_lgth = xmlNanoHTTPRecv( ctxt )) > 0 ) {
1479
1480 rcvd_lgth += cur_lgth;
1481 if ( (ctxt->ContentLength > 0) && (rcvd_lgth >= ctxt->ContentLength) )
1482 break;
1483 }
1484
1485 *ptr = ctxt->content;
1486 *len = rcvd_lgth;
1487
1488 if ( ( ctxt->ContentLength > 0 ) && ( rcvd_lgth < ctxt->ContentLength ) )
1489 rc = -1;
1490 else if ( rcvd_lgth == 0 )
1491 rc = -1;
1492
1493 return ( rc );
1494}
1495
Owen Taylor3473f882001-02-23 17:55:21 +00001496#ifdef STANDALONE
1497int main(int argc, char **argv) {
1498 char *contentType = NULL;
1499
1500 if (argv[1] != NULL) {
1501 if (argv[2] != NULL)
1502 xmlNanoHTTPFetch(argv[1], argv[2], &contentType);
1503 else
1504 xmlNanoHTTPFetch(argv[1], "-", &contentType);
1505 if (contentType != NULL) xmlFree(contentType);
1506 } else {
1507 xmlGenericError(xmlGenericErrorContext,
1508 "%s: minimal HTTP GET implementation\n", argv[0]);
1509 xmlGenericError(xmlGenericErrorContext,
1510 "\tusage %s [ URL [ filename ] ]\n", argv[0]);
1511 }
1512 xmlNanoHTTPCleanup();
1513 xmlMemoryDump();
1514 return(0);
1515}
1516#endif /* STANDALONE */
1517#else /* !LIBXML_HTTP_ENABLED */
1518#ifdef STANDALONE
1519#include <stdio.h>
1520int main(int argc, char **argv) {
1521 xmlGenericError(xmlGenericErrorContext,
1522 "%s : HTTP support not compiled in\n", argv[0]);
1523 return(0);
1524}
1525#endif /* STANDALONE */
1526#endif /* LIBXML_HTTP_ENABLED */