blob: 7acddca0e05adfe0a5bc8e837ee1aab64239f665 [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 Veillardd0463562001-10-13 09:15:48 +000066#include <libxml/globals.h>
Daniel Veillardf012a642001-07-23 19:10:52 +000067#include <libxml/xmlerror.h>
Owen Taylor3473f882001-02-23 17:55:21 +000068#include <libxml/xmlmemory.h>
69#include <libxml/parser.h> /* for xmlStr(n)casecmp() */
70#include <libxml/nanohttp.h>
Daniel Veillard3c01b1d2001-10-17 15:58:35 +000071#include <libxml/globals.h>
Owen Taylor3473f882001-02-23 17:55:21 +000072
73/**
74 * A couple portability macros
75 */
76#ifndef _WINSOCKAPI_
77#define closesocket(s) close(s)
78#define SOCKET int
79#endif
80
Daniel Veillard75be0132002-03-13 10:03:35 +000081#ifndef SOCKLEN_T
82#define SOCKLEN_T unsigned int
83#endif
84#ifndef SOCKET
85#define SOCKET int
86#endif
Daniel Veillardf012a642001-07-23 19:10:52 +000087
Owen Taylor3473f882001-02-23 17:55:21 +000088#ifdef STANDALONE
89#define DEBUG_HTTP
90#define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n)
91#define xmlStrcasecmpi(a, b) strcasecmp((char *)a, (char *)b)
92#endif
93
94#define XML_NANO_HTTP_MAX_REDIR 10
95
96#define XML_NANO_HTTP_CHUNK 4096
97
98#define XML_NANO_HTTP_CLOSED 0
99#define XML_NANO_HTTP_WRITE 1
100#define XML_NANO_HTTP_READ 2
101#define XML_NANO_HTTP_NONE 4
102
103typedef struct xmlNanoHTTPCtxt {
104 char *protocol; /* the protocol name */
105 char *hostname; /* the host name */
106 int port; /* the port */
107 char *path; /* the path within the URL */
108 SOCKET fd; /* the file descriptor for the socket */
109 int state; /* WRITE / READ / CLOSED */
110 char *out; /* buffer sent (zero terminated) */
111 char *outptr; /* index within the buffer sent */
112 char *in; /* the receiving buffer */
113 char *content; /* the start of the content */
114 char *inptr; /* the next byte to read from network */
115 char *inrptr; /* the next byte to give back to the client */
116 int inlen; /* len of the input buffer */
117 int last; /* return code for last operation */
118 int returnValue; /* the protocol return value */
Daniel Veillardf012a642001-07-23 19:10:52 +0000119 int ContentLength; /* specified content length from HTTP header */
Owen Taylor3473f882001-02-23 17:55:21 +0000120 char *contentType; /* the MIME type for the input */
121 char *location; /* the new URL in case of redirect */
122 char *authHeader; /* contents of {WWW,Proxy}-Authenticate header */
123} xmlNanoHTTPCtxt, *xmlNanoHTTPCtxtPtr;
124
125static int initialized = 0;
126static char *proxy = NULL; /* the proxy name if any */
127static int proxyPort; /* the proxy port if any */
128static unsigned int timeout = 60;/* the select() timeout in seconds */
129
Daniel Veillardf012a642001-07-23 19:10:52 +0000130int xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len );
131int xmlNanoHTTPContentLength( void * ctx );
132
Owen Taylor3473f882001-02-23 17:55:21 +0000133/**
134 * A portability function
135 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000136static int socket_errno(void) {
Owen Taylor3473f882001-02-23 17:55:21 +0000137#ifdef _WINSOCKAPI_
138 return(WSAGetLastError());
139#else
140 return(errno);
141#endif
142}
143
144/**
145 * xmlNanoHTTPInit:
146 *
147 * Initialize the HTTP protocol layer.
148 * Currently it just checks for proxy informations
149 */
150
151void
152xmlNanoHTTPInit(void) {
153 const char *env;
154#ifdef _WINSOCKAPI_
155 WSADATA wsaData;
156#endif
157
158 if (initialized)
159 return;
160
161#ifdef _WINSOCKAPI_
162 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
163 return;
164#endif
165
166 if (proxy == NULL) {
167 proxyPort = 80;
168 env = getenv("no_proxy");
169 if (env != NULL)
170 goto done;
171 env = getenv("http_proxy");
172 if (env != NULL) {
173 xmlNanoHTTPScanProxy(env);
174 goto done;
175 }
176 env = getenv("HTTP_PROXY");
177 if (env != NULL) {
178 xmlNanoHTTPScanProxy(env);
179 goto done;
180 }
181 }
182done:
183 initialized = 1;
184}
185
186/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000187 * xmlNanoHTTPCleanup:
Owen Taylor3473f882001-02-23 17:55:21 +0000188 *
189 * Cleanup the HTTP protocol layer.
190 */
191
192void
193xmlNanoHTTPCleanup(void) {
194 if (proxy != NULL)
195 xmlFree(proxy);
196#ifdef _WINSOCKAPI_
197 if (initialized)
198 WSACleanup();
199#endif
200 initialized = 0;
201 return;
202}
203
204/**
Owen Taylor3473f882001-02-23 17:55:21 +0000205 * xmlNanoHTTPScanURL:
206 * @ctxt: an HTTP context
207 * @URL: The URL used to initialize the context
208 *
209 * (Re)Initialize an HTTP context by parsing the URL and finding
210 * the protocol host port and path it indicates.
211 */
212
213static void
214xmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt, const char *URL) {
215 const char *cur = URL;
216 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000217 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000218 int port = 0;
219
220 if (ctxt->protocol != NULL) {
221 xmlFree(ctxt->protocol);
222 ctxt->protocol = NULL;
223 }
224 if (ctxt->hostname != NULL) {
225 xmlFree(ctxt->hostname);
226 ctxt->hostname = NULL;
227 }
228 if (ctxt->path != NULL) {
229 xmlFree(ctxt->path);
230 ctxt->path = NULL;
231 }
232 if (URL == NULL) return;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000233 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000234 while (*cur != 0) {
235 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000236 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000237 ctxt->protocol = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000238 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000239 cur += 3;
240 break;
241 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000242 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000243 }
244 if (*cur == 0) return;
245
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000246 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000247 while (1) {
248 if (cur[0] == ':') {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000249 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000250 ctxt->hostname = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000251 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000252 cur += 1;
253 while ((*cur >= '0') && (*cur <= '9')) {
254 port *= 10;
255 port += *cur - '0';
256 cur++;
257 }
258 if (port != 0) ctxt->port = port;
259 while ((cur[0] != '/') && (*cur != 0))
260 cur++;
261 break;
262 }
263 if ((*cur == '/') || (*cur == 0)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000264 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000265 ctxt->hostname = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000266 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000267 break;
268 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000269 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000270 }
271 if (*cur == 0)
272 ctxt->path = xmlMemStrdup("/");
273 else {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000274 indx = 0;
275 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000276 while (*cur != 0)
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000277 buf[indx++] = *cur++;
278 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000279 ctxt->path = xmlMemStrdup(buf);
280 }
281}
282
283/**
284 * xmlNanoHTTPScanProxy:
285 * @URL: The proxy URL used to initialize the proxy context
286 *
287 * (Re)Initialize the HTTP Proxy context by parsing the URL and finding
288 * the protocol host port it indicates.
289 * Should be like http://myproxy/ or http://myproxy:3128/
290 * A NULL URL cleans up proxy informations.
291 */
292
293void
294xmlNanoHTTPScanProxy(const char *URL) {
295 const char *cur = URL;
296 char buf[4096];
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000297 int indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000298 int port = 0;
299
300 if (proxy != NULL) {
301 xmlFree(proxy);
302 proxy = NULL;
303 }
304 if (proxyPort != 0) {
305 proxyPort = 0;
306 }
307#ifdef DEBUG_HTTP
308 if (URL == NULL)
309 xmlGenericError(xmlGenericErrorContext,
310 "Removing HTTP proxy info\n");
311 else
312 xmlGenericError(xmlGenericErrorContext,
313 "Using HTTP proxy %s\n", URL);
314#endif
315 if (URL == NULL) return;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000316 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000317 while (*cur != 0) {
318 if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000319 buf[indx] = 0;
320 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000321 cur += 3;
322 break;
323 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000324 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000325 }
326 if (*cur == 0) return;
327
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000328 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000329 while (1) {
330 if (cur[0] == ':') {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000331 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000332 proxy = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000333 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000334 cur += 1;
335 while ((*cur >= '0') && (*cur <= '9')) {
336 port *= 10;
337 port += *cur - '0';
338 cur++;
339 }
340 if (port != 0) proxyPort = port;
341 while ((cur[0] != '/') && (*cur != 0))
342 cur++;
343 break;
344 }
345 if ((*cur == '/') || (*cur == 0)) {
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000346 buf[indx] = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000347 proxy = xmlMemStrdup(buf);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000348 indx = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000349 break;
350 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000351 buf[indx++] = *cur++;
Owen Taylor3473f882001-02-23 17:55:21 +0000352 }
353}
354
355/**
356 * xmlNanoHTTPNewCtxt:
357 * @URL: The URL used to initialize the context
358 *
359 * Allocate and initialize a new HTTP context.
360 *
361 * Returns an HTTP context or NULL in case of error.
362 */
363
364static xmlNanoHTTPCtxtPtr
365xmlNanoHTTPNewCtxt(const char *URL) {
366 xmlNanoHTTPCtxtPtr ret;
367
368 ret = (xmlNanoHTTPCtxtPtr) xmlMalloc(sizeof(xmlNanoHTTPCtxt));
369 if (ret == NULL) return(NULL);
370
371 memset(ret, 0, sizeof(xmlNanoHTTPCtxt));
372 ret->port = 80;
373 ret->returnValue = 0;
374 ret->fd = -1;
Daniel Veillardf012a642001-07-23 19:10:52 +0000375 ret->ContentLength = -1;
Owen Taylor3473f882001-02-23 17:55:21 +0000376
377 xmlNanoHTTPScanURL(ret, URL);
378
379 return(ret);
380}
381
382/**
383 * xmlNanoHTTPFreeCtxt:
384 * @ctxt: an HTTP context
385 *
386 * Frees the context after closing the connection.
387 */
388
389static void
390xmlNanoHTTPFreeCtxt(xmlNanoHTTPCtxtPtr ctxt) {
391 if (ctxt == NULL) return;
392 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
393 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
394 if (ctxt->path != NULL) xmlFree(ctxt->path);
395 if (ctxt->out != NULL) xmlFree(ctxt->out);
396 if (ctxt->in != NULL) xmlFree(ctxt->in);
397 if (ctxt->contentType != NULL) xmlFree(ctxt->contentType);
398 if (ctxt->location != NULL) xmlFree(ctxt->location);
399 if (ctxt->authHeader != NULL) xmlFree(ctxt->authHeader);
400 ctxt->state = XML_NANO_HTTP_NONE;
401 if (ctxt->fd >= 0) closesocket(ctxt->fd);
402 ctxt->fd = -1;
403 xmlFree(ctxt);
404}
405
406/**
407 * xmlNanoHTTPSend:
408 * @ctxt: an HTTP context
409 *
410 * Send the input needed to initiate the processing on the server side
Daniel Veillardf012a642001-07-23 19:10:52 +0000411 * Returns number of bytes sent or -1 on error.
Owen Taylor3473f882001-02-23 17:55:21 +0000412 */
413
Daniel Veillardf012a642001-07-23 19:10:52 +0000414static int
415xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt, const char * xmt_ptr, int outlen) {
416
417 int total_sent = 0;
418
419 if ( (ctxt->state & XML_NANO_HTTP_WRITE) && (xmt_ptr != NULL ) ) {
420 while (total_sent < outlen) {
421 int nsent = send(ctxt->fd, xmt_ptr + total_sent,
422 outlen - total_sent, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000423 if (nsent>0)
424 total_sent += nsent;
Daniel Veillardf012a642001-07-23 19:10:52 +0000425 else if ( ( nsent == -1 ) &&
Daniel Veillardba6db032001-07-31 16:25:45 +0000426#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
Daniel Veillardf012a642001-07-23 19:10:52 +0000427 ( socket_errno( ) != EAGAIN ) &&
Daniel Veillardba6db032001-07-31 16:25:45 +0000428#endif
Daniel Veillardf012a642001-07-23 19:10:52 +0000429 ( socket_errno( ) != EWOULDBLOCK ) ) {
430 xmlGenericError( xmlGenericErrorContext,
431 "xmlNanoHTTPSend error: %s",
432 strerror( socket_errno( ) ) );
433
434 if ( total_sent == 0 )
435 total_sent = -1;
436 break;
437 }
438 else {
439 /*
440 ** No data sent
441 ** Since non-blocking sockets are used, wait for
442 ** socket to be writable or default timeout prior
443 ** to retrying.
444 */
445
446 struct timeval tv;
447 fd_set wfd;
448
449 tv.tv_sec = timeout;
450 tv.tv_usec = 0;
451 FD_ZERO( &wfd );
452 FD_SET( ctxt->fd, &wfd );
453 (void)select( ctxt->fd + 1, NULL, &wfd, NULL, &tv );
454 }
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000455 }
Owen Taylor3473f882001-02-23 17:55:21 +0000456 }
Daniel Veillardf012a642001-07-23 19:10:52 +0000457
458 return total_sent;
Owen Taylor3473f882001-02-23 17:55:21 +0000459}
460
461/**
462 * xmlNanoHTTPRecv:
463 * @ctxt: an HTTP context
464 *
465 * Read information coming from the HTTP connection.
466 * This is a blocking call (but it blocks in select(), not read()).
467 *
468 * Returns the number of byte read or -1 in case of error.
469 */
470
471static int
472xmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt) {
473 fd_set rfd;
474 struct timeval tv;
475
476
477 while (ctxt->state & XML_NANO_HTTP_READ) {
478 if (ctxt->in == NULL) {
479 ctxt->in = (char *) xmlMalloc(65000 * sizeof(char));
480 if (ctxt->in == NULL) {
481 ctxt->last = -1;
Daniel Veillardf012a642001-07-23 19:10:52 +0000482 xmlGenericError( xmlGenericErrorContext,
483 "xmlNanoHTTPRecv: Error allocating input memory." );
Owen Taylor3473f882001-02-23 17:55:21 +0000484 return(-1);
485 }
486 ctxt->inlen = 65000;
487 ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in;
488 }
489 if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) {
490 int delta = ctxt->inrptr - ctxt->in;
491 int len = ctxt->inptr - ctxt->inrptr;
492
493 memmove(ctxt->in, ctxt->inrptr, len);
494 ctxt->inrptr -= delta;
495 ctxt->content -= delta;
496 ctxt->inptr -= delta;
497 }
498 if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) {
499 int d_inptr = ctxt->inptr - ctxt->in;
500 int d_content = ctxt->content - ctxt->in;
501 int d_inrptr = ctxt->inrptr - ctxt->in;
Daniel Veillardf012a642001-07-23 19:10:52 +0000502 char * tmp_ptr = ctxt->in;
Owen Taylor3473f882001-02-23 17:55:21 +0000503
504 ctxt->inlen *= 2;
Daniel Veillardf012a642001-07-23 19:10:52 +0000505 ctxt->in = (char *) xmlRealloc(tmp_ptr, ctxt->inlen);
Owen Taylor3473f882001-02-23 17:55:21 +0000506 if (ctxt->in == NULL) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000507 xmlGenericError( xmlGenericErrorContext,
508 "xmlNanoHTTPRecv: %s %d bytes.",
509 "Failed to realloc input buffer to",
510 ctxt->inlen );
511 xmlFree( tmp_ptr );
Owen Taylor3473f882001-02-23 17:55:21 +0000512 ctxt->last = -1;
513 return(-1);
514 }
515 ctxt->inptr = ctxt->in + d_inptr;
516 ctxt->content = ctxt->in + d_content;
517 ctxt->inrptr = ctxt->in + d_inrptr;
518 }
519 ctxt->last = recv(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK, 0);
520 if (ctxt->last > 0) {
521 ctxt->inptr += ctxt->last;
522 return(ctxt->last);
523 }
524 if (ctxt->last == 0) {
525 return(0);
526 }
527 if (ctxt->last == -1) {
528 switch (socket_errno()) {
529 case EINPROGRESS:
530 case EWOULDBLOCK:
531#if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
532 case EAGAIN:
533#endif
534 break;
Daniel Veillardf012a642001-07-23 19:10:52 +0000535
536 case ECONNRESET:
537 case ESHUTDOWN:
538 return ( 0 );
539
Owen Taylor3473f882001-02-23 17:55:21 +0000540 default:
Daniel Veillardf012a642001-07-23 19:10:52 +0000541 xmlGenericError( xmlGenericErrorContext,
542 "xmlNanoHTTPRecv: recv( ) failure - %s",
543 strerror( socket_errno( ) ) );
544 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +0000545 }
546 }
547
548 tv.tv_sec = timeout;
549 tv.tv_usec = 0;
550 FD_ZERO(&rfd);
551 FD_SET(ctxt->fd, &rfd);
552
Daniel Veillard50f34372001-08-03 12:06:36 +0000553 if ( (select(ctxt->fd+1, &rfd, NULL, NULL, &tv)<1)
554#if defined(EINTR)
555 && (errno != EINTR)
556#endif
557 )
Owen Taylor3473f882001-02-23 17:55:21 +0000558 return(0);
559 }
560 return(0);
561}
562
563/**
564 * xmlNanoHTTPReadLine:
565 * @ctxt: an HTTP context
566 *
567 * Read one line in the HTTP server output, usually for extracting
568 * the HTTP protocol informations from the answer header.
569 *
570 * Returns a newly allocated string with a copy of the line, or NULL
571 * which indicate the end of the input.
572 */
573
574static char *
575xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt) {
576 char buf[4096];
577 char *bp = buf;
Daniel Veillardf012a642001-07-23 19:10:52 +0000578 int rc;
Owen Taylor3473f882001-02-23 17:55:21 +0000579
580 while (bp - buf < 4095) {
581 if (ctxt->inrptr == ctxt->inptr) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000582 if ( (rc = xmlNanoHTTPRecv(ctxt)) == 0) {
Owen Taylor3473f882001-02-23 17:55:21 +0000583 if (bp == buf)
584 return(NULL);
585 else
586 *bp = 0;
587 return(xmlMemStrdup(buf));
588 }
Daniel Veillardf012a642001-07-23 19:10:52 +0000589 else if ( rc == -1 ) {
590 return ( NULL );
591 }
Owen Taylor3473f882001-02-23 17:55:21 +0000592 }
593 *bp = *ctxt->inrptr++;
594 if (*bp == '\n') {
595 *bp = 0;
596 return(xmlMemStrdup(buf));
597 }
598 if (*bp != '\r')
599 bp++;
600 }
601 buf[4095] = 0;
602 return(xmlMemStrdup(buf));
603}
604
605
606/**
607 * xmlNanoHTTPScanAnswer:
608 * @ctxt: an HTTP context
609 * @line: an HTTP header line
610 *
611 * Try to extract useful informations from the server answer.
612 * We currently parse and process:
613 * - The HTTP revision/ return code
614 * - The Content-Type
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000615 * - The Location for redirect processing.
Owen Taylor3473f882001-02-23 17:55:21 +0000616 *
617 * Returns -1 in case of failure, the file descriptor number otherwise
618 */
619
620static void
621xmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt, const char *line) {
622 const char *cur = line;
623
624 if (line == NULL) return;
625
626 if (!strncmp(line, "HTTP/", 5)) {
627 int version = 0;
628 int ret = 0;
629
630 cur += 5;
631 while ((*cur >= '0') && (*cur <= '9')) {
632 version *= 10;
633 version += *cur - '0';
634 cur++;
635 }
636 if (*cur == '.') {
637 cur++;
638 if ((*cur >= '0') && (*cur <= '9')) {
639 version *= 10;
640 version += *cur - '0';
641 cur++;
642 }
643 while ((*cur >= '0') && (*cur <= '9'))
644 cur++;
645 } else
646 version *= 10;
647 if ((*cur != ' ') && (*cur != '\t')) return;
648 while ((*cur == ' ') || (*cur == '\t')) cur++;
649 if ((*cur < '0') || (*cur > '9')) return;
650 while ((*cur >= '0') && (*cur <= '9')) {
651 ret *= 10;
652 ret += *cur - '0';
653 cur++;
654 }
655 if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return;
656 ctxt->returnValue = ret;
657 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Content-Type:", 13)) {
658 cur += 13;
659 while ((*cur == ' ') || (*cur == '\t')) cur++;
660 if (ctxt->contentType != NULL)
661 xmlFree(ctxt->contentType);
662 ctxt->contentType = xmlMemStrdup(cur);
663 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"ContentType:", 12)) {
664 cur += 12;
665 if (ctxt->contentType != NULL) return;
666 while ((*cur == ' ') || (*cur == '\t')) cur++;
667 ctxt->contentType = xmlMemStrdup(cur);
668 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Location:", 9)) {
669 cur += 9;
670 while ((*cur == ' ') || (*cur == '\t')) cur++;
671 if (ctxt->location != NULL)
672 xmlFree(ctxt->location);
673 ctxt->location = xmlMemStrdup(cur);
674 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"WWW-Authenticate:", 17)) {
675 cur += 17;
676 while ((*cur == ' ') || (*cur == '\t')) cur++;
677 if (ctxt->authHeader != NULL)
678 xmlFree(ctxt->authHeader);
679 ctxt->authHeader = xmlMemStrdup(cur);
680 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Proxy-Authenticate:", 19)) {
681 cur += 19;
682 while ((*cur == ' ') || (*cur == '\t')) cur++;
683 if (ctxt->authHeader != NULL)
684 xmlFree(ctxt->authHeader);
685 ctxt->authHeader = xmlMemStrdup(cur);
Daniel Veillardf012a642001-07-23 19:10:52 +0000686 } else if ( !xmlStrncasecmp( BAD_CAST line, BAD_CAST"Content-Length:", 15) ) {
687 cur += 15;
688 ctxt->ContentLength = strtol( cur, NULL, 10 );
Owen Taylor3473f882001-02-23 17:55:21 +0000689 }
690}
691
692/**
693 * xmlNanoHTTPConnectAttempt:
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000694 * @addr: a socket address structure
Owen Taylor3473f882001-02-23 17:55:21 +0000695 *
696 * Attempt a connection to the given IP:port endpoint. It forces
697 * non-blocking semantic on the socket, and allow 60 seconds for
698 * the host to answer.
699 *
700 * Returns -1 in case of failure, the file descriptor number otherwise
701 */
702
703static int
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000704xmlNanoHTTPConnectAttempt(struct sockaddr *addr)
Owen Taylor3473f882001-02-23 17:55:21 +0000705{
706 SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
707 fd_set wfd;
708 struct timeval tv;
709 int status;
710
711 if (s==-1) {
712#ifdef DEBUG_HTTP
713 perror("socket");
714#endif
Daniel Veillardf012a642001-07-23 19:10:52 +0000715 xmlGenericError( xmlGenericErrorContext,
716 "xmlNanoHTTPConnectAttempt: %s - %s",
717 "socket creation failure",
718 strerror( socket_errno( ) ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000719 return(-1);
720 }
721
722#ifdef _WINSOCKAPI_
723 {
724 u_long one = 1;
725
726 status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0;
727 }
728#else /* _WINSOCKAPI_ */
729#if defined(VMS)
730 {
731 int enable = 1;
732 status = ioctl(s, FIONBIO, &enable);
733 }
734#else /* VMS */
735 if ((status = fcntl(s, F_GETFL, 0)) != -1) {
736#ifdef O_NONBLOCK
737 status |= O_NONBLOCK;
738#else /* O_NONBLOCK */
739#ifdef F_NDELAY
740 status |= F_NDELAY;
741#endif /* F_NDELAY */
742#endif /* !O_NONBLOCK */
743 status = fcntl(s, F_SETFL, status);
744 }
745 if (status < 0) {
746#ifdef DEBUG_HTTP
747 perror("nonblocking");
748#endif
Daniel Veillardf012a642001-07-23 19:10:52 +0000749 xmlGenericError( xmlGenericErrorContext,
750 "xmlNanoHTTPConnectAttempt: %s - %s",
751 "error setting non-blocking IO",
752 strerror( socket_errno( ) ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000753 closesocket(s);
754 return(-1);
755 }
756#endif /* !VMS */
757#endif /* !_WINSOCKAPI_ */
758
Owen Taylor3473f882001-02-23 17:55:21 +0000759 if ((connect(s, addr, sizeof(*addr))==-1)) {
760 switch (socket_errno()) {
761 case EINPROGRESS:
762 case EWOULDBLOCK:
763 break;
764 default:
Daniel Veillardf012a642001-07-23 19:10:52 +0000765 xmlGenericError( xmlGenericErrorContext,
766 "xmlNanoHTTPConnectAttempt: %s - %s",
767 "error connecting to HTTP server",
768 strerror( socket_errno( ) ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000769 closesocket(s);
770 return(-1);
771 }
772 }
773
774 tv.tv_sec = timeout;
775 tv.tv_usec = 0;
776
777 FD_ZERO(&wfd);
778 FD_SET(s, &wfd);
779
780 switch(select(s+1, NULL, &wfd, NULL, &tv))
781 {
782 case 0:
783 /* Time out */
Daniel Veillardf012a642001-07-23 19:10:52 +0000784 xmlGenericError( xmlGenericErrorContext,
785 "xmlNanoHTTPConnectAttempt: %s",
786 "Connect attempt timed out." );
Owen Taylor3473f882001-02-23 17:55:21 +0000787 closesocket(s);
788 return(-1);
789 case -1:
790 /* Ermm.. ?? */
Daniel Veillardf012a642001-07-23 19:10:52 +0000791 xmlGenericError( xmlGenericErrorContext,
792 "xmlNanoHTTPConnectAttempt: %s - %s",
793 "Error connecting to host",
794 strerror( socket_errno( ) ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000795 closesocket(s);
796 return(-1);
797 }
798
799 if ( FD_ISSET(s, &wfd) ) {
800 SOCKLEN_T len;
801 len = sizeof(status);
802 if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&status, &len) < 0 ) {
803 /* Solaris error code */
Daniel Veillardf012a642001-07-23 19:10:52 +0000804 xmlGenericError( xmlGenericErrorContext,
805 "xmlNanoHTTPConnectAttempt: %s - %s",
806 "Error retrieving pending socket errors",
807 strerror( socket_errno( ) ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000808 return (-1);
809 }
810 if ( status ) {
811 closesocket(s);
812 errno = status;
Daniel Veillardf012a642001-07-23 19:10:52 +0000813 xmlGenericError( xmlGenericErrorContext,
814 "xmlNanoHTTPConnectAttempt: %s - %s",
815 "Error connecting to remote host",
816 strerror( status ) );
Owen Taylor3473f882001-02-23 17:55:21 +0000817 return (-1);
818 }
819 } else {
820 /* pbm */
Daniel Veillardf012a642001-07-23 19:10:52 +0000821 xmlGenericError( xmlGenericErrorContext,
822 "xmlNanoHTTPConnectAttempt: %s\n",
823 "Select returned, but descriptor not set for connection.\n" );
824 closesocket(s);
Owen Taylor3473f882001-02-23 17:55:21 +0000825 return (-1);
826 }
827
828 return(s);
829}
830
831/**
832 * xmlNanoHTTPConnectHost:
833 * @host: the host name
834 * @port: the port number
835 *
836 * Attempt a connection to the given host:port endpoint. It tries
837 * the multiple IP provided by the DNS if available.
838 *
839 * Returns -1 in case of failure, the file descriptor number otherwise
840 */
841
842static int
843xmlNanoHTTPConnectHost(const char *host, int port)
844{
845 struct hostent *h;
846 struct sockaddr *addr;
847 struct in_addr ia;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000848 struct sockaddr_in sockin;
Owen Taylor3473f882001-02-23 17:55:21 +0000849#ifdef SUPPORT_IP6
850 struct in6_addr ia6;
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000851 struct sockaddr_in6 sockin6;
Owen Taylor3473f882001-02-23 17:55:21 +0000852#endif
853 int i;
854 int s;
855
856#if defined(SUPPORT_IP6) && defined(RES_USE_INET6)
857 if (!(_res.options & RES_INIT))
858 res_init();
859 _res.options |= RES_USE_INET6;
860#endif
861 h=gethostbyname(host);
862 if (h==NULL)
863 {
Daniel Veillardf012a642001-07-23 19:10:52 +0000864 const char * h_err_txt = "";
865 switch ( h_errno )
866 {
867 case HOST_NOT_FOUND:
868 h_err_txt = "Authoritive host not found";
869 break;
870
871 case TRY_AGAIN:
872 h_err_txt =
873 "Non-authoritive host not found or server failure.";
874 break;
875
876 case NO_RECOVERY:
877 h_err_txt =
878 "Non-recoverable errors: FORMERR, REFUSED, or NOTIMP.";
879 break;
880
881 case NO_ADDRESS:
882 h_err_txt = "Valid name, no data record of requested type.";
883 break;
884
885 default:
886 h_err_txt = "No error text defined.";
887 break;
888 }
889 xmlGenericError( xmlGenericErrorContext,
890 "xmlNanoHTTPConnectHost: %s '%s' - %s",
891 "Failed to resolve host", host, h_err_txt );
Owen Taylor3473f882001-02-23 17:55:21 +0000892 return(-1);
893 }
894
895 for(i=0; h->h_addr_list[i]; i++)
896 {
897 if (h->h_addrtype == AF_INET) {
898 /* A records (IPv4) */
899 memcpy(&ia, h->h_addr_list[i], h->h_length);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000900 sockin.sin_family = h->h_addrtype;
901 sockin.sin_addr = ia;
902 sockin.sin_port = htons(port);
903 addr = (struct sockaddr *)&sockin;
Owen Taylor3473f882001-02-23 17:55:21 +0000904#ifdef SUPPORT_IP6
905 } else if (h->h_addrtype == AF_INET6) {
906 /* AAAA records (IPv6) */
907 memcpy(&ia6, h->h_addr_list[i], h->h_length);
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000908 sockin6.sin_family = h->h_addrtype;
909 sockin6.sin_addr = ia6;
910 sockin6.sin_port = htons(port);
911 addr = (struct sockaddr *)&sockin6;
Owen Taylor3473f882001-02-23 17:55:21 +0000912#endif
913 } else
914 break; /* for */
915
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000916 s = xmlNanoHTTPConnectAttempt(addr);
Owen Taylor3473f882001-02-23 17:55:21 +0000917 if (s != -1)
918 return(s);
919 }
920
921#ifdef DEBUG_HTTP
922 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf012a642001-07-23 19:10:52 +0000923 "xmlNanoHTTPConnectHost: unable to connect to '%s'.\n", host);
Owen Taylor3473f882001-02-23 17:55:21 +0000924#endif
925 return(-1);
926}
927
928
929/**
930 * xmlNanoHTTPOpen:
931 * @URL: The URL to load
932 * @contentType: if available the Content-Type information will be
933 * returned at that location
934 *
935 * This function try to open a connection to the indicated resource
936 * via HTTP GET.
937 *
938 * Returns NULL in case of failure, otherwise a request handler.
939 * The contentType, if provided must be freed by the caller
940 */
941
942void*
943xmlNanoHTTPOpen(const char *URL, char **contentType) {
944 if (contentType != NULL) *contentType = NULL;
Daniel Veillardf012a642001-07-23 19:10:52 +0000945 return(xmlNanoHTTPMethod(URL, NULL, NULL, contentType, NULL, 0));
Daniel Veillard9403a042001-05-28 11:00:53 +0000946}
947
948/**
949 * xmlNanoHTTPOpenRedir:
950 * @URL: The URL to load
951 * @contentType: if available the Content-Type information will be
952 * returned at that location
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000953 * @redir: if available the redirected URL will be returned
Daniel Veillard9403a042001-05-28 11:00:53 +0000954 *
955 * This function try to open a connection to the indicated resource
956 * via HTTP GET.
957 *
958 * Returns NULL in case of failure, otherwise a request handler.
959 * The contentType, if provided must be freed by the caller
960 */
961
962void*
963xmlNanoHTTPOpenRedir(const char *URL, char **contentType, char **redir) {
964 if (contentType != NULL) *contentType = NULL;
965 if (redir != NULL) *redir = NULL;
Daniel Veillardf012a642001-07-23 19:10:52 +0000966 return(xmlNanoHTTPMethodRedir(URL, NULL, NULL, contentType, redir, NULL,0));
Owen Taylor3473f882001-02-23 17:55:21 +0000967}
968
969/**
970 * xmlNanoHTTPRead:
971 * @ctx: the HTTP context
972 * @dest: a buffer
973 * @len: the buffer length
974 *
975 * This function tries to read @len bytes from the existing HTTP connection
976 * and saves them in @dest. This is a blocking call.
977 *
978 * Returns the number of byte read. 0 is an indication of an end of connection.
979 * -1 indicates a parameter error.
980 */
981int
982xmlNanoHTTPRead(void *ctx, void *dest, int len) {
983 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
984
985 if (ctx == NULL) return(-1);
986 if (dest == NULL) return(-1);
987 if (len <= 0) return(0);
988
989 while (ctxt->inptr - ctxt->inrptr < len) {
Daniel Veillardf012a642001-07-23 19:10:52 +0000990 if (xmlNanoHTTPRecv(ctxt) <= 0) break;
Owen Taylor3473f882001-02-23 17:55:21 +0000991 }
992 if (ctxt->inptr - ctxt->inrptr < len)
993 len = ctxt->inptr - ctxt->inrptr;
994 memcpy(dest, ctxt->inrptr, len);
995 ctxt->inrptr += len;
996 return(len);
997}
998
999/**
1000 * xmlNanoHTTPClose:
1001 * @ctx: the HTTP context
1002 *
1003 * This function closes an HTTP context, it ends up the connection and
1004 * free all data related to it.
1005 */
1006void
1007xmlNanoHTTPClose(void *ctx) {
1008 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1009
1010 if (ctx == NULL) return;
1011
1012 xmlNanoHTTPFreeCtxt(ctxt);
1013}
1014
1015/**
Daniel Veillard9403a042001-05-28 11:00:53 +00001016 * xmlNanoHTTPMethodRedir:
Owen Taylor3473f882001-02-23 17:55:21 +00001017 * @URL: The URL to load
1018 * @method: the HTTP method to use
1019 * @input: the input string if any
1020 * @contentType: the Content-Type information IN and OUT
Daniel Veillard9403a042001-05-28 11:00:53 +00001021 * @redir: the redirected URL OUT
Owen Taylor3473f882001-02-23 17:55:21 +00001022 * @headers: the extra headers
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001023 * @ilen: input length
Owen Taylor3473f882001-02-23 17:55:21 +00001024 *
1025 * This function try to open a connection to the indicated resource
1026 * via HTTP using the given @method, adding the given extra headers
1027 * and the input buffer for the request content.
1028 *
1029 * Returns NULL in case of failure, otherwise a request handler.
Daniel Veillard9403a042001-05-28 11:00:53 +00001030 * The contentType, or redir, if provided must be freed by the caller
Owen Taylor3473f882001-02-23 17:55:21 +00001031 */
1032
1033void*
Daniel Veillard9403a042001-05-28 11:00:53 +00001034xmlNanoHTTPMethodRedir(const char *URL, const char *method, const char *input,
Daniel Veillardf012a642001-07-23 19:10:52 +00001035 char **contentType, char **redir,
1036 const char *headers, int ilen ) {
Owen Taylor3473f882001-02-23 17:55:21 +00001037 xmlNanoHTTPCtxtPtr ctxt;
1038 char *bp, *p;
Daniel Veillardf012a642001-07-23 19:10:52 +00001039 int blen, ret;
Owen Taylor3473f882001-02-23 17:55:21 +00001040 int head;
Daniel Veillardf012a642001-07-23 19:10:52 +00001041 int xmt_bytes;
Owen Taylor3473f882001-02-23 17:55:21 +00001042 int nbRedirects = 0;
1043 char *redirURL = NULL;
1044
1045 if (URL == NULL) return(NULL);
1046 if (method == NULL) method = "GET";
1047 xmlNanoHTTPInit();
1048
1049retry:
1050 if (redirURL == NULL)
1051 ctxt = xmlNanoHTTPNewCtxt(URL);
1052 else {
1053 ctxt = xmlNanoHTTPNewCtxt(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001054 }
1055
Daniel Veillardf012a642001-07-23 19:10:52 +00001056 if ( ctxt == NULL ) {
1057 xmlGenericError( xmlGenericErrorContext,
1058 "xmlNanoHTTPMethodRedir: %s %s.",
1059 "Unable to allocate HTTP context to URI",
1060 ( ( redirURL == NULL ) ? URL : redirURL ) );
1061 return ( NULL );
1062 }
1063
Owen Taylor3473f882001-02-23 17:55:21 +00001064 if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001065 xmlGenericError( xmlGenericErrorContext,
1066 "xmlNanoHTTPMethodRedir: %s - %s.",
1067 "Not a valid HTTP URI",
1068 ( ( redirURL == NULL ) ? URL : redirURL ) );
Owen Taylor3473f882001-02-23 17:55:21 +00001069 xmlNanoHTTPFreeCtxt(ctxt);
1070 if (redirURL != NULL) xmlFree(redirURL);
1071 return(NULL);
1072 }
1073 if (ctxt->hostname == NULL) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001074 xmlGenericError( xmlGenericErrorContext,
1075 "xmlNanoHTTPMethodRedir: %s - %s",
1076 "Failed to identify host in URI",
1077 ( ( redirURL == NULL ) ? URL : redirURL ) );
Owen Taylor3473f882001-02-23 17:55:21 +00001078 xmlNanoHTTPFreeCtxt(ctxt);
Daniel Veillard9403a042001-05-28 11:00:53 +00001079 if (redirURL != NULL) xmlFree(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001080 return(NULL);
1081 }
1082 if (proxy) {
1083 blen = strlen(ctxt->hostname) * 2 + 16;
1084 ret = xmlNanoHTTPConnectHost(proxy, proxyPort);
1085 }
1086 else {
1087 blen = strlen(ctxt->hostname);
1088 ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
1089 }
1090 if (ret < 0) {
1091 xmlNanoHTTPFreeCtxt(ctxt);
Daniel Veillard9403a042001-05-28 11:00:53 +00001092 if (redirURL != NULL) xmlFree(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001093 return(NULL);
1094 }
1095 ctxt->fd = ret;
1096
Daniel Veillardf012a642001-07-23 19:10:52 +00001097 if (input == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00001098 ilen = 0;
Daniel Veillardf012a642001-07-23 19:10:52 +00001099 else
1100 blen += 36;
1101
Owen Taylor3473f882001-02-23 17:55:21 +00001102 if (headers != NULL)
Daniel Veillardf012a642001-07-23 19:10:52 +00001103 blen += strlen(headers) + 2;
Owen Taylor3473f882001-02-23 17:55:21 +00001104 if (contentType && *contentType)
1105 blen += strlen(*contentType) + 16;
Daniel Veillardf012a642001-07-23 19:10:52 +00001106 blen += strlen(method) + strlen(ctxt->path) + 24;
Owen Taylor3473f882001-02-23 17:55:21 +00001107 bp = xmlMalloc(blen);
Daniel Veillardf012a642001-07-23 19:10:52 +00001108 if ( bp == NULL ) {
1109 xmlNanoHTTPFreeCtxt( ctxt );
1110 xmlGenericError( xmlGenericErrorContext,
1111 "xmlNanoHTTPMethodRedir: %s",
1112 "Error allocating HTTP header buffer." );
1113 return ( NULL );
1114 }
1115
1116 p = bp;
1117
Owen Taylor3473f882001-02-23 17:55:21 +00001118 if (proxy) {
1119 if (ctxt->port != 80) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001120 p += sprintf( p, "%s http://%s:%d%s", method, ctxt->hostname,
1121 ctxt->port, ctxt->path );
Owen Taylor3473f882001-02-23 17:55:21 +00001122 }
1123 else
Daniel Veillardf012a642001-07-23 19:10:52 +00001124 p += sprintf( p, "%s http://%s%s", method,
1125 ctxt->hostname, ctxt->path);
Owen Taylor3473f882001-02-23 17:55:21 +00001126 }
1127 else
Daniel Veillardf012a642001-07-23 19:10:52 +00001128 p += sprintf( p, "%s %s", method, ctxt->path);
1129
1130 p += sprintf(p, " HTTP/1.0\r\nHost: %s\r\n", ctxt->hostname);
1131
1132 if (contentType != NULL && *contentType)
1133 p += sprintf(p, "Content-Type: %s\r\n", *contentType);
1134
1135 if (headers != NULL)
1136 p += sprintf( p, "%s", headers );
1137
Owen Taylor3473f882001-02-23 17:55:21 +00001138 if (input != NULL)
Daniel Veillardf012a642001-07-23 19:10:52 +00001139 sprintf(p, "Content-Length: %d\r\n\r\n", ilen );
Owen Taylor3473f882001-02-23 17:55:21 +00001140 else
1141 strcpy(p, "\r\n");
Daniel Veillardf012a642001-07-23 19:10:52 +00001142
Owen Taylor3473f882001-02-23 17:55:21 +00001143#ifdef DEBUG_HTTP
1144 xmlGenericError(xmlGenericErrorContext,
1145 "-> %s%s", proxy? "(Proxy) " : "", bp);
1146 if ((blen -= strlen(bp)+1) < 0)
1147 xmlGenericError(xmlGenericErrorContext,
1148 "ERROR: overflowed buffer by %d bytes\n", -blen);
1149#endif
1150 ctxt->outptr = ctxt->out = bp;
1151 ctxt->state = XML_NANO_HTTP_WRITE;
Daniel Veillardf012a642001-07-23 19:10:52 +00001152 blen = strlen( ctxt->out );
1153 xmt_bytes = xmlNanoHTTPSend(ctxt, ctxt->out, blen );
1154#ifdef DEBUG_HTTP
1155 if ( xmt_bytes != blen )
1156 xmlGenericError( xmlGenericErrorContext,
1157 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
1158 xmt_bytes, blen,
1159 "bytes of HTTP headers sent to host",
1160 ctxt->hostname );
1161#endif
1162
1163 if ( input != NULL ) {
1164 xmt_bytes = xmlNanoHTTPSend( ctxt, input, ilen );
1165
1166#ifdef DEBUG_HTTP
1167 if ( xmt_bytes != ilen )
1168 xmlGenericError( xmlGenericErrorContext,
1169 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n",
1170 xmt_bytes, ilen,
1171 "bytes of HTTP content sent to host",
1172 ctxt->hostname );
1173#endif
1174 }
1175
Owen Taylor3473f882001-02-23 17:55:21 +00001176 ctxt->state = XML_NANO_HTTP_READ;
1177 head = 1;
1178
1179 while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
1180 if (head && (*p == 0)) {
1181 head = 0;
1182 ctxt->content = ctxt->inrptr;
1183 xmlFree(p);
1184 break;
1185 }
1186 xmlNanoHTTPScanAnswer(ctxt, p);
1187
1188#ifdef DEBUG_HTTP
1189 xmlGenericError(xmlGenericErrorContext, "<- %s\n", p);
1190#endif
1191 xmlFree(p);
1192 }
1193
1194 if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
1195 (ctxt->returnValue < 400)) {
1196#ifdef DEBUG_HTTP
1197 xmlGenericError(xmlGenericErrorContext,
1198 "\nRedirect to: %s\n", ctxt->location);
1199#endif
Daniel Veillardf012a642001-07-23 19:10:52 +00001200 while ( xmlNanoHTTPRecv(ctxt) > 0 ) ;
Owen Taylor3473f882001-02-23 17:55:21 +00001201 if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
1202 nbRedirects++;
Daniel Veillard9403a042001-05-28 11:00:53 +00001203 if (redirURL != NULL)
1204 xmlFree(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001205 redirURL = xmlMemStrdup(ctxt->location);
1206 xmlNanoHTTPFreeCtxt(ctxt);
1207 goto retry;
1208 }
1209 xmlNanoHTTPFreeCtxt(ctxt);
Daniel Veillard9403a042001-05-28 11:00:53 +00001210 if (redirURL != NULL) xmlFree(redirURL);
Owen Taylor3473f882001-02-23 17:55:21 +00001211#ifdef DEBUG_HTTP
1212 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardf012a642001-07-23 19:10:52 +00001213 "xmlNanoHTTPMethodRedir: Too many redirects, aborting ...\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001214#endif
1215 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001216 }
1217
1218 if (contentType != NULL) {
1219 if (ctxt->contentType != NULL)
1220 *contentType = xmlMemStrdup(ctxt->contentType);
1221 else
1222 *contentType = NULL;
1223 }
1224
Daniel Veillard9403a042001-05-28 11:00:53 +00001225 if ((redir != NULL) && (redirURL != NULL)) {
1226 *redir = redirURL;
1227 } else {
1228 if (redirURL != NULL)
1229 xmlFree(redirURL);
1230 if (redir != NULL)
1231 *redir = NULL;
1232 }
1233
Owen Taylor3473f882001-02-23 17:55:21 +00001234#ifdef DEBUG_HTTP
1235 if (ctxt->contentType != NULL)
1236 xmlGenericError(xmlGenericErrorContext,
1237 "\nCode %d, content-type '%s'\n\n",
1238 ctxt->returnValue, ctxt->contentType);
1239 else
1240 xmlGenericError(xmlGenericErrorContext,
1241 "\nCode %d, no content-type\n\n",
1242 ctxt->returnValue);
1243#endif
1244
1245 return((void *) ctxt);
1246}
1247
1248/**
Daniel Veillard9403a042001-05-28 11:00:53 +00001249 * xmlNanoHTTPMethod:
1250 * @URL: The URL to load
1251 * @method: the HTTP method to use
1252 * @input: the input string if any
1253 * @contentType: the Content-Type information IN and OUT
1254 * @headers: the extra headers
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001255 * @ilen: input length
Daniel Veillard9403a042001-05-28 11:00:53 +00001256 *
1257 * This function try to open a connection to the indicated resource
1258 * via HTTP using the given @method, adding the given extra headers
1259 * and the input buffer for the request content.
1260 *
1261 * Returns NULL in case of failure, otherwise a request handler.
1262 * The contentType, if provided must be freed by the caller
1263 */
1264
1265void*
1266xmlNanoHTTPMethod(const char *URL, const char *method, const char *input,
Daniel Veillardf012a642001-07-23 19:10:52 +00001267 char **contentType, const char *headers, int ilen) {
Daniel Veillard9403a042001-05-28 11:00:53 +00001268 return(xmlNanoHTTPMethodRedir(URL, method, input, contentType,
Daniel Veillardf012a642001-07-23 19:10:52 +00001269 NULL, headers, ilen));
Daniel Veillard9403a042001-05-28 11:00:53 +00001270}
1271
1272/**
Owen Taylor3473f882001-02-23 17:55:21 +00001273 * xmlNanoHTTPFetch:
1274 * @URL: The URL to load
1275 * @filename: the filename where the content should be saved
1276 * @contentType: if available the Content-Type information will be
1277 * returned at that location
1278 *
1279 * This function try to fetch the indicated resource via HTTP GET
1280 * and save it's content in the file.
1281 *
1282 * Returns -1 in case of failure, 0 incase of success. The contentType,
1283 * if provided must be freed by the caller
1284 */
1285int
1286xmlNanoHTTPFetch(const char *URL, const char *filename, char **contentType) {
Daniel Veillardf012a642001-07-23 19:10:52 +00001287 void *ctxt = NULL;
1288 char *buf = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001289 int fd;
1290 int len;
1291
1292 ctxt = xmlNanoHTTPOpen(URL, contentType);
1293 if (ctxt == NULL) return(-1);
1294
1295 if (!strcmp(filename, "-"))
1296 fd = 0;
1297 else {
1298 fd = open(filename, O_CREAT | O_WRONLY, 00644);
1299 if (fd < 0) {
1300 xmlNanoHTTPClose(ctxt);
1301 if ((contentType != NULL) && (*contentType != NULL)) {
1302 xmlFree(*contentType);
1303 *contentType = NULL;
1304 }
1305 return(-1);
1306 }
1307 }
1308
Daniel Veillardf012a642001-07-23 19:10:52 +00001309 xmlNanoHTTPFetchContent( ctxt, &buf, &len );
1310 if ( len > 0 ) {
Owen Taylor3473f882001-02-23 17:55:21 +00001311 write(fd, buf, len);
1312 }
1313
1314 xmlNanoHTTPClose(ctxt);
1315 close(fd);
1316 return(0);
1317}
1318
1319/**
1320 * xmlNanoHTTPSave:
1321 * @ctxt: the HTTP context
1322 * @filename: the filename where the content should be saved
1323 *
1324 * This function saves the output of the HTTP transaction to a file
1325 * It closes and free the context at the end
1326 *
1327 * Returns -1 in case of failure, 0 incase of success.
1328 */
1329int
1330xmlNanoHTTPSave(void *ctxt, const char *filename) {
Daniel Veillarde3924972001-07-25 20:25:21 +00001331 char *buf = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001332 int fd;
1333 int len;
1334
1335 if (ctxt == NULL) return(-1);
1336
1337 if (!strcmp(filename, "-"))
1338 fd = 0;
1339 else {
1340 fd = open(filename, O_CREAT | O_WRONLY);
1341 if (fd < 0) {
1342 xmlNanoHTTPClose(ctxt);
1343 return(-1);
1344 }
1345 }
1346
Daniel Veillardf012a642001-07-23 19:10:52 +00001347 xmlNanoHTTPFetchContent( ctxt, &buf, &len );
1348 if ( len > 0 ) {
Owen Taylor3473f882001-02-23 17:55:21 +00001349 write(fd, buf, len);
1350 }
1351
1352 xmlNanoHTTPClose(ctxt);
1353 return(0);
1354}
1355
1356/**
1357 * xmlNanoHTTPReturnCode:
1358 * @ctx: the HTTP context
1359 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001360 * Get the latest HTTP return code received
1361 *
Owen Taylor3473f882001-02-23 17:55:21 +00001362 * Returns the HTTP return code for the request.
1363 */
1364int
1365xmlNanoHTTPReturnCode(void *ctx) {
1366 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1367
1368 if (ctxt == NULL) return(-1);
1369
1370 return(ctxt->returnValue);
1371}
1372
1373/**
1374 * xmlNanoHTTPAuthHeader:
1375 * @ctx: the HTTP context
1376 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +00001377 * Get the authentication header of an HTTP context
1378 *
Owen Taylor3473f882001-02-23 17:55:21 +00001379 * Returns the stashed value of the WWW-Authenticate or Proxy-Authenticate
1380 * header.
1381 */
1382const char *
1383xmlNanoHTTPAuthHeader(void *ctx) {
1384 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
1385
1386 if (ctxt == NULL) return(NULL);
1387
1388 return(ctxt->authHeader);
1389}
1390
Daniel Veillardf012a642001-07-23 19:10:52 +00001391/**
1392 * xmlNanoHTTPContentLength
1393 * @ctx: the HTTP context
1394 *
1395 * Return the specified content length from the HTTP header. Note that
1396 * a value of -1 indicates that the content length element was not included in
1397 * the response header.
1398 */
1399int
1400xmlNanoHTTPContentLength( void * ctx ) {
1401 xmlNanoHTTPCtxtPtr ctxt = ctx;
1402
1403 return ( ( ctxt == NULL ) ? -1 : ctxt->ContentLength );
1404}
1405
1406/**
1407 * xmlNanoHTTPFetchContent
1408 * @ctx: the HTTP context
1409 * @ptr: pointer to set to the content buffer.
1410 * @len: integer pointer to hold the length of the content
1411 *
1412 * Returns 0 if all the content was read and available, returns
1413 * -1 if received content length was less than specified or an error
1414 * occurred.
1415 */
1416int
1417xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len ) {
1418 xmlNanoHTTPCtxtPtr ctxt = ctx;
1419
1420 int rc = 0;
1421 int cur_lgth;
1422 int rcvd_lgth;
1423 int dummy_int;
1424 char * dummy_ptr = NULL;
1425
1426 /* Dummy up return input parameters if not provided */
1427
1428 if ( len == NULL )
1429 len = &dummy_int;
1430
1431 if ( ptr == NULL )
1432 ptr = &dummy_ptr;
1433
1434 /* But can't work without the context pointer */
1435
1436 if ( ( ctxt == NULL ) || ( ctxt->content == NULL ) ) {
1437 *len = 0;
1438 *ptr = NULL;
1439 return ( -1 );
1440 }
1441
1442 rcvd_lgth = ctxt->inptr - ctxt->content;
1443
1444 while ( (cur_lgth = xmlNanoHTTPRecv( ctxt )) > 0 ) {
1445
1446 rcvd_lgth += cur_lgth;
1447 if ( (ctxt->ContentLength > 0) && (rcvd_lgth >= ctxt->ContentLength) )
1448 break;
1449 }
1450
1451 *ptr = ctxt->content;
1452 *len = rcvd_lgth;
1453
1454 if ( ( ctxt->ContentLength > 0 ) && ( rcvd_lgth < ctxt->ContentLength ) )
1455 rc = -1;
1456 else if ( rcvd_lgth == 0 )
1457 rc = -1;
1458
1459 return ( rc );
1460}
1461
Owen Taylor3473f882001-02-23 17:55:21 +00001462#ifdef STANDALONE
1463int main(int argc, char **argv) {
1464 char *contentType = NULL;
1465
1466 if (argv[1] != NULL) {
1467 if (argv[2] != NULL)
1468 xmlNanoHTTPFetch(argv[1], argv[2], &contentType);
1469 else
1470 xmlNanoHTTPFetch(argv[1], "-", &contentType);
1471 if (contentType != NULL) xmlFree(contentType);
1472 } else {
1473 xmlGenericError(xmlGenericErrorContext,
1474 "%s: minimal HTTP GET implementation\n", argv[0]);
1475 xmlGenericError(xmlGenericErrorContext,
1476 "\tusage %s [ URL [ filename ] ]\n", argv[0]);
1477 }
1478 xmlNanoHTTPCleanup();
1479 xmlMemoryDump();
1480 return(0);
1481}
1482#endif /* STANDALONE */
1483#else /* !LIBXML_HTTP_ENABLED */
1484#ifdef STANDALONE
1485#include <stdio.h>
1486int main(int argc, char **argv) {
1487 xmlGenericError(xmlGenericErrorContext,
1488 "%s : HTTP support not compiled in\n", argv[0]);
1489 return(0);
1490}
1491#endif /* STANDALONE */
1492#endif /* LIBXML_HTTP_ENABLED */