blob: c29cc7ab39cb0789b293cd8afe310ae279a52e83 [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
Bjorn Reese70a9da52001-04-21 16:57:29 +000018#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000019
20#ifdef LIBXML_HTTP_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +000021#include <string.h>
22
23#ifdef HAVE_STDLIB_H
24#include <stdlib.h>
25#endif
26#ifdef HAVE_UNISTD_H
27#include <unistd.h>
28#endif
29#ifdef HAVE_SYS_SOCKET_H
30#include <sys/socket.h>
31#endif
32#ifdef HAVE_NETINET_IN_H
33#include <netinet/in.h>
34#endif
35#ifdef HAVE_ARPA_INET_H
36#include <arpa/inet.h>
37#endif
38#ifdef HAVE_NETDB_H
39#include <netdb.h>
40#endif
41#ifdef HAVE_FCNTL_H
42#include <fcntl.h>
43#endif
44#ifdef HAVE_ERRNO_H
45#include <errno.h>
46#endif
47#ifdef HAVE_SYS_TIME_H
48#include <sys/time.h>
49#endif
50#ifdef HAVE_SYS_SELECT_H
51#include <sys/select.h>
52#endif
53#ifdef HAVE_STRINGS_H
54#include <strings.h>
55#endif
56#ifdef SUPPORT_IP6
57#include <resolv.h>
58#endif
59
60#ifdef VMS
61#include <stropts>
62#define SOCKLEN_T unsigned int
63#define SOCKET int
64#endif
65
Daniel Veillardf012a642001-07-23 19:10:52 +000066#include <libxml/xmlerror.h>
Owen Taylor3473f882001-02-23 17:55:21 +000067#include <libxml/xmlmemory.h>
68#include <libxml/parser.h> /* for xmlStr(n)casecmp() */
69#include <libxml/nanohttp.h>
70
71/**
72 * A couple portability macros
73 */
74#ifndef _WINSOCKAPI_
75#define closesocket(s) close(s)
76#define SOCKET int
77#endif
78
Daniel Veillardf012a642001-07-23 19:10:52 +000079
Owen Taylor3473f882001-02-23 17:55:21 +000080#ifdef STANDALONE
81#define DEBUG_HTTP
82#define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n)
83#define xmlStrcasecmpi(a, b) strcasecmp((char *)a, (char *)b)
84#endif
85
86#define XML_NANO_HTTP_MAX_REDIR 10
87
88#define XML_NANO_HTTP_CHUNK 4096
89
90#define XML_NANO_HTTP_CLOSED 0
91#define XML_NANO_HTTP_WRITE 1
92#define XML_NANO_HTTP_READ 2
93#define XML_NANO_HTTP_NONE 4
94
95typedef struct xmlNanoHTTPCtxt {
96 char *protocol; /* the protocol name */
97 char *hostname; /* the host name */
98 int port; /* the port */
99 char *path; /* the path within the URL */
100 SOCKET fd; /* the file descriptor for the socket */
101 int state; /* WRITE / READ / CLOSED */
102 char *out; /* buffer sent (zero terminated) */
103 char *outptr; /* index within the buffer sent */
104 char *in; /* the receiving buffer */
105 char *content; /* the start of the content */
106 char *inptr; /* the next byte to read from network */
107 char *inrptr; /* the next byte to give back to the client */
108 int inlen; /* len of the input buffer */
109 int last; /* return code for last operation */
110 int returnValue; /* the protocol return value */
Daniel Veillardf012a642001-07-23 19:10:52 +0000111 int ContentLength; /* specified content length from HTTP header */
Owen Taylor3473f882001-02-23 17:55:21 +0000112 char *contentType; /* the MIME type for the input */
113 char *location; /* the new URL in case of redirect */
114 char *authHeader; /* contents of {WWW,Proxy}-Authenticate header */
115} xmlNanoHTTPCtxt, *xmlNanoHTTPCtxtPtr;
116
117static int initialized = 0;
118static char *proxy = NULL; /* the proxy name if any */
119static int proxyPort; /* the proxy port if any */
120static unsigned int timeout = 60;/* the select() timeout in seconds */
121
Daniel Veillardf012a642001-07-23 19:10:52 +0000122int xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len );
123int xmlNanoHTTPContentLength( void * ctx );
124
Owen Taylor3473f882001-02-23 17:55:21 +0000125/**
126 * A portability function
127 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000128static int socket_errno(void) {
Owen Taylor3473f882001-02-23 17:55:21 +0000129#ifdef _WINSOCKAPI_
130 return(WSAGetLastError());
131#else
132 return(errno);
133#endif
134}
135
136/**
137 * xmlNanoHTTPInit:
138 *
139 * Initialize the HTTP protocol layer.
140 * Currently it just checks for proxy informations
141 */
142
143void
144xmlNanoHTTPInit(void) {
145 const char *env;
146#ifdef _WINSOCKAPI_
147 WSADATA wsaData;
148#endif
149
150 if (initialized)
151 return;
152
153#ifdef _WINSOCKAPI_
154 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
155 return;
156#endif
157
158 if (proxy == NULL) {
159 proxyPort = 80;
160 env = getenv("no_proxy");
161 if (env != NULL)
162 goto done;
163 env = getenv("http_proxy");
164 if (env != NULL) {
165 xmlNanoHTTPScanProxy(env);
166 goto done;
167 }
168 env = getenv("HTTP_PROXY");
169 if (env != NULL) {
170 xmlNanoHTTPScanProxy(env);
171 goto done;
172 }
173 }
174done:
175 initialized = 1;
176}
177
178/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000179 * xmlNanoHTTPCleanup:
Owen Taylor3473f882001-02-23 17:55:21 +0000180 *
181 * Cleanup the HTTP protocol layer.
182 */
183
184void
185xmlNanoHTTPCleanup(void) {
186 if (proxy != NULL)
187 xmlFree(proxy);
188#ifdef _WINSOCKAPI_
189 if (initialized)
190 WSACleanup();
191#endif
192 initialized = 0;
193 return;
194}
195
196/**
Owen Taylor3473f882001-02-23 17:55:21 +0000197 * xmlNanoHTTPScanURL:
198 * @ctxt: an HTTP context
199 * @URL: The URL used to initialize the context
200 *
201 * (Re)Initialize an HTTP context by parsing the URL and finding
202 * the protocol host port and path it indicates.
203 */
204
205static void
206xmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt, const char *URL) {
207 const char *cur = URL;
208 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000209 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000210 int port = 0;
211
212 if (ctxt->protocol != NULL) {
213 xmlFree(ctxt->protocol);
214 ctxt->protocol = NULL;
215 }
216 if (ctxt->hostname != NULL) {
217 xmlFree(ctxt->hostname);
218 ctxt->hostname = NULL;
219 }
220 if (ctxt->path != NULL) {
221 xmlFree(ctxt->path);
222 ctxt->path = NULL;
223 }
224 if (URL == NULL) return;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000225 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000226 while (*cur != 0) {
227 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000228 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000229 ctxt->protocol = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000230 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000231 cur += 3;
232 break;
233 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000234 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000235 }
236 if (*cur == 0) return;
237
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000238 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000239 while (1) {
240 if (cur[0] == ':') {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000241 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000242 ctxt->hostname = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000243 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000244 cur += 1;
245 while ((*cur >= '0') && (*cur <= '9')) {
246 port *= 10;
247 port += *cur - '0';
248 cur++;
249 }
250 if (port != 0) ctxt->port = port;
251 while ((cur[0] != '/') && (*cur != 0))
252 cur++;
253 break;
254 }
255 if ((*cur == '/') || (*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 break;
260 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000261 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000262 }
263 if (*cur == 0)
264 ctxt->path = xmlMemStrdup("/");
265 else {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000266 indx = 0;
267 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000268 while (*cur != 0)
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000269 buf[indx++] = *cur++;
270 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000271 ctxt->path = xmlMemStrdup(buf);
272 }
273}
274
275/**
276 * xmlNanoHTTPScanProxy:
277 * @URL: The proxy URL used to initialize the proxy context
278 *
279 * (Re)Initialize the HTTP Proxy context by parsing the URL and finding
280 * the protocol host port it indicates.
281 * Should be like http://myproxy/ or http://myproxy:3128/
282 * A NULL URL cleans up proxy informations.
283 */
284
285void
286xmlNanoHTTPScanProxy(const char *URL) {
287 const char *cur = URL;
288 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000289 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000290 int port = 0;
291
292 if (proxy != NULL) {
293 xmlFree(proxy);
294 proxy = NULL;
295 }
296 if (proxyPort != 0) {
297 proxyPort = 0;
298 }
299#ifdef DEBUG_HTTP
300 if (URL == NULL)
301 xmlGenericError(xmlGenericErrorContext,
302 "Removing HTTP proxy info\n");
303 else
304 xmlGenericError(xmlGenericErrorContext,
305 "Using HTTP proxy %s\n", URL);
306#endif
307 if (URL == NULL) return;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000308 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000309 while (*cur != 0) {
310 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000311 buf[indx] = 0;
312 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000313 cur += 3;
314 break;
315 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000316 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000317 }
318 if (*cur == 0) return;
319
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000320 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000321 while (1) {
322 if (cur[0] == ':') {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000323 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000324 proxy = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000325 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000326 cur += 1;
327 while ((*cur >= '0') && (*cur <= '9')) {
328 port *= 10;
329 port += *cur - '0';
330 cur++;
331 }
332 if (port != 0) proxyPort = port;
333 while ((cur[0] != '/') && (*cur != 0))
334 cur++;
335 break;
336 }
337 if ((*cur == '/') || (*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 break;
342 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000343 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000344 }
345}
346
347/**
348 * xmlNanoHTTPNewCtxt:
349 * @URL: The URL used to initialize the context
350 *
351 * Allocate and initialize a new HTTP context.
352 *
353 * Returns an HTTP context or NULL in case of error.
354 */
355
356static xmlNanoHTTPCtxtPtr
357xmlNanoHTTPNewCtxt(const char *URL) {
358 xmlNanoHTTPCtxtPtr ret;
359
360 ret = (xmlNanoHTTPCtxtPtr) xmlMalloc(sizeof(xmlNanoHTTPCtxt));
361 if (ret == NULL) return(NULL);
362
363 memset(ret, 0, sizeof(xmlNanoHTTPCtxt));
364 ret->port = 80;
365 ret->returnValue = 0;
366 ret->fd = -1;
Daniel Veillardf012a642001-07-23 19:10:52 +0000367 ret->ContentLength = -1;
Owen Taylor3473f882001-02-23 17:55:21 +0000368
369 xmlNanoHTTPScanURL(ret, URL);
370
371 return(ret);
372}
373
374/**
375 * xmlNanoHTTPFreeCtxt:
376 * @ctxt: an HTTP context
377 *
378 * Frees the context after closing the connection.
379 */
380
381static void
382xmlNanoHTTPFreeCtxt(xmlNanoHTTPCtxtPtr ctxt) {
383 if (ctxt == NULL) return;
384 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
385 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
386 if (ctxt->path != NULL) xmlFree(ctxt->path);
387 if (ctxt->out != NULL) xmlFree(ctxt->out);
388 if (ctxt->in != NULL) xmlFree(ctxt->in);
389 if (ctxt->contentType != NULL) xmlFree(ctxt->contentType);
390 if (ctxt->location != NULL) xmlFree(ctxt->location);
391 if (ctxt->authHeader != NULL) xmlFree(ctxt->authHeader);
392 ctxt->state = XML_NANO_HTTP_NONE;
393 if (ctxt->fd >= 0) closesocket(ctxt->fd);
394 ctxt->fd = -1;
395 xmlFree(ctxt);
396}
397
398/**
399 * xmlNanoHTTPSend:
400 * @ctxt: an HTTP context
401 *
402 * Send the input needed to initiate the processing on the server side
Daniel Veillardf012a642001-07-23 19:10:52 +0000403 * Returns number of bytes sent or -1 on error.
Owen Taylor3473f882001-02-23 17:55:21 +0000404 */
405
Daniel Veillardf012a642001-07-23 19:10:52 +0000406static int
407xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt, const char * xmt_ptr, int outlen) {
408
409 int total_sent = 0;
410
411 if ( (ctxt->state & XML_NANO_HTTP_WRITE) && (xmt_ptr != NULL ) ) {
412 while (total_sent < outlen) {
413 int nsent = send(ctxt->fd, xmt_ptr + total_sent,
414 outlen - total_sent, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000415 if (nsent>0)
416 total_sent += nsent;
Daniel Veillardf012a642001-07-23 19:10:52 +0000417 else if ( ( nsent == -1 ) &&
418 ( socket_errno( ) != EAGAIN ) &&
419 ( socket_errno( ) != EWOULDBLOCK ) ) {
420 xmlGenericError( xmlGenericErrorContext,
421 "xmlNanoHTTPSend error: %s",
422 strerror( socket_errno( ) ) );
423
424 if ( total_sent == 0 )
425 total_sent = -1;
426 break;
427 }
428 else {
429 /*
430 ** No data sent
431 ** Since non-blocking sockets are used, wait for
432 ** socket to be writable or default timeout prior
433 ** to retrying.
434 */
435
436 struct timeval tv;
437 fd_set wfd;
438
439 tv.tv_sec = timeout;
440 tv.tv_usec = 0;
441 FD_ZERO( &wfd );
442 FD_SET( ctxt->fd, &wfd );
443 (void)select( ctxt->fd + 1, NULL, &wfd, NULL, &tv );
444 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000445 }
Owen Taylor3473f882001-02-23 17:55:21 +0000446 }
Daniel Veillardf012a642001-07-23 19:10:52 +0000447
448 return total_sent;
Owen Taylor3473f882001-02-23 17:55:21 +0000449}
450
451/**
452 * xmlNanoHTTPRecv:
453 * @ctxt: an HTTP context
454 *
455 * Read information coming from the HTTP connection.
456 * This is a blocking call (but it blocks in select(), not read()).
457 *
458 * Returns the number of byte read or -1 in case of error.
459 */
460
461static int
462xmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt) {
463 fd_set rfd;
464 struct timeval tv;
465
466
467 while (ctxt->state & XML_NANO_HTTP_READ) {
468 if (ctxt->in == NULL) {
469 ctxt->in = (char *) xmlMalloc(65000 * sizeof(char));
470 if (ctxt->in == NULL) {
471 ctxt->last = -1;
Daniel Veillardf012a642001-07-23 19:10:52 +0000472 xmlGenericError( xmlGenericErrorContext,
473 "xmlNanoHTTPRecv: Error allocating input memory." );
Owen Taylor3473f882001-02-23 17:55:21 +0000474 return(-1);
475 }
476 ctxt->inlen = 65000;
477 ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in;
478 }
479 if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) {
480 int delta = ctxt->inrptr - ctxt->in;
481 int len = ctxt->inptr - ctxt->inrptr;
482
483 memmove(ctxt->in, ctxt->inrptr, len);
484 ctxt->inrptr -= delta;
485 ctxt->content -= delta;
486 ctxt->inptr -= delta;
487 }
488 if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) {
489 int d_inptr = ctxt->inptr - ctxt->in;
490 int d_content = ctxt->content - ctxt->in;
491 int d_inrptr = ctxt->inrptr - ctxt->in;
Daniel Veillardf012a642001-07-23 19:10:52 +0000492 char * tmp_ptr = ctxt->in;
Owen Taylor3473f882001-02-23 17:55:21 +0000493
494 ctxt->inlen *= 2;
Daniel Veillardf012a642001-07-23 19:10:52 +0000495 ctxt->in = (char *) xmlRealloc(tmp_ptr, ctxt->inlen);
Owen Taylor3473f882001-02-23 17:55:21 +0000496 if (ctxt->in == NULL) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000497 xmlGenericError( xmlGenericErrorContext,
498 "xmlNanoHTTPRecv: %s %d bytes.",
499 "Failed to realloc input buffer to",
500 ctxt->inlen );
501 xmlFree( tmp_ptr );
Owen Taylor3473f882001-02-23 17:55:21 +0000502 ctxt->last = -1;
503 return(-1);
504 }
505 ctxt->inptr = ctxt->in + d_inptr;
506 ctxt->content = ctxt->in + d_content;
507 ctxt->inrptr = ctxt->in + d_inrptr;
508 }
509 ctxt->last = recv(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK, 0);
510 if (ctxt->last > 0) {
511 ctxt->inptr += ctxt->last;
512 return(ctxt->last);
513 }
514 if (ctxt->last == 0) {
515 return(0);
516 }
517 if (ctxt->last == -1) {
518 switch (socket_errno()) {
519 case EINPROGRESS:
520 case EWOULDBLOCK:
521#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
522 case EAGAIN:
523#endif
524 break;
Daniel Veillardf012a642001-07-23 19:10:52 +0000525
526 case ECONNRESET:
527 case ESHUTDOWN:
528 return ( 0 );
529
Owen Taylor3473f882001-02-23 17:55:21 +0000530 default:
Daniel Veillardf012a642001-07-23 19:10:52 +0000531 xmlGenericError( xmlGenericErrorContext,
532 "xmlNanoHTTPRecv: recv( ) failure - %s",
533 strerror( socket_errno( ) ) );
534 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +0000535 }
536 }
537
538 tv.tv_sec = timeout;
539 tv.tv_usec = 0;
540 FD_ZERO(&rfd);
541 FD_SET(ctxt->fd, &rfd);
542
Daniel Veillardf012a642001-07-23 19:10:52 +0000543 if ( (select(ctxt->fd+1, &rfd, NULL, NULL, &tv)<1) && (errno != EINTR) )
Owen Taylor3473f882001-02-23 17:55:21 +0000544 return(0);
545 }
546 return(0);
547}
548
549/**
550 * xmlNanoHTTPReadLine:
551 * @ctxt: an HTTP context
552 *
553 * Read one line in the HTTP server output, usually for extracting
554 * the HTTP protocol informations from the answer header.
555 *
556 * Returns a newly allocated string with a copy of the line, or NULL
557 * which indicate the end of the input.
558 */
559
560static char *
561xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt) {
562 char buf[4096];
563 char *bp = buf;
Daniel Veillardf012a642001-07-23 19:10:52 +0000564 int rc;
Owen Taylor3473f882001-02-23 17:55:21 +0000565
566 while (bp - buf < 4095) {
567 if (ctxt->inrptr == ctxt->inptr) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000568 if ( (rc = xmlNanoHTTPRecv(ctxt)) == 0) {
Owen Taylor3473f882001-02-23 17:55:21 +0000569 if (bp == buf)
570 return(NULL);
571 else
572 *bp = 0;
573 return(xmlMemStrdup(buf));
574 }
Daniel Veillardf012a642001-07-23 19:10:52 +0000575 else if ( rc == -1 ) {
576 return ( NULL );
577 }
Owen Taylor3473f882001-02-23 17:55:21 +0000578 }
579 *bp = *ctxt->inrptr++;
580 if (*bp == '\n') {
581 *bp = 0;
582 return(xmlMemStrdup(buf));
583 }
584 if (*bp != '\r')
585 bp++;
586 }
587 buf[4095] = 0;
588 return(xmlMemStrdup(buf));
589}
590
591
592/**
593 * xmlNanoHTTPScanAnswer:
594 * @ctxt: an HTTP context
595 * @line: an HTTP header line
596 *
597 * Try to extract useful informations from the server answer.
598 * We currently parse and process:
599 * - The HTTP revision/ return code
600 * - The Content-Type
601 * - The Location for redirrect processing.
602 *
603 * Returns -1 in case of failure, the file descriptor number otherwise
604 */
605
606static void
607xmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt, const char *line) {
608 const char *cur = line;
609
610 if (line == NULL) return;
611
612 if (!strncmp(line, "HTTP/", 5)) {
613 int version = 0;
614 int ret = 0;
615
616 cur += 5;
617 while ((*cur >= '0') && (*cur <= '9')) {
618 version *= 10;
619 version += *cur - '0';
620 cur++;
621 }
622 if (*cur == '.') {
623 cur++;
624 if ((*cur >= '0') && (*cur <= '9')) {
625 version *= 10;
626 version += *cur - '0';
627 cur++;
628 }
629 while ((*cur >= '0') && (*cur <= '9'))
630 cur++;
631 } else
632 version *= 10;
633 if ((*cur != ' ') && (*cur != '\t')) return;
634 while ((*cur == ' ') || (*cur == '\t')) cur++;
635 if ((*cur < '0') || (*cur > '9')) return;
636 while ((*cur >= '0') && (*cur <= '9')) {
637 ret *= 10;
638 ret += *cur - '0';
639 cur++;
640 }
641 if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return;
642 ctxt->returnValue = ret;
643 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Content-Type:", 13)) {
644 cur += 13;
645 while ((*cur == ' ') || (*cur == '\t')) cur++;
646 if (ctxt->contentType != NULL)
647 xmlFree(ctxt->contentType);
648 ctxt->contentType = xmlMemStrdup(cur);
649 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"ContentType:", 12)) {
650 cur += 12;
651 if (ctxt->contentType != NULL) return;
652 while ((*cur == ' ') || (*cur == '\t')) cur++;
653 ctxt->contentType = xmlMemStrdup(cur);
654 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Location:", 9)) {
655 cur += 9;
656 while ((*cur == ' ') || (*cur == '\t')) cur++;
657 if (ctxt->location != NULL)
658 xmlFree(ctxt->location);
659 ctxt->location = xmlMemStrdup(cur);
660 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"WWW-Authenticate:", 17)) {
661 cur += 17;
662 while ((*cur == ' ') || (*cur == '\t')) cur++;
663 if (ctxt->authHeader != NULL)
664 xmlFree(ctxt->authHeader);
665 ctxt->authHeader = xmlMemStrdup(cur);
666 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Proxy-Authenticate:", 19)) {
667 cur += 19;
668 while ((*cur == ' ') || (*cur == '\t')) cur++;
669 if (ctxt->authHeader != NULL)
670 xmlFree(ctxt->authHeader);
671 ctxt->authHeader = xmlMemStrdup(cur);
Daniel Veillardf012a642001-07-23 19:10:52 +0000672 } else if ( !xmlStrncasecmp( BAD_CAST line, BAD_CAST"Content-Length:", 15) ) {
673 cur += 15;
674 ctxt->ContentLength = strtol( cur, NULL, 10 );
Owen Taylor3473f882001-02-23 17:55:21 +0000675 }
676}
677
678/**
679 * xmlNanoHTTPConnectAttempt:
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000680 * @addr: a socket adress structure
Owen Taylor3473f882001-02-23 17:55:21 +0000681 *
682 * Attempt a connection to the given IP:port endpoint. It forces
683 * non-blocking semantic on the socket, and allow 60 seconds for
684 * the host to answer.
685 *
686 * Returns -1 in case of failure, the file descriptor number otherwise
687 */
688
689static int
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000690xmlNanoHTTPConnectAttempt(struct sockaddr *addr)
Owen Taylor3473f882001-02-23 17:55:21 +0000691{
692 SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
693 fd_set wfd;
694 struct timeval tv;
695 int status;
696
697 if (s==-1) {
698#ifdef DEBUG_HTTP
699 perror("socket");
700#endif
Daniel Veillardf012a642001-07-23 19:10:52 +0000701 xmlGenericError( xmlGenericErrorContext,
702 "xmlNanoHTTPConnectAttempt: %s - %s",
703 "socket creation failure",
704 strerror( socket_errno( ) ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000705 return(-1);
706 }
707
708#ifdef _WINSOCKAPI_
709 {
710 u_long one = 1;
711
712 status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0;
713 }
714#else /* _WINSOCKAPI_ */
715#if defined(VMS)
716 {
717 int enable = 1;
718 status = ioctl(s, FIONBIO, &enable);
719 }
720#else /* VMS */
721 if ((status = fcntl(s, F_GETFL, 0)) != -1) {
722#ifdef O_NONBLOCK
723 status |= O_NONBLOCK;
724#else /* O_NONBLOCK */
725#ifdef F_NDELAY
726 status |= F_NDELAY;
727#endif /* F_NDELAY */
728#endif /* !O_NONBLOCK */
729 status = fcntl(s, F_SETFL, status);
730 }
731 if (status < 0) {
732#ifdef DEBUG_HTTP
733 perror("nonblocking");
734#endif
Daniel Veillardf012a642001-07-23 19:10:52 +0000735 xmlGenericError( xmlGenericErrorContext,
736 "xmlNanoHTTPConnectAttempt: %s - %s",
737 "error setting non-blocking IO",
738 strerror( socket_errno( ) ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000739 closesocket(s);
740 return(-1);
741 }
742#endif /* !VMS */
743#endif /* !_WINSOCKAPI_ */
744
Owen Taylor3473f882001-02-23 17:55:21 +0000745 if ((connect(s, addr, sizeof(*addr))==-1)) {
746 switch (socket_errno()) {
747 case EINPROGRESS:
748 case EWOULDBLOCK:
749 break;
750 default:
Daniel Veillardf012a642001-07-23 19:10:52 +0000751 xmlGenericError( xmlGenericErrorContext,
752 "xmlNanoHTTPConnectAttempt: %s - %s",
753 "error connecting to HTTP server",
754 strerror( socket_errno( ) ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000755 closesocket(s);
756 return(-1);
757 }
758 }
759
760 tv.tv_sec = timeout;
761 tv.tv_usec = 0;
762
763 FD_ZERO(&wfd);
764 FD_SET(s, &wfd);
765
766 switch(select(s+1, NULL, &wfd, NULL, &tv))
767 {
768 case 0:
769 /* Time out */
Daniel Veillardf012a642001-07-23 19:10:52 +0000770 xmlGenericError( xmlGenericErrorContext,
771 "xmlNanoHTTPConnectAttempt: %s",
772 "Connect attempt timed out." );
Owen Taylor3473f882001-02-23 17:55:21 +0000773 closesocket(s);
774 return(-1);
775 case -1:
776 /* Ermm.. ?? */
Daniel Veillardf012a642001-07-23 19:10:52 +0000777 xmlGenericError( xmlGenericErrorContext,
778 "xmlNanoHTTPConnectAttempt: %s - %s",
779 "Error connecting to host",
780 strerror( socket_errno( ) ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000781 closesocket(s);
782 return(-1);
783 }
784
785 if ( FD_ISSET(s, &wfd) ) {
786 SOCKLEN_T len;
787 len = sizeof(status);
788 if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&status, &len) < 0 ) {
789 /* Solaris error code */
Daniel Veillardf012a642001-07-23 19:10:52 +0000790 xmlGenericError( xmlGenericErrorContext,
791 "xmlNanoHTTPConnectAttempt: %s - %s",
792 "Error retrieving pending socket errors",
793 strerror( socket_errno( ) ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000794 return (-1);
795 }
796 if ( status ) {
797 closesocket(s);
798 errno = status;
Daniel Veillardf012a642001-07-23 19:10:52 +0000799 xmlGenericError( xmlGenericErrorContext,
800 "xmlNanoHTTPConnectAttempt: %s - %s",
801 "Error connecting to remote host",
802 strerror( status ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000803 return (-1);
804 }
805 } else {
806 /* pbm */
Daniel Veillardf012a642001-07-23 19:10:52 +0000807 xmlGenericError( xmlGenericErrorContext,
808 "xmlNanoHTTPConnectAttempt: %s\n",
809 "Select returned, but descriptor not set for connection.\n" );
810 closesocket(s);
Owen Taylor3473f882001-02-23 17:55:21 +0000811 return (-1);
812 }
813
814 return(s);
815}
816
817/**
818 * xmlNanoHTTPConnectHost:
819 * @host: the host name
820 * @port: the port number
821 *
822 * Attempt a connection to the given host:port endpoint. It tries
823 * the multiple IP provided by the DNS if available.
824 *
825 * Returns -1 in case of failure, the file descriptor number otherwise
826 */
827
828static int
829xmlNanoHTTPConnectHost(const char *host, int port)
830{
831 struct hostent *h;
832 struct sockaddr *addr;
833 struct in_addr ia;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000834 struct sockaddr_in sockin;
Owen Taylor3473f882001-02-23 17:55:21 +0000835#ifdef SUPPORT_IP6
836 struct in6_addr ia6;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000837 struct sockaddr_in6 sockin6;
Owen Taylor3473f882001-02-23 17:55:21 +0000838#endif
839 int i;
840 int s;
841
842#if defined(SUPPORT_IP6) && defined(RES_USE_INET6)
843 if (!(_res.options & RES_INIT))
844 res_init();
845 _res.options |= RES_USE_INET6;
846#endif
847 h=gethostbyname(host);
848 if (h==NULL)
849 {
Daniel Veillardf012a642001-07-23 19:10:52 +0000850 const char * h_err_txt = "";
851 switch ( h_errno )
852 {
853 case HOST_NOT_FOUND:
854 h_err_txt = "Authoritive host not found";
855 break;
856
857 case TRY_AGAIN:
858 h_err_txt =
859 "Non-authoritive host not found or server failure.";
860 break;
861
862 case NO_RECOVERY:
863 h_err_txt =
864 "Non-recoverable errors: FORMERR, REFUSED, or NOTIMP.";
865 break;
866
867 case NO_ADDRESS:
868 h_err_txt = "Valid name, no data record of requested type.";
869 break;
870
871 default:
872 h_err_txt = "No error text defined.";
873 break;
874 }
875 xmlGenericError( xmlGenericErrorContext,
876 "xmlNanoHTTPConnectHost: %s '%s' - %s",
877 "Failed to resolve host", host, h_err_txt );
Owen Taylor3473f882001-02-23 17:55:21 +0000878 return(-1);
879 }
880
881 for(i=0; h->h_addr_list[i]; i++)
882 {
883 if (h->h_addrtype == AF_INET) {
884 /* A records (IPv4) */
885 memcpy(&ia, h->h_addr_list[i], h->h_length);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000886 sockin.sin_family = h->h_addrtype;
887 sockin.sin_addr = ia;
888 sockin.sin_port = htons(port);
889 addr = (struct sockaddr *)&sockin;
Owen Taylor3473f882001-02-23 17:55:21 +0000890#ifdef SUPPORT_IP6
891 } else if (h->h_addrtype == AF_INET6) {
892 /* AAAA records (IPv6) */
893 memcpy(&ia6, h->h_addr_list[i], h->h_length);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000894 sockin6.sin_family = h->h_addrtype;
895 sockin6.sin_addr = ia6;
896 sockin6.sin_port = htons(port);
897 addr = (struct sockaddr *)&sockin6;
Owen Taylor3473f882001-02-23 17:55:21 +0000898#endif
899 } else
900 break; /* for */
901
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000902 s = xmlNanoHTTPConnectAttempt(addr);
Owen Taylor3473f882001-02-23 17:55:21 +0000903 if (s != -1)
904 return(s);
905 }
906
907#ifdef DEBUG_HTTP
908 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf012a642001-07-23 19:10:52 +0000909 "xmlNanoHTTPConnectHost: unable to connect to '%s'.\n", host);
Owen Taylor3473f882001-02-23 17:55:21 +0000910#endif
911 return(-1);
912}
913
914
915/**
916 * xmlNanoHTTPOpen:
917 * @URL: The URL to load
918 * @contentType: if available the Content-Type information will be
919 * returned at that location
920 *
921 * This function try to open a connection to the indicated resource
922 * via HTTP GET.
923 *
924 * Returns NULL in case of failure, otherwise a request handler.
925 * The contentType, if provided must be freed by the caller
926 */
927
928void*
929xmlNanoHTTPOpen(const char *URL, char **contentType) {
930 if (contentType != NULL) *contentType = NULL;
Daniel Veillardf012a642001-07-23 19:10:52 +0000931 return(xmlNanoHTTPMethod(URL, NULL, NULL, contentType, NULL, 0));
Daniel Veillard9403a042001-05-28 11:00:53 +0000932}
933
934/**
935 * xmlNanoHTTPOpenRedir:
936 * @URL: The URL to load
937 * @contentType: if available the Content-Type information will be
938 * returned at that location
939 * @redir: if availble the redirected URL will be returned
940 *
941 * This function try to open a connection to the indicated resource
942 * via HTTP GET.
943 *
944 * Returns NULL in case of failure, otherwise a request handler.
945 * The contentType, if provided must be freed by the caller
946 */
947
948void*
949xmlNanoHTTPOpenRedir(const char *URL, char **contentType, char **redir) {
950 if (contentType != NULL) *contentType = NULL;
951 if (redir != NULL) *redir = NULL;
Daniel Veillardf012a642001-07-23 19:10:52 +0000952 return(xmlNanoHTTPMethodRedir(URL, NULL, NULL, contentType, redir, NULL,0));
Owen Taylor3473f882001-02-23 17:55:21 +0000953}
954
955/**
956 * xmlNanoHTTPRead:
957 * @ctx: the HTTP context
958 * @dest: a buffer
959 * @len: the buffer length
960 *
961 * This function tries to read @len bytes from the existing HTTP connection
962 * and saves them in @dest. This is a blocking call.
963 *
964 * Returns the number of byte read. 0 is an indication of an end of connection.
965 * -1 indicates a parameter error.
966 */
967int
968xmlNanoHTTPRead(void *ctx, void *dest, int len) {
969 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
970
971 if (ctx == NULL) return(-1);
972 if (dest == NULL) return(-1);
973 if (len <= 0) return(0);
974
975 while (ctxt->inptr - ctxt->inrptr < len) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000976 if (xmlNanoHTTPRecv(ctxt) <= 0) break;
Owen Taylor3473f882001-02-23 17:55:21 +0000977 }
978 if (ctxt->inptr - ctxt->inrptr < len)
979 len = ctxt->inptr - ctxt->inrptr;
980 memcpy(dest, ctxt->inrptr, len);
981 ctxt->inrptr += len;
982 return(len);
983}
984
985/**
986 * xmlNanoHTTPClose:
987 * @ctx: the HTTP context
988 *
989 * This function closes an HTTP context, it ends up the connection and
990 * free all data related to it.
991 */
992void
993xmlNanoHTTPClose(void *ctx) {
994 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
995
996 if (ctx == NULL) return;
997
998 xmlNanoHTTPFreeCtxt(ctxt);
999}
1000
1001/**
Daniel Veillard9403a042001-05-28 11:00:53 +00001002 * xmlNanoHTTPMethodRedir:
Owen Taylor3473f882001-02-23 17:55:21 +00001003 * @URL: The URL to load
1004 * @method: the HTTP method to use
1005 * @input: the input string if any
1006 * @contentType: the Content-Type information IN and OUT
Daniel Veillard9403a042001-05-28 11:00:53 +00001007 * @redir: the redirected URL OUT
Owen Taylor3473f882001-02-23 17:55:21 +00001008 * @headers: the extra headers
1009 *
1010 * This function try to open a connection to the indicated resource
1011 * via HTTP using the given @method, adding the given extra headers
1012 * and the input buffer for the request content.
1013 *
1014 * Returns NULL in case of failure, otherwise a request handler.
Daniel Veillard9403a042001-05-28 11:00:53 +00001015 * The contentType, or redir, if provided must be freed by the caller
Owen Taylor3473f882001-02-23 17:55:21 +00001016 */
1017
1018void*
Daniel Veillard9403a042001-05-28 11:00:53 +00001019xmlNanoHTTPMethodRedir(const char *URL, const char *method, const char *input,
Daniel Veillardf012a642001-07-23 19:10:52 +00001020 char **contentType, char **redir,
1021 const char *headers, int ilen ) {
Owen Taylor3473f882001-02-23 17:55:21 +00001022 xmlNanoHTTPCtxtPtr ctxt;
1023 char *bp, *p;
Daniel Veillardf012a642001-07-23 19:10:52 +00001024 int blen, ret;
Owen Taylor3473f882001-02-23 17:55:21 +00001025 int head;
Daniel Veillardf012a642001-07-23 19:10:52 +00001026 int xmt_bytes;
Owen Taylor3473f882001-02-23 17:55:21 +00001027 int nbRedirects = 0;
1028 char *redirURL = NULL;
1029
1030 if (URL == NULL) return(NULL);
1031 if (method == NULL) method = "GET";
1032 xmlNanoHTTPInit();
1033
1034retry:
1035 if (redirURL == NULL)
1036 ctxt = xmlNanoHTTPNewCtxt(URL);
1037 else {
1038 ctxt = xmlNanoHTTPNewCtxt(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001039 }
1040
Daniel Veillardf012a642001-07-23 19:10:52 +00001041 if ( ctxt == NULL ) {
1042 xmlGenericError( xmlGenericErrorContext,
1043 "xmlNanoHTTPMethodRedir: %s %s.",
1044 "Unable to allocate HTTP context to URI",
1045 ( ( redirURL == NULL ) ? URL : redirURL ) );
1046 return ( NULL );
1047 }
1048
Owen Taylor3473f882001-02-23 17:55:21 +00001049 if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001050 xmlGenericError( xmlGenericErrorContext,
1051 "xmlNanoHTTPMethodRedir: %s - %s.",
1052 "Not a valid HTTP URI",
1053 ( ( redirURL == NULL ) ? URL : redirURL ) );
Owen Taylor3473f882001-02-23 17:55:21 +00001054 xmlNanoHTTPFreeCtxt(ctxt);
1055 if (redirURL != NULL) xmlFree(redirURL);
1056 return(NULL);
1057 }
1058 if (ctxt->hostname == NULL) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001059 xmlGenericError( xmlGenericErrorContext,
1060 "xmlNanoHTTPMethodRedir: %s - %s",
1061 "Failed to identify host in URI",
1062 ( ( redirURL == NULL ) ? URL : redirURL ) );
Owen Taylor3473f882001-02-23 17:55:21 +00001063 xmlNanoHTTPFreeCtxt(ctxt);
Daniel Veillard9403a042001-05-28 11:00:53 +00001064 if (redirURL != NULL) xmlFree(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001065 return(NULL);
1066 }
1067 if (proxy) {
1068 blen = strlen(ctxt->hostname) * 2 + 16;
1069 ret = xmlNanoHTTPConnectHost(proxy, proxyPort);
1070 }
1071 else {
1072 blen = strlen(ctxt->hostname);
1073 ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
1074 }
1075 if (ret < 0) {
1076 xmlNanoHTTPFreeCtxt(ctxt);
Daniel Veillard9403a042001-05-28 11:00:53 +00001077 if (redirURL != NULL) xmlFree(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001078 return(NULL);
1079 }
1080 ctxt->fd = ret;
1081
Daniel Veillardf012a642001-07-23 19:10:52 +00001082 if (input == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001083 ilen = 0;
Daniel Veillardf012a642001-07-23 19:10:52 +00001084 else
1085 blen += 36;
1086
Owen Taylor3473f882001-02-23 17:55:21 +00001087 if (headers != NULL)
Daniel Veillardf012a642001-07-23 19:10:52 +00001088 blen += strlen(headers) + 2;
Owen Taylor3473f882001-02-23 17:55:21 +00001089 if (contentType && *contentType)
1090 blen += strlen(*contentType) + 16;
Daniel Veillardf012a642001-07-23 19:10:52 +00001091 blen += strlen(method) + strlen(ctxt->path) + 24;
Owen Taylor3473f882001-02-23 17:55:21 +00001092 bp = xmlMalloc(blen);
Daniel Veillardf012a642001-07-23 19:10:52 +00001093 if ( bp == NULL ) {
1094 xmlNanoHTTPFreeCtxt( ctxt );
1095 xmlGenericError( xmlGenericErrorContext,
1096 "xmlNanoHTTPMethodRedir: %s",
1097 "Error allocating HTTP header buffer." );
1098 return ( NULL );
1099 }
1100
1101 p = bp;
1102
Owen Taylor3473f882001-02-23 17:55:21 +00001103 if (proxy) {
1104 if (ctxt->port != 80) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001105 p += sprintf( p, "%s http://%s:%d%s", method, ctxt->hostname,
1106 ctxt->port, ctxt->path );
Owen Taylor3473f882001-02-23 17:55:21 +00001107 }
1108 else
Daniel Veillardf012a642001-07-23 19:10:52 +00001109 p += sprintf( p, "%s http://%s%s", method,
1110 ctxt->hostname, ctxt->path);
Owen Taylor3473f882001-02-23 17:55:21 +00001111 }
1112 else
Daniel Veillardf012a642001-07-23 19:10:52 +00001113 p += sprintf( p, "%s %s", method, ctxt->path);
1114
1115 p += sprintf(p, " HTTP/1.0\r\nHost: %s\r\n", ctxt->hostname);
1116
1117 if (contentType != NULL && *contentType)
1118 p += sprintf(p, "Content-Type: %s\r\n", *contentType);
1119
1120 if (headers != NULL)
1121 p += sprintf( p, "%s", headers );
1122
Owen Taylor3473f882001-02-23 17:55:21 +00001123 if (input != NULL)
Daniel Veillardf012a642001-07-23 19:10:52 +00001124 sprintf(p, "Content-Length: %d\r\n\r\n", ilen );
Owen Taylor3473f882001-02-23 17:55:21 +00001125 else
1126 strcpy(p, "\r\n");
Daniel Veillardf012a642001-07-23 19:10:52 +00001127
Owen Taylor3473f882001-02-23 17:55:21 +00001128#ifdef DEBUG_HTTP
1129 xmlGenericError(xmlGenericErrorContext,
1130 "-> %s%s", proxy? "(Proxy) " : "", bp);
1131 if ((blen -= strlen(bp)+1) < 0)
1132 xmlGenericError(xmlGenericErrorContext,
1133 "ERROR: overflowed buffer by %d bytes\n", -blen);
1134#endif
1135 ctxt->outptr = ctxt->out = bp;
1136 ctxt->state = XML_NANO_HTTP_WRITE;
Daniel Veillardf012a642001-07-23 19:10:52 +00001137 blen = strlen( ctxt->out );
1138 xmt_bytes = xmlNanoHTTPSend(ctxt, ctxt->out, blen );
1139#ifdef DEBUG_HTTP
1140 if ( xmt_bytes != blen )
1141 xmlGenericError( xmlGenericErrorContext,
1142 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
1143 xmt_bytes, blen,
1144 "bytes of HTTP headers sent to host",
1145 ctxt->hostname );
1146#endif
1147
1148 if ( input != NULL ) {
1149 xmt_bytes = xmlNanoHTTPSend( ctxt, input, ilen );
1150
1151#ifdef DEBUG_HTTP
1152 if ( xmt_bytes != ilen )
1153 xmlGenericError( xmlGenericErrorContext,
1154 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
1155 xmt_bytes, ilen,
1156 "bytes of HTTP content sent to host",
1157 ctxt->hostname );
1158#endif
1159 }
1160
Owen Taylor3473f882001-02-23 17:55:21 +00001161 ctxt->state = XML_NANO_HTTP_READ;
1162 head = 1;
1163
1164 while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
1165 if (head && (*p == 0)) {
1166 head = 0;
1167 ctxt->content = ctxt->inrptr;
1168 xmlFree(p);
1169 break;
1170 }
1171 xmlNanoHTTPScanAnswer(ctxt, p);
1172
1173#ifdef DEBUG_HTTP
1174 xmlGenericError(xmlGenericErrorContext, "<- %s\n", p);
1175#endif
1176 xmlFree(p);
1177 }
1178
1179 if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
1180 (ctxt->returnValue < 400)) {
1181#ifdef DEBUG_HTTP
1182 xmlGenericError(xmlGenericErrorContext,
1183 "\nRedirect to: %s\n", ctxt->location);
1184#endif
Daniel Veillardf012a642001-07-23 19:10:52 +00001185 while ( xmlNanoHTTPRecv(ctxt) > 0 ) ;
Owen Taylor3473f882001-02-23 17:55:21 +00001186 if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
1187 nbRedirects++;
Daniel Veillard9403a042001-05-28 11:00:53 +00001188 if (redirURL != NULL)
1189 xmlFree(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001190 redirURL = xmlMemStrdup(ctxt->location);
1191 xmlNanoHTTPFreeCtxt(ctxt);
1192 goto retry;
1193 }
1194 xmlNanoHTTPFreeCtxt(ctxt);
Daniel Veillard9403a042001-05-28 11:00:53 +00001195 if (redirURL != NULL) xmlFree(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001196#ifdef DEBUG_HTTP
1197 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf012a642001-07-23 19:10:52 +00001198 "xmlNanoHTTPMethodRedir: Too many redirects, aborting ...\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001199#endif
1200 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001201 }
1202
1203 if (contentType != NULL) {
1204 if (ctxt->contentType != NULL)
1205 *contentType = xmlMemStrdup(ctxt->contentType);
1206 else
1207 *contentType = NULL;
1208 }
1209
Daniel Veillard9403a042001-05-28 11:00:53 +00001210 if ((redir != NULL) && (redirURL != NULL)) {
1211 *redir = redirURL;
1212 } else {
1213 if (redirURL != NULL)
1214 xmlFree(redirURL);
1215 if (redir != NULL)
1216 *redir = NULL;
1217 }
1218
Owen Taylor3473f882001-02-23 17:55:21 +00001219#ifdef DEBUG_HTTP
1220 if (ctxt->contentType != NULL)
1221 xmlGenericError(xmlGenericErrorContext,
1222 "\nCode %d, content-type '%s'\n\n",
1223 ctxt->returnValue, ctxt->contentType);
1224 else
1225 xmlGenericError(xmlGenericErrorContext,
1226 "\nCode %d, no content-type\n\n",
1227 ctxt->returnValue);
1228#endif
1229
1230 return((void *) ctxt);
1231}
1232
1233/**
Daniel Veillard9403a042001-05-28 11:00:53 +00001234 * xmlNanoHTTPMethod:
1235 * @URL: The URL to load
1236 * @method: the HTTP method to use
1237 * @input: the input string if any
1238 * @contentType: the Content-Type information IN and OUT
1239 * @headers: the extra headers
1240 *
1241 * This function try to open a connection to the indicated resource
1242 * via HTTP using the given @method, adding the given extra headers
1243 * and the input buffer for the request content.
1244 *
1245 * Returns NULL in case of failure, otherwise a request handler.
1246 * The contentType, if provided must be freed by the caller
1247 */
1248
1249void*
1250xmlNanoHTTPMethod(const char *URL, const char *method, const char *input,
Daniel Veillardf012a642001-07-23 19:10:52 +00001251 char **contentType, const char *headers, int ilen) {
Daniel Veillard9403a042001-05-28 11:00:53 +00001252 return(xmlNanoHTTPMethodRedir(URL, method, input, contentType,
Daniel Veillardf012a642001-07-23 19:10:52 +00001253 NULL, headers, ilen));
Daniel Veillard9403a042001-05-28 11:00:53 +00001254}
1255
1256/**
Owen Taylor3473f882001-02-23 17:55:21 +00001257 * xmlNanoHTTPFetch:
1258 * @URL: The URL to load
1259 * @filename: the filename where the content should be saved
1260 * @contentType: if available the Content-Type information will be
1261 * returned at that location
1262 *
1263 * This function try to fetch the indicated resource via HTTP GET
1264 * and save it's content in the file.
1265 *
1266 * Returns -1 in case of failure, 0 incase of success. The contentType,
1267 * if provided must be freed by the caller
1268 */
1269int
1270xmlNanoHTTPFetch(const char *URL, const char *filename, char **contentType) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001271 void *ctxt = NULL;
1272 char *buf = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001273 int fd;
1274 int len;
1275
1276 ctxt = xmlNanoHTTPOpen(URL, contentType);
1277 if (ctxt == NULL) return(-1);
1278
1279 if (!strcmp(filename, "-"))
1280 fd = 0;
1281 else {
1282 fd = open(filename, O_CREAT | O_WRONLY, 00644);
1283 if (fd < 0) {
1284 xmlNanoHTTPClose(ctxt);
1285 if ((contentType != NULL) && (*contentType != NULL)) {
1286 xmlFree(*contentType);
1287 *contentType = NULL;
1288 }
1289 return(-1);
1290 }
1291 }
1292
Daniel Veillardf012a642001-07-23 19:10:52 +00001293 xmlNanoHTTPFetchContent( ctxt, &buf, &len );
1294 if ( len > 0 ) {
Owen Taylor3473f882001-02-23 17:55:21 +00001295 write(fd, buf, len);
1296 }
1297
1298 xmlNanoHTTPClose(ctxt);
1299 close(fd);
1300 return(0);
1301}
1302
1303/**
1304 * xmlNanoHTTPSave:
1305 * @ctxt: the HTTP context
1306 * @filename: the filename where the content should be saved
1307 *
1308 * This function saves the output of the HTTP transaction to a file
1309 * It closes and free the context at the end
1310 *
1311 * Returns -1 in case of failure, 0 incase of success.
1312 */
1313int
1314xmlNanoHTTPSave(void *ctxt, const char *filename) {
1315 char buf[4096];
1316 int fd;
1317 int len;
1318
1319 if (ctxt == NULL) return(-1);
1320
1321 if (!strcmp(filename, "-"))
1322 fd = 0;
1323 else {
1324 fd = open(filename, O_CREAT | O_WRONLY);
1325 if (fd < 0) {
1326 xmlNanoHTTPClose(ctxt);
1327 return(-1);
1328 }
1329 }
1330
Daniel Veillardf012a642001-07-23 19:10:52 +00001331 xmlNanoHTTPFetchContent( ctxt, &buf, &len );
1332 if ( len > 0 ) {
Owen Taylor3473f882001-02-23 17:55:21 +00001333 write(fd, buf, len);
1334 }
1335
1336 xmlNanoHTTPClose(ctxt);
1337 return(0);
1338}
1339
1340/**
1341 * xmlNanoHTTPReturnCode:
1342 * @ctx: the HTTP context
1343 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001344 * Get the latest HTTP return code received
1345 *
Owen Taylor3473f882001-02-23 17:55:21 +00001346 * Returns the HTTP return code for the request.
1347 */
1348int
1349xmlNanoHTTPReturnCode(void *ctx) {
1350 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1351
1352 if (ctxt == NULL) return(-1);
1353
1354 return(ctxt->returnValue);
1355}
1356
1357/**
1358 * xmlNanoHTTPAuthHeader:
1359 * @ctx: the HTTP context
1360 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001361 * Get the authentication header of an HTTP context
1362 *
Owen Taylor3473f882001-02-23 17:55:21 +00001363 * Returns the stashed value of the WWW-Authenticate or Proxy-Authenticate
1364 * header.
1365 */
1366const char *
1367xmlNanoHTTPAuthHeader(void *ctx) {
1368 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1369
1370 if (ctxt == NULL) return(NULL);
1371
1372 return(ctxt->authHeader);
1373}
1374
Daniel Veillardf012a642001-07-23 19:10:52 +00001375/**
1376 * xmlNanoHTTPContentLength
1377 * @ctx: the HTTP context
1378 *
1379 * Return the specified content length from the HTTP header. Note that
1380 * a value of -1 indicates that the content length element was not included in
1381 * the response header.
1382 */
1383int
1384xmlNanoHTTPContentLength( void * ctx ) {
1385 xmlNanoHTTPCtxtPtr ctxt = ctx;
1386
1387 return ( ( ctxt == NULL ) ? -1 : ctxt->ContentLength );
1388}
1389
1390/**
1391 * xmlNanoHTTPFetchContent
1392 * @ctx: the HTTP context
1393 * @ptr: pointer to set to the content buffer.
1394 * @len: integer pointer to hold the length of the content
1395 *
1396 * Returns 0 if all the content was read and available, returns
1397 * -1 if received content length was less than specified or an error
1398 * occurred.
1399 */
1400int
1401xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len ) {
1402 xmlNanoHTTPCtxtPtr ctxt = ctx;
1403
1404 int rc = 0;
1405 int cur_lgth;
1406 int rcvd_lgth;
1407 int dummy_int;
1408 char * dummy_ptr = NULL;
1409
1410 /* Dummy up return input parameters if not provided */
1411
1412 if ( len == NULL )
1413 len = &dummy_int;
1414
1415 if ( ptr == NULL )
1416 ptr = &dummy_ptr;
1417
1418 /* But can't work without the context pointer */
1419
1420 if ( ( ctxt == NULL ) || ( ctxt->content == NULL ) ) {
1421 *len = 0;
1422 *ptr = NULL;
1423 return ( -1 );
1424 }
1425
1426 rcvd_lgth = ctxt->inptr - ctxt->content;
1427
1428 while ( (cur_lgth = xmlNanoHTTPRecv( ctxt )) > 0 ) {
1429
1430 rcvd_lgth += cur_lgth;
1431 if ( (ctxt->ContentLength > 0) && (rcvd_lgth >= ctxt->ContentLength) )
1432 break;
1433 }
1434
1435 *ptr = ctxt->content;
1436 *len = rcvd_lgth;
1437
1438 if ( ( ctxt->ContentLength > 0 ) && ( rcvd_lgth < ctxt->ContentLength ) )
1439 rc = -1;
1440 else if ( rcvd_lgth == 0 )
1441 rc = -1;
1442
1443 return ( rc );
1444}
1445
Owen Taylor3473f882001-02-23 17:55:21 +00001446#ifdef STANDALONE
1447int main(int argc, char **argv) {
1448 char *contentType = NULL;
1449
1450 if (argv[1] != NULL) {
1451 if (argv[2] != NULL)
1452 xmlNanoHTTPFetch(argv[1], argv[2], &contentType);
1453 else
1454 xmlNanoHTTPFetch(argv[1], "-", &contentType);
1455 if (contentType != NULL) xmlFree(contentType);
1456 } else {
1457 xmlGenericError(xmlGenericErrorContext,
1458 "%s: minimal HTTP GET implementation\n", argv[0]);
1459 xmlGenericError(xmlGenericErrorContext,
1460 "\tusage %s [ URL [ filename ] ]\n", argv[0]);
1461 }
1462 xmlNanoHTTPCleanup();
1463 xmlMemoryDump();
1464 return(0);
1465}
1466#endif /* STANDALONE */
1467#else /* !LIBXML_HTTP_ENABLED */
1468#ifdef STANDALONE
1469#include <stdio.h>
1470int main(int argc, char **argv) {
1471 xmlGenericError(xmlGenericErrorContext,
1472 "%s : HTTP support not compiled in\n", argv[0]);
1473 return(0);
1474}
1475#endif /* STANDALONE */
1476#endif /* LIBXML_HTTP_ENABLED */