Paul Cercueil / libxml2

Dependents:   libiio

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers nanohttp.c Source File

nanohttp.c

00001 /*
00002  * nanohttp.c: minimalist HTTP GET implementation to fetch external subsets.
00003  *             focuses on size, streamability, reentrancy and portability
00004  *
00005  * This is clearly not a general purpose HTTP implementation
00006  * If you look for one, check:
00007  *         http://www.w3.org/Library/
00008  *
00009  * See Copyright for the status of this software.
00010  *
00011  * daniel@veillard.com
00012  */
00013 
00014 #define NEED_SOCKETS
00015 #define IN_LIBXML
00016 #include "libxml.h"
00017 
00018 #ifdef LIBXML_HTTP_ENABLED
00019 #include <string.h>
00020 
00021 #ifdef HAVE_STDLIB_H
00022 #include <stdlib.h>
00023 #endif
00024 #ifdef HAVE_UNISTD_H
00025 #include <unistd.h>
00026 #endif
00027 #ifdef HAVE_SYS_TYPES_H
00028 #include <sys/types.h>
00029 #endif
00030 #ifdef HAVE_SYS_SOCKET_H
00031 #include <sys/socket.h>
00032 #endif
00033 #ifdef HAVE_NETINET_IN_H
00034 #include <netinet/in.h>
00035 #endif
00036 #ifdef HAVE_ARPA_INET_H
00037 #include <arpa/inet.h>
00038 #endif
00039 #ifdef HAVE_NETDB_H
00040 #include <netdb.h>
00041 #endif
00042 #ifdef HAVE_RESOLV_H
00043 #ifdef HAVE_ARPA_NAMESER_H
00044 #include <arpa/nameser.h>
00045 #endif
00046 #include <resolv.h>
00047 #endif
00048 #ifdef HAVE_FCNTL_H
00049 #include <fcntl.h>
00050 #endif
00051 #ifdef HAVE_ERRNO_H
00052 #include <errno.h>
00053 #endif
00054 #ifdef HAVE_SYS_TIME_H
00055 #include <sys/time.h>
00056 #endif
00057 #ifndef HAVE_POLL_H
00058 #ifdef HAVE_SYS_SELECT_H
00059 #include <sys/select.h>
00060 #endif
00061 #else
00062 #include <poll.h>
00063 #endif
00064 #ifdef HAVE_STRINGS_H
00065 #include <strings.h>
00066 #endif
00067 #ifdef HAVE_ZLIB_H
00068 #include <zlib.h>
00069 #endif
00070 
00071 
00072 #ifdef VMS
00073 #include <stropts>
00074 #define XML_SOCKLEN_T unsigned int
00075 #endif
00076 
00077 #if defined(__MINGW32__) || defined(_WIN32_WCE)
00078 #ifndef _WINSOCKAPI_
00079 #define _WINSOCKAPI_
00080 #endif
00081 #include <wsockcompat.h>
00082 #include <winsock2.h>
00083 #undef XML_SOCKLEN_T
00084 #define XML_SOCKLEN_T unsigned int
00085 #endif
00086 
00087 #include <libxml/globals.h>
00088 #include <libxml/xmlerror.h>
00089 #include <libxml/xmlmemory.h>
00090 #include <libxml/parser.h> /* for xmlStr(n)casecmp() */
00091 #include <libxml/nanohttp.h>
00092 #include <libxml/globals.h>
00093 #include <libxml/uri.h>
00094 
00095 /**
00096  * A couple portability macros
00097  */
00098 #ifndef _WINSOCKAPI_
00099 #if !defined(__BEOS__) || defined(__HAIKU__)
00100 #define closesocket(s) close(s)
00101 #endif
00102 #define SOCKET int
00103 #define INVALID_SOCKET (-1)
00104 #endif
00105 
00106 #ifdef __BEOS__
00107 #ifndef PF_INET
00108 #define PF_INET AF_INET
00109 #endif
00110 #endif
00111 
00112 #ifndef XML_SOCKLEN_T
00113 #define XML_SOCKLEN_T unsigned int
00114 #endif
00115 
00116 #ifdef STANDALONE
00117 #define DEBUG_HTTP
00118 #define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n)
00119 #define xmlStrcasecmpi(a, b) strcasecmp((char *)a, (char *)b)
00120 #endif
00121 
00122 #define XML_NANO_HTTP_MAX_REDIR 10
00123 
00124 #define XML_NANO_HTTP_CHUNK 4096
00125 
00126 #define XML_NANO_HTTP_CLOSED    0
00127 #define XML_NANO_HTTP_WRITE 1
00128 #define XML_NANO_HTTP_READ  2
00129 #define XML_NANO_HTTP_NONE  4
00130 
00131 typedef struct xmlNanoHTTPCtxt {
00132     char *protocol; /* the protocol name */
00133     char *hostname; /* the host name */
00134     int port;       /* the port */
00135     char *path;     /* the path within the URL */
00136     char *query;    /* the query string */
00137     SOCKET fd;      /* the file descriptor for the socket */
00138     int state;      /* WRITE / READ / CLOSED */
00139     char *out;      /* buffer sent (zero terminated) */
00140     char *outptr;   /* index within the buffer sent */
00141     char *in;       /* the receiving buffer */
00142     char *content;  /* the start of the content */
00143     char *inptr;    /* the next byte to read from network */
00144     char *inrptr;   /* the next byte to give back to the client */
00145     int inlen;      /* len of the input buffer */
00146     int last;       /* return code for last operation */
00147     int returnValue;    /* the protocol return value */
00148     int version;        /* the protocol version */
00149     int ContentLength;  /* specified content length from HTTP header */
00150     char *contentType;  /* the MIME type for the input */
00151     char *location; /* the new URL in case of redirect */
00152     char *authHeader;   /* contents of {WWW,Proxy}-Authenticate header */
00153     char *encoding; /* encoding extracted from the contentType */
00154     char *mimeType; /* Mime-Type extracted from the contentType */
00155 #ifdef HAVE_ZLIB_H
00156     z_stream *strm; /* Zlib stream object */
00157     int usesGzip;   /* "Content-Encoding: gzip" was detected */
00158 #endif
00159 } xmlNanoHTTPCtxt, *xmlNanoHTTPCtxtPtr;
00160 
00161 static int initialized = 0;
00162 static char *proxy = NULL;   /* the proxy name if any */
00163 static int proxyPort;   /* the proxy port if any */
00164 static unsigned int timeout = 60;/* the select() timeout in seconds */
00165 
00166 static int xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len );
00167 
00168 /**
00169  * xmlHTTPErrMemory:
00170  * @extra:  extra informations
00171  *
00172  * Handle an out of memory condition
00173  */
00174 static void
00175 xmlHTTPErrMemory(const char *extra)
00176 {
00177     __xmlSimpleError(XML_FROM_HTTP, XML_ERR_NO_MEMORY, NULL, NULL, extra);
00178 }
00179 
00180 /**
00181  * A portability function
00182  */
00183 static int socket_errno(void) {
00184 #ifdef _WINSOCKAPI_
00185     return(WSAGetLastError());
00186 #else
00187     return(errno);
00188 #endif
00189 }
00190 
00191 #ifdef SUPPORT_IP6
00192 static
00193 int have_ipv6(void) {
00194     SOCKET s;
00195 
00196     s = socket (AF_INET6, SOCK_STREAM, 0);
00197     if (s != INVALID_SOCKET) {
00198     close (s);
00199     return (1);
00200     }
00201     return (0);
00202 }
00203 #endif
00204 
00205 /**
00206  * xmlNanoHTTPInit:
00207  *
00208  * Initialize the HTTP protocol layer.
00209  * Currently it just checks for proxy informations
00210  */
00211 
00212 void
00213 xmlNanoHTTPInit(void) {
00214     const char *env;
00215 #ifdef _WINSOCKAPI_
00216     WSADATA wsaData;
00217 #endif
00218 
00219     if (initialized)
00220     return;
00221 
00222 #ifdef _WINSOCKAPI_
00223     if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
00224     return;
00225 #endif
00226 
00227     if (proxy == NULL) {
00228     proxyPort = 80;
00229     env = getenv("no_proxy");
00230     if (env && ((env[0] == '*') && (env[1] == 0)))
00231         goto done;
00232     env = getenv("http_proxy");
00233     if (env != NULL) {
00234         xmlNanoHTTPScanProxy(env);
00235         goto done;
00236     }
00237     env = getenv("HTTP_PROXY");
00238     if (env != NULL) {
00239         xmlNanoHTTPScanProxy(env);
00240         goto done;
00241     }
00242     }
00243 done:
00244     initialized = 1;
00245 }
00246 
00247 /**
00248  * xmlNanoHTTPCleanup:
00249  *
00250  * Cleanup the HTTP protocol layer.
00251  */
00252 
00253 void
00254 xmlNanoHTTPCleanup(void) {
00255     if (proxy != NULL) {
00256     xmlFree(proxy);
00257     proxy = NULL;
00258     }
00259 #ifdef _WINSOCKAPI_
00260     if (initialized)
00261     WSACleanup();
00262 #endif
00263     initialized = 0;
00264     return;
00265 }
00266 
00267 /**
00268  * xmlNanoHTTPScanURL:
00269  * @ctxt:  an HTTP context
00270  * @URL:  The URL used to initialize the context
00271  *
00272  * (Re)Initialize an HTTP context by parsing the URL and finding
00273  * the protocol host port and path it indicates.
00274  */
00275 
00276 static void
00277 xmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt, const char *URL) {
00278     xmlURIPtr uri;
00279     int len;
00280 
00281     /*
00282      * Clear any existing data from the context
00283      */
00284     if (ctxt->protocol != NULL) {
00285         xmlFree(ctxt->protocol);
00286     ctxt->protocol = NULL;
00287     }
00288     if (ctxt->hostname != NULL) {
00289         xmlFree(ctxt->hostname);
00290     ctxt->hostname = NULL;
00291     }
00292     if (ctxt->path != NULL) {
00293         xmlFree(ctxt->path);
00294     ctxt->path = NULL;
00295     }
00296     if (ctxt->query != NULL) {
00297         xmlFree(ctxt->query);
00298     ctxt->query = NULL;
00299     }
00300     if (URL == NULL) return;
00301 
00302     uri = xmlParseURIRaw(URL, 1);
00303     if (uri == NULL)
00304     return;
00305 
00306     if ((uri->scheme == NULL) || (uri->server == NULL)) {
00307     xmlFreeURI(uri);
00308     return;
00309     }
00310 
00311     ctxt->protocol = xmlMemStrdup(uri->scheme);
00312     /* special case of IPv6 addresses, the [] need to be removed */
00313     if ((uri->server != NULL) && (*uri->server == '[')) {
00314         len = strlen(uri->server);
00315     if ((len > 2) && (uri->server[len - 1] == ']')) {
00316         ctxt->hostname = (char *) xmlCharStrndup(uri->server + 1, len -2);
00317     } else
00318         ctxt->hostname = xmlMemStrdup(uri->server);
00319     } else
00320     ctxt->hostname = xmlMemStrdup(uri->server);
00321     if (uri->path != NULL)
00322     ctxt->path = xmlMemStrdup(uri->path);
00323     else
00324     ctxt->path = xmlMemStrdup("/");
00325     if (uri->query != NULL)
00326     ctxt->query = xmlMemStrdup(uri->query);
00327     if (uri->port != 0)
00328     ctxt->port = uri->port;
00329 
00330     xmlFreeURI(uri);
00331 }
00332 
00333 /**
00334  * xmlNanoHTTPScanProxy:
00335  * @URL:  The proxy URL used to initialize the proxy context
00336  *
00337  * (Re)Initialize the HTTP Proxy context by parsing the URL and finding
00338  * the protocol host port it indicates.
00339  * Should be like http://myproxy/ or http://myproxy:3128/
00340  * A NULL URL cleans up proxy informations.
00341  */
00342 
00343 void
00344 xmlNanoHTTPScanProxy(const char *URL) {
00345     xmlURIPtr uri;
00346 
00347     if (proxy != NULL) {
00348         xmlFree(proxy);
00349     proxy = NULL;
00350     }
00351     proxyPort = 0;
00352 
00353 #ifdef DEBUG_HTTP
00354     if (URL == NULL)
00355     xmlGenericError(xmlGenericErrorContext,
00356         "Removing HTTP proxy info\n");
00357     else
00358     xmlGenericError(xmlGenericErrorContext,
00359         "Using HTTP proxy %s\n", URL);
00360 #endif
00361     if (URL == NULL) return;
00362 
00363     uri = xmlParseURIRaw(URL, 1);
00364     if ((uri == NULL) || (uri->scheme == NULL) ||
00365     (strcmp(uri->scheme, "http")) || (uri->server == NULL)) {
00366     __xmlIOErr(XML_FROM_HTTP, XML_HTTP_URL_SYNTAX, "Syntax Error\n");
00367     if (uri != NULL)
00368         xmlFreeURI(uri);
00369     return;
00370     }
00371 
00372     proxy = xmlMemStrdup(uri->server);
00373     if (uri->port != 0)
00374     proxyPort = uri->port;
00375 
00376     xmlFreeURI(uri);
00377 }
00378 
00379 /**
00380  * xmlNanoHTTPNewCtxt:
00381  * @URL:  The URL used to initialize the context
00382  *
00383  * Allocate and initialize a new HTTP context.
00384  *
00385  * Returns an HTTP context or NULL in case of error.
00386  */
00387 
00388 static xmlNanoHTTPCtxtPtr
00389 xmlNanoHTTPNewCtxt(const char *URL) {
00390     xmlNanoHTTPCtxtPtr ret;
00391 
00392     ret = (xmlNanoHTTPCtxtPtr) xmlMalloc(sizeof(xmlNanoHTTPCtxt));
00393     if (ret == NULL) {
00394         xmlHTTPErrMemory("allocating context");
00395         return(NULL);
00396     }
00397 
00398     memset(ret, 0, sizeof(xmlNanoHTTPCtxt));
00399     ret->port = 80;
00400     ret->returnValue = 0;
00401     ret->fd = INVALID_SOCKET;
00402     ret->ContentLength = -1;
00403 
00404     xmlNanoHTTPScanURL(ret, URL);
00405 
00406     return(ret);
00407 }
00408 
00409 /**
00410  * xmlNanoHTTPFreeCtxt:
00411  * @ctxt:  an HTTP context
00412  *
00413  * Frees the context after closing the connection.
00414  */
00415 
00416 static void
00417 xmlNanoHTTPFreeCtxt(xmlNanoHTTPCtxtPtr ctxt) {
00418     if (ctxt == NULL) return;
00419     if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
00420     if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
00421     if (ctxt->path != NULL) xmlFree(ctxt->path);
00422     if (ctxt->query != NULL) xmlFree(ctxt->query);
00423     if (ctxt->out != NULL) xmlFree(ctxt->out);
00424     if (ctxt->in != NULL) xmlFree(ctxt->in);
00425     if (ctxt->contentType != NULL) xmlFree(ctxt->contentType);
00426     if (ctxt->encoding != NULL) xmlFree(ctxt->encoding);
00427     if (ctxt->mimeType != NULL) xmlFree(ctxt->mimeType);
00428     if (ctxt->location != NULL) xmlFree(ctxt->location);
00429     if (ctxt->authHeader != NULL) xmlFree(ctxt->authHeader);
00430 #ifdef HAVE_ZLIB_H
00431     if (ctxt->strm != NULL) {
00432     inflateEnd(ctxt->strm);
00433     xmlFree(ctxt->strm);
00434     }
00435 #endif
00436 
00437     ctxt->state = XML_NANO_HTTP_NONE;
00438     if (ctxt->fd != INVALID_SOCKET) closesocket(ctxt->fd);
00439     ctxt->fd = INVALID_SOCKET;
00440     xmlFree(ctxt);
00441 }
00442 
00443 /**
00444  * xmlNanoHTTPSend:
00445  * @ctxt:  an HTTP context
00446  *
00447  * Send the input needed to initiate the processing on the server side
00448  * Returns number of bytes sent or -1 on error.
00449  */
00450 
00451 static int
00452 xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt, const char *xmt_ptr, int outlen)
00453 {
00454     int total_sent = 0;
00455 #ifdef HAVE_POLL_H
00456     struct pollfd p;
00457 #else
00458     struct timeval tv;
00459     fd_set wfd;
00460 #endif
00461 
00462     if ((ctxt->state & XML_NANO_HTTP_WRITE) && (xmt_ptr != NULL)) {
00463         while (total_sent < outlen) {
00464             int nsent = send(ctxt->fd, SEND_ARG2_CAST (xmt_ptr + total_sent),
00465                              outlen - total_sent, 0);
00466 
00467             if (nsent > 0)
00468                 total_sent += nsent;
00469             else if ((nsent == -1) &&
00470 #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
00471                      (socket_errno() != EAGAIN) &&
00472 #endif
00473                      (socket_errno() != EWOULDBLOCK)) {
00474                 __xmlIOErr(XML_FROM_HTTP, 0, "send failed\n");
00475                 if (total_sent == 0)
00476                     total_sent = -1;
00477                 break;
00478             } else {
00479                 /*
00480                  * No data sent
00481                  * Since non-blocking sockets are used, wait for
00482                  * socket to be writable or default timeout prior
00483                  * to retrying.
00484                  */
00485 #ifndef HAVE_POLL_H
00486 #ifndef _WINSOCKAPI_
00487                 if (ctxt->fd > FD_SETSIZE)
00488                     return -1;
00489 #endif
00490 
00491                 tv.tv_sec = timeout;
00492                 tv.tv_usec = 0;
00493                 FD_ZERO(&wfd);
00494 #ifdef _MSC_VER
00495 #pragma warning(push)
00496 #pragma warning(disable: 4018)
00497 #endif
00498                 FD_SET(ctxt->fd, &wfd);
00499 #ifdef _MSC_VER
00500 #pragma warning(pop)
00501 #endif
00502                 (void) select(ctxt->fd + 1, NULL, &wfd, NULL, &tv);
00503 #else
00504                 p.fd = ctxt->fd;
00505                 p.events = POLLOUT;
00506                 (void) poll(&p, 1, timeout * 1000);
00507 #endif /* !HAVE_POLL_H */
00508             }
00509         }
00510     }
00511 
00512     return total_sent;
00513 }
00514 
00515 /**
00516  * xmlNanoHTTPRecv:
00517  * @ctxt:  an HTTP context
00518  *
00519  * Read information coming from the HTTP connection.
00520  * This is a blocking call (but it blocks in select(), not read()).
00521  *
00522  * Returns the number of byte read or -1 in case of error.
00523  */
00524 
00525 static int
00526 xmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt)
00527 {
00528 #ifdef HAVE_POLL_H
00529     struct pollfd p;
00530 #else
00531     fd_set rfd;
00532     struct timeval tv;
00533 #endif
00534 
00535 
00536     while (ctxt->state & XML_NANO_HTTP_READ) {
00537         if (ctxt->in == NULL) {
00538             ctxt->in = (char *) xmlMallocAtomic(65000 * sizeof(char));
00539             if (ctxt->in == NULL) {
00540                 xmlHTTPErrMemory("allocating input");
00541                 ctxt->last = -1;
00542                 return (-1);
00543             }
00544             ctxt->inlen = 65000;
00545             ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in;
00546         }
00547         if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) {
00548             int delta = ctxt->inrptr - ctxt->in;
00549             int len = ctxt->inptr - ctxt->inrptr;
00550 
00551             memmove(ctxt->in, ctxt->inrptr, len);
00552             ctxt->inrptr -= delta;
00553             ctxt->content -= delta;
00554             ctxt->inptr -= delta;
00555         }
00556         if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) {
00557             int d_inptr = ctxt->inptr - ctxt->in;
00558             int d_content = ctxt->content - ctxt->in;
00559             int d_inrptr = ctxt->inrptr - ctxt->in;
00560             char *tmp_ptr = ctxt->in;
00561 
00562             ctxt->inlen *= 2;
00563             ctxt->in = (char *) xmlRealloc(tmp_ptr, ctxt->inlen);
00564             if (ctxt->in == NULL) {
00565                 xmlHTTPErrMemory("allocating input buffer");
00566                 xmlFree(tmp_ptr);
00567                 ctxt->last = -1;
00568                 return (-1);
00569             }
00570             ctxt->inptr = ctxt->in + d_inptr;
00571             ctxt->content = ctxt->in + d_content;
00572             ctxt->inrptr = ctxt->in + d_inrptr;
00573         }
00574         ctxt->last = recv(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK, 0);
00575         if (ctxt->last > 0) {
00576             ctxt->inptr += ctxt->last;
00577             return (ctxt->last);
00578         }
00579         if (ctxt->last == 0) {
00580             return (0);
00581         }
00582         if (ctxt->last == -1) {
00583             switch (socket_errno()) {
00584                 case EINPROGRESS:
00585                 case EWOULDBLOCK:
00586 #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
00587                 case EAGAIN:
00588 #endif
00589                     break;
00590 
00591                 case ECONNRESET:
00592                 case ESHUTDOWN:
00593                     return (0);
00594 
00595                 default:
00596                     __xmlIOErr(XML_FROM_HTTP, 0, "recv failed\n");
00597                     return (-1);
00598             }
00599         }
00600 #ifdef HAVE_POLL_H
00601         p.fd = ctxt->fd;
00602         p.events = POLLIN;
00603         if ((poll(&p, 1, timeout * 1000) < 1)
00604 #if defined(EINTR)
00605             && (errno != EINTR)
00606 #endif
00607             )
00608             return (0);
00609 #else /* !HAVE_POLL_H */
00610 #ifndef _WINSOCKAPI_
00611         if (ctxt->fd > FD_SETSIZE)
00612             return 0;
00613 #endif
00614 
00615         tv.tv_sec = timeout;
00616         tv.tv_usec = 0;
00617         FD_ZERO(&rfd);
00618 
00619 #ifdef _MSC_VER
00620 #pragma warning(push)
00621 #pragma warning(disable: 4018)
00622 #endif
00623 
00624         FD_SET(ctxt->fd, &rfd);
00625 
00626 #ifdef _MSC_VER
00627 #pragma warning(pop)
00628 #endif
00629 
00630         if ((select(ctxt->fd + 1, &rfd, NULL, NULL, &tv) < 1)
00631 #if defined(EINTR)
00632             && (errno != EINTR)
00633 #endif
00634             )
00635             return (0);
00636 #endif /* !HAVE_POLL_H */
00637     }
00638     return (0);
00639 }
00640 
00641 /**
00642  * xmlNanoHTTPReadLine:
00643  * @ctxt:  an HTTP context
00644  *
00645  * Read one line in the HTTP server output, usually for extracting
00646  * the HTTP protocol informations from the answer header.
00647  *
00648  * Returns a newly allocated string with a copy of the line, or NULL
00649  *         which indicate the end of the input.
00650  */
00651 
00652 static char *
00653 xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt) {
00654     char buf[4096];
00655     char *bp = buf;
00656     int rc;
00657 
00658     while (bp - buf < 4095) {
00659     if (ctxt->inrptr == ctxt->inptr) {
00660         if ( (rc = xmlNanoHTTPRecv(ctxt)) == 0) {
00661         if (bp == buf)
00662             return(NULL);
00663         else
00664             *bp = 0;
00665         return(xmlMemStrdup(buf));
00666         }
00667         else if ( rc == -1 ) {
00668             return ( NULL );
00669         }
00670     }
00671     *bp = *ctxt->inrptr++;
00672     if (*bp == '\n') {
00673         *bp = 0;
00674         return(xmlMemStrdup(buf));
00675     }
00676     if (*bp != '\r')
00677         bp++;
00678     }
00679     buf[4095] = 0;
00680     return(xmlMemStrdup(buf));
00681 }
00682 
00683 
00684 /**
00685  * xmlNanoHTTPScanAnswer:
00686  * @ctxt:  an HTTP context
00687  * @line:  an HTTP header line
00688  *
00689  * Try to extract useful informations from the server answer.
00690  * We currently parse and process:
00691  *  - The HTTP revision/ return code
00692  *  - The Content-Type, Mime-Type and charset used
00693  *  - The Location for redirect processing.
00694  *
00695  * Returns -1 in case of failure, the file descriptor number otherwise
00696  */
00697 
00698 static void
00699 xmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt, const char *line) {
00700     const char *cur = line;
00701 
00702     if (line == NULL) return;
00703 
00704     if (!strncmp(line, "HTTP/", 5)) {
00705         int version = 0;
00706     int ret = 0;
00707 
00708     cur += 5;
00709     while ((*cur >= '0') && (*cur <= '9')) {
00710         version *= 10;
00711         version += *cur - '0';
00712         cur++;
00713     }
00714     if (*cur == '.') {
00715         cur++;
00716         if ((*cur >= '0') && (*cur <= '9')) {
00717         version *= 10;
00718         version += *cur - '0';
00719         cur++;
00720         }
00721         while ((*cur >= '0') && (*cur <= '9'))
00722         cur++;
00723     } else
00724         version *= 10;
00725     if ((*cur != ' ') && (*cur != '\t')) return;
00726     while ((*cur == ' ') || (*cur == '\t')) cur++;
00727     if ((*cur < '0') || (*cur > '9')) return;
00728     while ((*cur >= '0') && (*cur <= '9')) {
00729         ret *= 10;
00730         ret += *cur - '0';
00731         cur++;
00732     }
00733     if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return;
00734     ctxt->returnValue = ret;
00735         ctxt->version = version;
00736     } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Content-Type:", 13)) {
00737         const xmlChar *charset, *last, *mime;
00738         cur += 13;
00739     while ((*cur == ' ') || (*cur == '\t')) cur++;
00740     if (ctxt->contentType != NULL)
00741         xmlFree(ctxt->contentType);
00742     ctxt->contentType = xmlMemStrdup(cur);
00743     mime = (const xmlChar *) cur;
00744     last = mime;
00745     while ((*last != 0) && (*last != ' ') && (*last != '\t') &&
00746            (*last != ';') && (*last != ','))
00747         last++;
00748     if (ctxt->mimeType != NULL)
00749         xmlFree(ctxt->mimeType);
00750     ctxt->mimeType = (char *) xmlStrndup(mime, last - mime);
00751     charset = xmlStrstr(BAD_CAST ctxt->contentType, BAD_CAST "charset=");
00752     if (charset != NULL) {
00753         charset += 8;
00754         last = charset;
00755         while ((*last != 0) && (*last != ' ') && (*last != '\t') &&
00756                (*last != ';') && (*last != ','))
00757         last++;
00758         if (ctxt->encoding != NULL)
00759             xmlFree(ctxt->encoding);
00760         ctxt->encoding = (char *) xmlStrndup(charset, last - charset);
00761     }
00762     } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"ContentType:", 12)) {
00763         const xmlChar *charset, *last, *mime;
00764         cur += 12;
00765     if (ctxt->contentType != NULL) return;
00766     while ((*cur == ' ') || (*cur == '\t')) cur++;
00767     ctxt->contentType = xmlMemStrdup(cur);
00768     mime = (const xmlChar *) cur;
00769     last = mime;
00770     while ((*last != 0) && (*last != ' ') && (*last != '\t') &&
00771            (*last != ';') && (*last != ','))
00772         last++;
00773     if (ctxt->mimeType != NULL)
00774         xmlFree(ctxt->mimeType);
00775     ctxt->mimeType = (char *) xmlStrndup(mime, last - mime);
00776     charset = xmlStrstr(BAD_CAST ctxt->contentType, BAD_CAST "charset=");
00777     if (charset != NULL) {
00778         charset += 8;
00779         last = charset;
00780         while ((*last != 0) && (*last != ' ') && (*last != '\t') &&
00781                (*last != ';') && (*last != ','))
00782         last++;
00783         if (ctxt->encoding != NULL)
00784             xmlFree(ctxt->encoding);
00785         ctxt->encoding = (char *) xmlStrndup(charset, last - charset);
00786     }
00787     } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Location:", 9)) {
00788         cur += 9;
00789     while ((*cur == ' ') || (*cur == '\t')) cur++;
00790     if (ctxt->location != NULL)
00791         xmlFree(ctxt->location);
00792     if (*cur == '/') {
00793         xmlChar *tmp_http = xmlStrdup(BAD_CAST "http://");
00794         xmlChar *tmp_loc =
00795             xmlStrcat(tmp_http, (const xmlChar *) ctxt->hostname);
00796         ctxt->location =
00797             (char *) xmlStrcat (tmp_loc, (const xmlChar *) cur);
00798     } else {
00799         ctxt->location = xmlMemStrdup(cur);
00800     }
00801     } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"WWW-Authenticate:", 17)) {
00802         cur += 17;
00803     while ((*cur == ' ') || (*cur == '\t')) cur++;
00804     if (ctxt->authHeader != NULL)
00805         xmlFree(ctxt->authHeader);
00806     ctxt->authHeader = xmlMemStrdup(cur);
00807     } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Proxy-Authenticate:", 19)) {
00808         cur += 19;
00809     while ((*cur == ' ') || (*cur == '\t')) cur++;
00810     if (ctxt->authHeader != NULL)
00811         xmlFree(ctxt->authHeader);
00812     ctxt->authHeader = xmlMemStrdup(cur);
00813 #ifdef HAVE_ZLIB_H
00814     } else if ( !xmlStrncasecmp( BAD_CAST line, BAD_CAST"Content-Encoding:", 17) ) {
00815     cur += 17;
00816     while ((*cur == ' ') || (*cur == '\t')) cur++;
00817     if ( !xmlStrncasecmp( BAD_CAST cur, BAD_CAST"gzip", 4) ) {
00818         ctxt->usesGzip = 1;
00819 
00820         ctxt->strm = xmlMalloc(sizeof(z_stream));
00821 
00822         if (ctxt->strm != NULL) {
00823         ctxt->strm->zalloc = Z_NULL;
00824         ctxt->strm->zfree = Z_NULL;
00825         ctxt->strm->opaque = Z_NULL;
00826         ctxt->strm->avail_in = 0;
00827         ctxt->strm->next_in = Z_NULL;
00828 
00829         inflateInit2( ctxt->strm, 31 );
00830         }
00831     }
00832 #endif
00833     } else if ( !xmlStrncasecmp( BAD_CAST line, BAD_CAST"Content-Length:", 15) ) {
00834     cur += 15;
00835     ctxt->ContentLength = strtol( cur, NULL, 10 );
00836     }
00837 }
00838 
00839 /**
00840  * xmlNanoHTTPConnectAttempt:
00841  * @addr:  a socket address structure
00842  *
00843  * Attempt a connection to the given IP:port endpoint. It forces
00844  * non-blocking semantic on the socket, and allow 60 seconds for
00845  * the host to answer.
00846  *
00847  * Returns -1 in case of failure, the file descriptor number otherwise
00848  */
00849 
00850 static SOCKET
00851 xmlNanoHTTPConnectAttempt(struct sockaddr *addr)
00852 {
00853 #ifndef HAVE_POLL_H
00854     fd_set wfd;
00855 #ifdef _WINSOCKAPI_
00856     fd_set xfd;
00857 #endif
00858     struct timeval tv;
00859 #else /* !HAVE_POLL_H */
00860     struct pollfd p;
00861 #endif /* !HAVE_POLL_H */
00862     int status;
00863 
00864     int addrlen;
00865 
00866     SOCKET s;
00867 
00868 #ifdef SUPPORT_IP6
00869     if (addr->sa_family == AF_INET6) {
00870         s = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP);
00871         addrlen = sizeof(struct sockaddr_in6);
00872     } else
00873 #endif
00874     {
00875         s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
00876         addrlen = sizeof(struct sockaddr_in);
00877     }
00878     if (s == INVALID_SOCKET) {
00879 #ifdef DEBUG_HTTP
00880         perror("socket");
00881 #endif
00882         __xmlIOErr(XML_FROM_HTTP, 0, "socket failed\n");
00883         return INVALID_SOCKET;
00884     }
00885 #ifdef _WINSOCKAPI_
00886     {
00887         u_long one = 1;
00888 
00889         status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0;
00890     }
00891 #else /* _WINSOCKAPI_ */
00892 #if defined(VMS)
00893     {
00894         int enable = 1;
00895 
00896         status = ioctl(s, FIONBIO, &enable);
00897     }
00898 #else /* VMS */
00899 #if defined(__BEOS__) && !defined(__HAIKU__)
00900     {
00901         bool noblock = true;
00902 
00903         status =
00904             setsockopt(s, SOL_SOCKET, SO_NONBLOCK, &noblock,
00905                        sizeof(noblock));
00906     }
00907 #else /* __BEOS__ */
00908     if ((status = fcntl(s, F_GETFL, 0)) != -1) {
00909 #ifdef O_NONBLOCK
00910         status |= O_NONBLOCK;
00911 #else /* O_NONBLOCK */
00912 #ifdef F_NDELAY
00913         status |= F_NDELAY;
00914 #endif /* F_NDELAY */
00915 #endif /* !O_NONBLOCK */
00916         status = fcntl(s, F_SETFL, status);
00917     }
00918     if (status < 0) {
00919 #ifdef DEBUG_HTTP
00920         perror("nonblocking");
00921 #endif
00922         __xmlIOErr(XML_FROM_HTTP, 0, "error setting non-blocking IO\n");
00923         closesocket(s);
00924         return INVALID_SOCKET;
00925     }
00926 #endif /* !__BEOS__ */
00927 #endif /* !VMS */
00928 #endif /* !_WINSOCKAPI_ */
00929 
00930     if (connect(s, addr, addrlen) == -1) {
00931         switch (socket_errno()) {
00932             case EINPROGRESS:
00933             case EWOULDBLOCK:
00934                 break;
00935             default:
00936                 __xmlIOErr(XML_FROM_HTTP, 0,
00937                            "error connecting to HTTP server");
00938                 closesocket(s);
00939                 return INVALID_SOCKET;
00940         }
00941     }
00942 #ifndef HAVE_POLL_H
00943     tv.tv_sec = timeout;
00944     tv.tv_usec = 0;
00945 
00946 #ifdef _MSC_VER
00947 #pragma warning(push)
00948 #pragma warning(disable: 4018)
00949 #endif
00950 #ifndef _WINSOCKAPI_
00951     if (s > FD_SETSIZE)
00952         return INVALID_SOCKET;
00953 #endif
00954     FD_ZERO(&wfd);
00955     FD_SET(s, &wfd);
00956 
00957 #ifdef _WINSOCKAPI_
00958     FD_ZERO(&xfd);
00959     FD_SET(s, &xfd);
00960 
00961     switch (select(s + 1, NULL, &wfd, &xfd, &tv))
00962 #else
00963     switch (select(s + 1, NULL, &wfd, NULL, &tv))
00964 #endif
00965 #ifdef _MSC_VER
00966 #pragma warning(pop)
00967 #endif
00968 
00969 #else /* !HAVE_POLL_H */
00970     p.fd = s;
00971     p.events = POLLOUT;
00972     switch (poll(&p, 1, timeout * 1000))
00973 #endif /* !HAVE_POLL_H */
00974 
00975     {
00976         case 0:
00977             /* Time out */
00978             __xmlIOErr(XML_FROM_HTTP, 0, "Connect attempt timed out");
00979             closesocket(s);
00980             return INVALID_SOCKET;
00981         case -1:
00982             /* Ermm.. ?? */
00983             __xmlIOErr(XML_FROM_HTTP, 0, "Connect failed");
00984             closesocket(s);
00985             return INVALID_SOCKET;
00986     }
00987 
00988 #ifndef HAVE_POLL_H
00989     if (FD_ISSET(s, &wfd)
00990 #ifdef _WINSOCKAPI_
00991         || FD_ISSET(s, &xfd)
00992 #endif
00993         )
00994 #else /* !HAVE_POLL_H */
00995     if (p.revents == POLLOUT)
00996 #endif /* !HAVE_POLL_H */
00997     {
00998         XML_SOCKLEN_T len;
00999 
01000         len = sizeof(status);
01001 #ifdef SO_ERROR
01002         if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char *) &status, &len) <
01003             0) {
01004             /* Solaris error code */
01005             __xmlIOErr(XML_FROM_HTTP, 0, "getsockopt failed\n");
01006             closesocket(s);
01007             return INVALID_SOCKET;
01008         }
01009 #endif
01010         if (status) {
01011             __xmlIOErr(XML_FROM_HTTP, 0,
01012                        "Error connecting to remote host");
01013             closesocket(s);
01014             errno = status;
01015             return INVALID_SOCKET;
01016         }
01017     } else {
01018         /* pbm */
01019         __xmlIOErr(XML_FROM_HTTP, 0, "select failed\n");
01020         closesocket(s);
01021         return INVALID_SOCKET;
01022     }
01023 
01024     return (s);
01025 }
01026 
01027 /**
01028  * xmlNanoHTTPConnectHost:
01029  * @host:  the host name
01030  * @port:  the port number
01031  *
01032  * Attempt a connection to the given host:port endpoint. It tries
01033  * the multiple IP provided by the DNS if available.
01034  *
01035  * Returns -1 in case of failure, the file descriptor number otherwise
01036  */
01037 
01038 static SOCKET
01039 xmlNanoHTTPConnectHost(const char *host, int port)
01040 {
01041     struct hostent *h;
01042     struct sockaddr *addr = NULL;
01043     struct in_addr ia;
01044     struct sockaddr_in sockin;
01045 
01046 #ifdef SUPPORT_IP6
01047     struct in6_addr ia6;
01048     struct sockaddr_in6 sockin6;
01049 #endif
01050     int i;
01051     SOCKET s;
01052 
01053     memset (&sockin, 0, sizeof(sockin));
01054 #ifdef SUPPORT_IP6
01055     memset (&sockin6, 0, sizeof(sockin6));
01056 #endif
01057 
01058 #if !defined(HAVE_GETADDRINFO) && defined(SUPPORT_IP6) && defined(RES_USE_INET6)
01059     if (have_ipv6 ())
01060     {
01061     if (!(_res.options & RES_INIT))
01062         res_init();
01063     _res.options |= RES_USE_INET6;
01064     }
01065 #endif
01066 
01067 #if defined(HAVE_GETADDRINFO) && defined(SUPPORT_IP6) && !defined(_WIN32)
01068     if (have_ipv6 ())
01069 #endif
01070 #if defined(HAVE_GETADDRINFO) && (defined(SUPPORT_IP6) || defined(_WIN32))
01071     {
01072     int status;
01073     struct addrinfo hints, *res, *result;
01074 
01075     result = NULL;
01076     memset (&hints, 0,sizeof(hints));
01077     hints.ai_socktype = SOCK_STREAM;
01078 
01079     status = getaddrinfo (host, NULL, &hints, &result);
01080     if (status) {
01081         __xmlIOErr(XML_FROM_HTTP, 0, "getaddrinfo failed\n");
01082         return INVALID_SOCKET;
01083     }
01084 
01085     for (res = result; res; res = res->ai_next) {
01086         if (res->ai_family == AF_INET) {
01087         if (res->ai_addrlen > sizeof(sockin)) {
01088             __xmlIOErr(XML_FROM_HTTP, 0, "address size mismatch\n");
01089             freeaddrinfo (result);
01090             return INVALID_SOCKET;
01091         }
01092         memcpy (&sockin, res->ai_addr, res->ai_addrlen);
01093         sockin.sin_port = htons (port);
01094         addr = (struct sockaddr *)&sockin;
01095 #ifdef SUPPORT_IP6
01096         } else if (have_ipv6 () && (res->ai_family == AF_INET6)) {
01097         if (res->ai_addrlen > sizeof(sockin6)) {
01098             __xmlIOErr(XML_FROM_HTTP, 0, "address size mismatch\n");
01099             freeaddrinfo (result);
01100             return INVALID_SOCKET;
01101         }
01102         memcpy (&sockin6, res->ai_addr, res->ai_addrlen);
01103         sockin6.sin6_port = htons (port);
01104         addr = (struct sockaddr *)&sockin6;
01105 #endif
01106         } else
01107         continue;              /* for */
01108 
01109         s = xmlNanoHTTPConnectAttempt (addr);
01110         if (s != INVALID_SOCKET) {
01111         freeaddrinfo (result);
01112         return (s);
01113         }
01114     }
01115 
01116     if (result)
01117         freeaddrinfo (result);
01118     }
01119 #endif
01120 #if defined(HAVE_GETADDRINFO) && defined(SUPPORT_IP6) && !defined(_WIN32)
01121     else
01122 #endif
01123 #if !defined(HAVE_GETADDRINFO) || !defined(_WIN32)
01124     {
01125     h = gethostbyname (GETHOSTBYNAME_ARG_CAST host);
01126     if (h == NULL) {
01127 
01128 /*
01129  * Okay, I got fed up by the non-portability of this error message
01130  * extraction code. it work on Linux, if it work on your platform
01131  * and one want to enable it, send me the defined(foobar) needed
01132  */
01133 #if defined(HAVE_NETDB_H) && defined(HOST_NOT_FOUND) && defined(linux)
01134         const char *h_err_txt = "";
01135 
01136         switch (h_errno) {
01137         case HOST_NOT_FOUND:
01138             h_err_txt = "Authoritive host not found";
01139             break;
01140 
01141         case TRY_AGAIN:
01142             h_err_txt =
01143             "Non-authoritive host not found or server failure.";
01144             break;
01145 
01146         case NO_RECOVERY:
01147             h_err_txt =
01148             "Non-recoverable errors:  FORMERR, REFUSED, or NOTIMP.";
01149             break;
01150 
01151 #ifdef NO_ADDRESS
01152         case NO_ADDRESS:
01153             h_err_txt =
01154             "Valid name, no data record of requested type.";
01155             break;
01156 #endif
01157 
01158         default:
01159             h_err_txt = "No error text defined.";
01160             break;
01161         }
01162         __xmlIOErr(XML_FROM_HTTP, 0, h_err_txt);
01163 #else
01164         __xmlIOErr(XML_FROM_HTTP, 0, "Failed to resolve host");
01165 #endif
01166         return INVALID_SOCKET;
01167     }
01168 
01169     for (i = 0; h->h_addr_list[i]; i++) {
01170         if (h->h_addrtype == AF_INET) {
01171         /* A records (IPv4) */
01172         if ((unsigned int) h->h_length > sizeof(ia)) {
01173             __xmlIOErr(XML_FROM_HTTP, 0, "address size mismatch\n");
01174             return INVALID_SOCKET;
01175         }
01176         memcpy (&ia, h->h_addr_list[i], h->h_length);
01177         sockin.sin_family = h->h_addrtype;
01178         sockin.sin_addr = ia;
01179         sockin.sin_port = (unsigned short)htons ((unsigned short)port);
01180         addr = (struct sockaddr *) &sockin;
01181 #ifdef SUPPORT_IP6
01182         } else if (have_ipv6 () && (h->h_addrtype == AF_INET6)) {
01183         /* AAAA records (IPv6) */
01184         if ((unsigned int) h->h_length > sizeof(ia6)) {
01185             __xmlIOErr(XML_FROM_HTTP, 0, "address size mismatch\n");
01186             return INVALID_SOCKET;
01187         }
01188         memcpy (&ia6, h->h_addr_list[i], h->h_length);
01189         sockin6.sin6_family = h->h_addrtype;
01190         sockin6.sin6_addr = ia6;
01191         sockin6.sin6_port = htons (port);
01192         addr = (struct sockaddr *) &sockin6;
01193 #endif
01194         } else
01195         break;              /* for */
01196 
01197         s = xmlNanoHTTPConnectAttempt (addr);
01198         if (s != INVALID_SOCKET)
01199         return (s);
01200     }
01201     }
01202 #endif
01203 
01204 #ifdef DEBUG_HTTP
01205     xmlGenericError(xmlGenericErrorContext,
01206                     "xmlNanoHTTPConnectHost:  unable to connect to '%s'.\n",
01207                     host);
01208 #endif
01209     return INVALID_SOCKET;
01210 }
01211 
01212 
01213 /**
01214  * xmlNanoHTTPOpen:
01215  * @URL:  The URL to load
01216  * @contentType:  if available the Content-Type information will be
01217  *                returned at that location
01218  *
01219  * This function try to open a connection to the indicated resource
01220  * via HTTP GET.
01221  *
01222  * Returns NULL in case of failure, otherwise a request handler.
01223  *     The contentType, if provided must be freed by the caller
01224  */
01225 
01226 void*
01227 xmlNanoHTTPOpen(const char *URL, char **contentType) {
01228     if (contentType != NULL) *contentType = NULL;
01229     return(xmlNanoHTTPMethod(URL, NULL, NULL, contentType, NULL, 0));
01230 }
01231 
01232 /**
01233  * xmlNanoHTTPOpenRedir:
01234  * @URL:  The URL to load
01235  * @contentType:  if available the Content-Type information will be
01236  *                returned at that location
01237  * @redir: if available the redirected URL will be returned
01238  *
01239  * This function try to open a connection to the indicated resource
01240  * via HTTP GET.
01241  *
01242  * Returns NULL in case of failure, otherwise a request handler.
01243  *     The contentType, if provided must be freed by the caller
01244  */
01245 
01246 void*
01247 xmlNanoHTTPOpenRedir(const char *URL, char **contentType, char **redir) {
01248     if (contentType != NULL) *contentType = NULL;
01249     if (redir != NULL) *redir = NULL;
01250     return(xmlNanoHTTPMethodRedir(URL, NULL, NULL, contentType, redir, NULL,0));
01251 }
01252 
01253 /**
01254  * xmlNanoHTTPRead:
01255  * @ctx:  the HTTP context
01256  * @dest:  a buffer
01257  * @len:  the buffer length
01258  *
01259  * This function tries to read @len bytes from the existing HTTP connection
01260  * and saves them in @dest. This is a blocking call.
01261  *
01262  * Returns the number of byte read. 0 is an indication of an end of connection.
01263  *         -1 indicates a parameter error.
01264  */
01265 int
01266 xmlNanoHTTPRead(void *ctx, void *dest, int len) {
01267     xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
01268 #ifdef HAVE_ZLIB_H
01269     int bytes_read = 0;
01270     int orig_avail_in;
01271     int z_ret;
01272 #endif
01273 
01274     if (ctx == NULL) return(-1);
01275     if (dest == NULL) return(-1);
01276     if (len <= 0) return(0);
01277 
01278 #ifdef HAVE_ZLIB_H
01279     if (ctxt->usesGzip == 1) {
01280         if (ctxt->strm == NULL) return(0);
01281 
01282         ctxt->strm->next_out = dest;
01283         ctxt->strm->avail_out = len;
01284     ctxt->strm->avail_in = ctxt->inptr - ctxt->inrptr;
01285 
01286         while (ctxt->strm->avail_out > 0 &&
01287            (ctxt->strm->avail_in > 0 || xmlNanoHTTPRecv(ctxt) > 0)) {
01288             orig_avail_in = ctxt->strm->avail_in =
01289                 ctxt->inptr - ctxt->inrptr - bytes_read;
01290             ctxt->strm->next_in = BAD_CAST (ctxt->inrptr + bytes_read);
01291 
01292             z_ret = inflate(ctxt->strm, Z_NO_FLUSH);
01293             bytes_read += orig_avail_in - ctxt->strm->avail_in;
01294 
01295             if (z_ret != Z_OK) break;
01296     }
01297 
01298         ctxt->inrptr += bytes_read;
01299         return(len - ctxt->strm->avail_out);
01300     }
01301 #endif
01302 
01303     while (ctxt->inptr - ctxt->inrptr < len) {
01304         if (xmlNanoHTTPRecv(ctxt) <= 0) break;
01305     }
01306     if (ctxt->inptr - ctxt->inrptr < len)
01307         len = ctxt->inptr - ctxt->inrptr;
01308     memcpy(dest, ctxt->inrptr, len);
01309     ctxt->inrptr += len;
01310     return(len);
01311 }
01312 
01313 /**
01314  * xmlNanoHTTPClose:
01315  * @ctx:  the HTTP context
01316  *
01317  * This function closes an HTTP context, it ends up the connection and
01318  * free all data related to it.
01319  */
01320 void
01321 xmlNanoHTTPClose(void *ctx) {
01322     xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
01323 
01324     if (ctx == NULL) return;
01325 
01326     xmlNanoHTTPFreeCtxt(ctxt);
01327 }
01328 
01329 /**
01330  * xmlNanoHTTPMethodRedir:
01331  * @URL:  The URL to load
01332  * @method:  the HTTP method to use
01333  * @input:  the input string if any
01334  * @contentType:  the Content-Type information IN and OUT
01335  * @redir:  the redirected URL OUT
01336  * @headers:  the extra headers
01337  * @ilen:  input length
01338  *
01339  * This function try to open a connection to the indicated resource
01340  * via HTTP using the given @method, adding the given extra headers
01341  * and the input buffer for the request content.
01342  *
01343  * Returns NULL in case of failure, otherwise a request handler.
01344  *     The contentType, or redir, if provided must be freed by the caller
01345  */
01346 
01347 void*
01348 xmlNanoHTTPMethodRedir(const char *URL, const char *method, const char *input,
01349                   char **contentType, char **redir,
01350           const char *headers, int ilen ) {
01351     xmlNanoHTTPCtxtPtr ctxt;
01352     char *bp, *p;
01353     int blen;
01354     SOCKET ret;
01355     int nbRedirects = 0;
01356     char *redirURL = NULL;
01357 #ifdef DEBUG_HTTP
01358     int xmt_bytes;
01359 #endif
01360 
01361     if (URL == NULL) return(NULL);
01362     if (method == NULL) method = "GET";
01363     xmlNanoHTTPInit();
01364 
01365 retry:
01366     if (redirURL == NULL) {
01367     ctxt = xmlNanoHTTPNewCtxt(URL);
01368     if (ctxt == NULL)
01369         return(NULL);
01370     } else {
01371     ctxt = xmlNanoHTTPNewCtxt(redirURL);
01372     if (ctxt == NULL)
01373         return(NULL);
01374     ctxt->location = xmlMemStrdup(redirURL);
01375     }
01376 
01377     if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
01378     __xmlIOErr(XML_FROM_HTTP, XML_HTTP_URL_SYNTAX, "Not a valid HTTP URI");
01379         xmlNanoHTTPFreeCtxt(ctxt);
01380     if (redirURL != NULL) xmlFree(redirURL);
01381         return(NULL);
01382     }
01383     if (ctxt->hostname == NULL) {
01384     __xmlIOErr(XML_FROM_HTTP, XML_HTTP_UNKNOWN_HOST,
01385                "Failed to identify host in URI");
01386         xmlNanoHTTPFreeCtxt(ctxt);
01387     if (redirURL != NULL) xmlFree(redirURL);
01388         return(NULL);
01389     }
01390     if (proxy) {
01391     blen = strlen(ctxt->hostname) * 2 + 16;
01392     ret = xmlNanoHTTPConnectHost(proxy, proxyPort);
01393     }
01394     else {
01395     blen = strlen(ctxt->hostname);
01396     ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
01397     }
01398     if (ret == INVALID_SOCKET) {
01399         xmlNanoHTTPFreeCtxt(ctxt);
01400     if (redirURL != NULL) xmlFree(redirURL);
01401         return(NULL);
01402     }
01403     ctxt->fd = ret;
01404 
01405     if (input == NULL)
01406     ilen = 0;
01407     else
01408     blen += 36;
01409 
01410     if (headers != NULL)
01411     blen += strlen(headers) + 2;
01412     if (contentType && *contentType)
01413     /* reserve for string plus 'Content-Type: \r\n" */
01414     blen += strlen(*contentType) + 16;
01415     if (ctxt->query != NULL)
01416     /* 1 for '?' */
01417     blen += strlen(ctxt->query) + 1;
01418     blen += strlen(method) + strlen(ctxt->path) + 24;
01419 #ifdef HAVE_ZLIB_H
01420     /* reserve for possible 'Accept-Encoding: gzip' string */
01421     blen += 23;
01422 #endif
01423     if (ctxt->port != 80) {
01424     /* reserve space for ':xxxxx', incl. potential proxy */
01425     if (proxy)
01426         blen += 12;
01427     else
01428         blen += 6;
01429     }
01430     bp = (char*)xmlMallocAtomic(blen);
01431     if ( bp == NULL ) {
01432         xmlNanoHTTPFreeCtxt( ctxt );
01433     xmlHTTPErrMemory("allocating header buffer");
01434     return ( NULL );
01435     }
01436 
01437     p = bp;
01438 
01439     if (proxy) {
01440     if (ctxt->port != 80) {
01441         p += snprintf( p, blen - (p - bp), "%s http://%s:%d%s",
01442             method, ctxt->hostname,
01443             ctxt->port, ctxt->path );
01444     }
01445     else
01446         p += snprintf( p, blen - (p - bp), "%s http://%s%s", method,
01447             ctxt->hostname, ctxt->path);
01448     }
01449     else
01450     p += snprintf( p, blen - (p - bp), "%s %s", method, ctxt->path);
01451 
01452     if (ctxt->query != NULL)
01453     p += snprintf( p, blen - (p - bp), "?%s", ctxt->query);
01454 
01455     if (ctxt->port == 80) {
01456         p += snprintf( p, blen - (p - bp), " HTTP/1.0\r\nHost: %s\r\n",
01457             ctxt->hostname);
01458     } else {
01459         p += snprintf( p, blen - (p - bp), " HTTP/1.0\r\nHost: %s:%d\r\n",
01460             ctxt->hostname, ctxt->port);
01461     }
01462 
01463 #ifdef HAVE_ZLIB_H
01464     p += snprintf(p, blen - (p - bp), "Accept-Encoding: gzip\r\n");
01465 #endif
01466 
01467     if (contentType != NULL && *contentType)
01468     p += snprintf(p, blen - (p - bp), "Content-Type: %s\r\n", *contentType);
01469 
01470     if (headers != NULL)
01471     p += snprintf( p, blen - (p - bp), "%s", headers );
01472 
01473     if (input != NULL)
01474     snprintf(p, blen - (p - bp), "Content-Length: %d\r\n\r\n", ilen );
01475     else
01476     snprintf(p, blen - (p - bp), "\r\n");
01477 
01478 #ifdef DEBUG_HTTP
01479     xmlGenericError(xmlGenericErrorContext,
01480         "-> %s%s", proxy? "(Proxy) " : "", bp);
01481     if ((blen -= strlen(bp)+1) < 0)
01482     xmlGenericError(xmlGenericErrorContext,
01483         "ERROR: overflowed buffer by %d bytes\n", -blen);
01484 #endif
01485     ctxt->outptr = ctxt->out = bp;
01486     ctxt->state = XML_NANO_HTTP_WRITE;
01487     blen = strlen( ctxt->out );
01488 #ifdef DEBUG_HTTP
01489     xmt_bytes = xmlNanoHTTPSend(ctxt, ctxt->out, blen );
01490     if ( xmt_bytes != blen )
01491         xmlGenericError( xmlGenericErrorContext,
01492             "xmlNanoHTTPMethodRedir:  Only %d of %d %s %s\n",
01493             xmt_bytes, blen,
01494             "bytes of HTTP headers sent to host",
01495             ctxt->hostname );
01496 #else
01497     xmlNanoHTTPSend(ctxt, ctxt->out, blen );
01498 #endif
01499 
01500     if ( input != NULL ) {
01501 #ifdef DEBUG_HTTP
01502         xmt_bytes = xmlNanoHTTPSend( ctxt, input, ilen );
01503 
01504     if ( xmt_bytes != ilen )
01505         xmlGenericError( xmlGenericErrorContext,
01506             "xmlNanoHTTPMethodRedir:  Only %d of %d %s %s\n",
01507             xmt_bytes, ilen,
01508             "bytes of HTTP content sent to host",
01509             ctxt->hostname );
01510 #else
01511     xmlNanoHTTPSend( ctxt, input, ilen );
01512 #endif
01513     }
01514 
01515     ctxt->state = XML_NANO_HTTP_READ;
01516 
01517     while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
01518         if (*p == 0) {
01519         ctxt->content = ctxt->inrptr;
01520         xmlFree(p);
01521         break;
01522     }
01523     xmlNanoHTTPScanAnswer(ctxt, p);
01524 
01525 #ifdef DEBUG_HTTP
01526     xmlGenericError(xmlGenericErrorContext, "<- %s\n", p);
01527 #endif
01528         xmlFree(p);
01529     }
01530 
01531     if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
01532         (ctxt->returnValue < 400)) {
01533 #ifdef DEBUG_HTTP
01534     xmlGenericError(xmlGenericErrorContext,
01535         "\nRedirect to: %s\n", ctxt->location);
01536 #endif
01537     while ( xmlNanoHTTPRecv(ctxt) > 0 ) ;
01538         if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
01539         nbRedirects++;
01540         if (redirURL != NULL)
01541         xmlFree(redirURL);
01542         redirURL = xmlMemStrdup(ctxt->location);
01543         xmlNanoHTTPFreeCtxt(ctxt);
01544         goto retry;
01545     }
01546     xmlNanoHTTPFreeCtxt(ctxt);
01547     if (redirURL != NULL) xmlFree(redirURL);
01548 #ifdef DEBUG_HTTP
01549     xmlGenericError(xmlGenericErrorContext,
01550         "xmlNanoHTTPMethodRedir: Too many redirects, aborting ...\n");
01551 #endif
01552     return(NULL);
01553     }
01554 
01555     if (contentType != NULL) {
01556     if (ctxt->contentType != NULL)
01557         *contentType = xmlMemStrdup(ctxt->contentType);
01558     else
01559         *contentType = NULL;
01560     }
01561 
01562     if ((redir != NULL) && (redirURL != NULL)) {
01563     *redir = redirURL;
01564     } else {
01565     if (redirURL != NULL)
01566         xmlFree(redirURL);
01567     if (redir != NULL)
01568         *redir = NULL;
01569     }
01570 
01571 #ifdef DEBUG_HTTP
01572     if (ctxt->contentType != NULL)
01573     xmlGenericError(xmlGenericErrorContext,
01574         "\nCode %d, content-type '%s'\n\n",
01575            ctxt->returnValue, ctxt->contentType);
01576     else
01577     xmlGenericError(xmlGenericErrorContext,
01578         "\nCode %d, no content-type\n\n",
01579            ctxt->returnValue);
01580 #endif
01581 
01582     return((void *) ctxt);
01583 }
01584 
01585 /**
01586  * xmlNanoHTTPMethod:
01587  * @URL:  The URL to load
01588  * @method:  the HTTP method to use
01589  * @input:  the input string if any
01590  * @contentType:  the Content-Type information IN and OUT
01591  * @headers:  the extra headers
01592  * @ilen:  input length
01593  *
01594  * This function try to open a connection to the indicated resource
01595  * via HTTP using the given @method, adding the given extra headers
01596  * and the input buffer for the request content.
01597  *
01598  * Returns NULL in case of failure, otherwise a request handler.
01599  *     The contentType, if provided must be freed by the caller
01600  */
01601 
01602 void*
01603 xmlNanoHTTPMethod(const char *URL, const char *method, const char *input,
01604                   char **contentType, const char *headers, int ilen) {
01605     return(xmlNanoHTTPMethodRedir(URL, method, input, contentType,
01606                           NULL, headers, ilen));
01607 }
01608 
01609 /**
01610  * xmlNanoHTTPFetch:
01611  * @URL:  The URL to load
01612  * @filename:  the filename where the content should be saved
01613  * @contentType:  if available the Content-Type information will be
01614  *                returned at that location
01615  *
01616  * This function try to fetch the indicated resource via HTTP GET
01617  * and save it's content in the file.
01618  *
01619  * Returns -1 in case of failure, 0 incase of success. The contentType,
01620  *     if provided must be freed by the caller
01621  */
01622 int
01623 xmlNanoHTTPFetch(const char *URL, const char *filename, char **contentType) {
01624     void *ctxt = NULL;
01625     char *buf = NULL;
01626     int fd;
01627     int len;
01628     int ret = 0;
01629 
01630     if (filename == NULL) return(-1);
01631     ctxt = xmlNanoHTTPOpen(URL, contentType);
01632     if (ctxt == NULL) return(-1);
01633 
01634     if (!strcmp(filename, "-"))
01635         fd = 0;
01636     else {
01637         fd = open(filename, O_CREAT | O_WRONLY, 00644);
01638     if (fd < 0) {
01639         xmlNanoHTTPClose(ctxt);
01640         if ((contentType != NULL) && (*contentType != NULL)) {
01641             xmlFree(*contentType);
01642         *contentType = NULL;
01643         }
01644         return(-1);
01645     }
01646     }
01647 
01648     xmlNanoHTTPFetchContent( ctxt, &buf, &len );
01649     if ( len > 0 ) {
01650     if (write(fd, buf, len) == -1) {
01651         ret = -1;
01652     }
01653     }
01654 
01655     xmlNanoHTTPClose(ctxt);
01656     close(fd);
01657     return(ret);
01658 }
01659 
01660 #ifdef LIBXML_OUTPUT_ENABLED
01661 /**
01662  * xmlNanoHTTPSave:
01663  * @ctxt:  the HTTP context
01664  * @filename:  the filename where the content should be saved
01665  *
01666  * This function saves the output of the HTTP transaction to a file
01667  * It closes and free the context at the end
01668  *
01669  * Returns -1 in case of failure, 0 incase of success.
01670  */
01671 int
01672 xmlNanoHTTPSave(void *ctxt, const char *filename) {
01673     char *buf = NULL;
01674     int fd;
01675     int len;
01676     int ret = 0;
01677 
01678     if ((ctxt == NULL) || (filename == NULL)) return(-1);
01679 
01680     if (!strcmp(filename, "-"))
01681         fd = 0;
01682     else {
01683         fd = open(filename, O_CREAT | O_WRONLY, 0666);
01684     if (fd < 0) {
01685         xmlNanoHTTPClose(ctxt);
01686         return(-1);
01687     }
01688     }
01689 
01690     xmlNanoHTTPFetchContent( ctxt, &buf, &len );
01691     if ( len > 0 ) {
01692     if (write(fd, buf, len) == -1) {
01693         ret = -1;
01694     }
01695     }
01696 
01697     xmlNanoHTTPClose(ctxt);
01698     close(fd);
01699     return(ret);
01700 }
01701 #endif /* LIBXML_OUTPUT_ENABLED */
01702 
01703 /**
01704  * xmlNanoHTTPReturnCode:
01705  * @ctx:  the HTTP context
01706  *
01707  * Get the latest HTTP return code received
01708  *
01709  * Returns the HTTP return code for the request.
01710  */
01711 int
01712 xmlNanoHTTPReturnCode(void *ctx) {
01713     xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
01714 
01715     if (ctxt == NULL) return(-1);
01716 
01717     return(ctxt->returnValue);
01718 }
01719 
01720 /**
01721  * xmlNanoHTTPAuthHeader:
01722  * @ctx:  the HTTP context
01723  *
01724  * Get the authentication header of an HTTP context
01725  *
01726  * Returns the stashed value of the WWW-Authenticate or Proxy-Authenticate
01727  * header.
01728  */
01729 const char *
01730 xmlNanoHTTPAuthHeader(void *ctx) {
01731     xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
01732 
01733     if (ctxt == NULL) return(NULL);
01734 
01735     return(ctxt->authHeader);
01736 }
01737 
01738 /**
01739  * xmlNanoHTTPContentLength:
01740  * @ctx:  the HTTP context
01741  *
01742  * Provides the specified content length from the HTTP header.
01743  *
01744  * Return the specified content length from the HTTP header.  Note that
01745  * a value of -1 indicates that the content length element was not included in
01746  * the response header.
01747  */
01748 int
01749 xmlNanoHTTPContentLength( void * ctx ) {
01750     xmlNanoHTTPCtxtPtr  ctxt = (xmlNanoHTTPCtxtPtr)ctx;
01751 
01752     return ( ( ctxt == NULL ) ? -1 : ctxt->ContentLength );
01753 }
01754 
01755 /**
01756  * xmlNanoHTTPRedir:
01757  * @ctx:  the HTTP context
01758  *
01759  * Provides the specified redirection URL if available from the HTTP header.
01760  *
01761  * Return the specified redirection URL or NULL if not redirected.
01762  */
01763 const char *
01764 xmlNanoHTTPRedir( void * ctx ) {
01765     xmlNanoHTTPCtxtPtr  ctxt = (xmlNanoHTTPCtxtPtr)ctx;
01766 
01767     return ( ( ctxt == NULL ) ? NULL : ctxt->location );
01768 }
01769 
01770 /**
01771  * xmlNanoHTTPEncoding:
01772  * @ctx:  the HTTP context
01773  *
01774  * Provides the specified encoding if specified in the HTTP headers.
01775  *
01776  * Return the specified encoding or NULL if not available
01777  */
01778 const char *
01779 xmlNanoHTTPEncoding( void * ctx ) {
01780     xmlNanoHTTPCtxtPtr  ctxt = (xmlNanoHTTPCtxtPtr)ctx;
01781 
01782     return ( ( ctxt == NULL ) ? NULL : ctxt->encoding );
01783 }
01784 
01785 /**
01786  * xmlNanoHTTPMimeType:
01787  * @ctx:  the HTTP context
01788  *
01789  * Provides the specified Mime-Type if specified in the HTTP headers.
01790  *
01791  * Return the specified Mime-Type or NULL if not available
01792  */
01793 const char *
01794 xmlNanoHTTPMimeType( void * ctx ) {
01795     xmlNanoHTTPCtxtPtr  ctxt = (xmlNanoHTTPCtxtPtr)ctx;
01796 
01797     return ( ( ctxt == NULL ) ? NULL : ctxt->mimeType );
01798 }
01799 
01800 /**
01801  * xmlNanoHTTPFetchContent:
01802  * @ctx:  the HTTP context
01803  * @ptr:  pointer to set to the content buffer.
01804  * @len:  integer pointer to hold the length of the content
01805  *
01806  * Check if all the content was read
01807  *
01808  * Returns 0 if all the content was read and available, returns
01809  * -1 if received content length was less than specified or an error
01810  * occurred.
01811  */
01812 static int
01813 xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len ) {
01814     xmlNanoHTTPCtxtPtr  ctxt = (xmlNanoHTTPCtxtPtr)ctx;
01815 
01816     int         rc = 0;
01817     int         cur_lgth;
01818     int         rcvd_lgth;
01819     int         dummy_int;
01820     char *      dummy_ptr = NULL;
01821 
01822     /*  Dummy up return input parameters if not provided  */
01823 
01824     if ( len == NULL )
01825         len = &dummy_int;
01826 
01827     if ( ptr == NULL )
01828         ptr = &dummy_ptr;
01829 
01830     /*  But can't work without the context pointer  */
01831 
01832     if ( ( ctxt == NULL ) || ( ctxt->content == NULL ) ) {
01833         *len = 0;
01834     *ptr = NULL;
01835     return ( -1 );
01836     }
01837 
01838     rcvd_lgth = ctxt->inptr - ctxt->content;
01839 
01840     while ( (cur_lgth = xmlNanoHTTPRecv( ctxt )) > 0 ) {
01841 
01842     rcvd_lgth += cur_lgth;
01843     if ( (ctxt->ContentLength > 0) && (rcvd_lgth >= ctxt->ContentLength) )
01844         break;
01845     }
01846 
01847     *ptr = ctxt->content;
01848     *len = rcvd_lgth;
01849 
01850     if ( ( ctxt->ContentLength > 0 ) && ( rcvd_lgth < ctxt->ContentLength ) )
01851         rc = -1;
01852     else if ( rcvd_lgth == 0 )
01853     rc = -1;
01854 
01855     return ( rc );
01856 }
01857 
01858 #ifdef STANDALONE
01859 int main(int argc, char **argv) {
01860     char *contentType = NULL;
01861 
01862     if (argv[1] != NULL) {
01863     if (argv[2] != NULL)
01864         xmlNanoHTTPFetch(argv[1], argv[2], &contentType);
01865         else
01866         xmlNanoHTTPFetch(argv[1], "-", &contentType);
01867     if (contentType != NULL) xmlFree(contentType);
01868     } else {
01869         xmlGenericError(xmlGenericErrorContext,
01870         "%s: minimal HTTP GET implementation\n", argv[0]);
01871         xmlGenericError(xmlGenericErrorContext,
01872         "\tusage %s [ URL [ filename ] ]\n", argv[0]);
01873     }
01874     xmlNanoHTTPCleanup();
01875     xmlMemoryDump();
01876     return(0);
01877 }
01878 #endif /* STANDALONE */
01879 #else /* !LIBXML_HTTP_ENABLED */
01880 #ifdef STANDALONE
01881 #include <stdio.h>
01882 int main(int argc, char **argv) {
01883     xmlGenericError(xmlGenericErrorContext,
01884         "%s : HTTP support not compiled in\n", argv[0]);
01885     return(0);
01886 }
01887 #endif /* STANDALONE */
01888 #endif /* LIBXML_HTTP_ENABLED */
01889 #define bottom_nanohttp
01890 #include "elfgcchack.h"
01891