| /* |
| * R : A Computer Language for Statistical Data Analysis |
| * Copyright (C) 2001-2017 The R Core Team. |
| * Copyright (C) 1998-2012 Daniel Veillard. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, a copy is available at |
| * https://www.R-project.org/Licenses/ |
| */ |
| |
| /* <UTF8> the only interpretation of char is ASCII |
| <MBCS> all the char quantities should be ASCII |
| */ |
| |
| |
| /* based on libxml2-2.3.6: |
| * (but updated to protect against CVE-2004-0989): |
| * nanohttp.c: minimalist HTTP GET implementation to fetch external subsets. |
| * focuses on size, streamability, reentrancy and portability |
| * |
| * This is clearly not a general purpose HTTP implementation |
| * If you look for one, check: |
| * http://www.w3.org/Library/ |
| * |
| * See Copyright for the status of this software. |
| * |
| * daniel@veillard.com |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #undef HAVE_ZLIB_H |
| |
| #ifdef ENABLE_NLS |
| #include <libintl.h> |
| #ifdef Win32 |
| #define _(String) libintl_gettext (String) |
| #undef gettext /* needed for graphapp */ |
| #else |
| #define _(String) gettext (String) |
| #endif |
| #else /* not NLS */ |
| #define _(String) (String) |
| #endif |
| |
| extern void R_ProcessEvents(void); |
| |
| #ifdef Win32 |
| #define FD_SETSIZE 1024 |
| #include <io.h> |
| #include <winsock2.h> |
| #ifndef EWOULDBLOCK |
| # define EWOULDBLOCK WSAEWOULDBLOCK |
| #endif |
| #ifndef EINPROGRESS |
| # define EINPROGRESS WSAEINPROGRESS |
| #endif |
| #ifndef EALREADY |
| # define EALREADY WSAEALREADY |
| #endif |
| #define _WINSOCKAPI_ |
| extern void R_FlushConsole(void); |
| #define R_SelectEx(n,rfd,wrd,efd,tv,ih) select(n,rfd,wrd,efd,tv) |
| #endif |
| |
| // ../../include/R_ext/R-ftp-http.h : |
| #include <R_ext/R-ftp-http.h> |
| #ifdef Win32 |
| # include <R_ext/Print.h> |
| #endif |
| |
| #ifdef HAVE_STRINGS_H |
| /* may be needed to define bzero in FD_ZERO (eg AIX) */ |
| #include <strings.h> |
| #endif |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <stdlib.h> |
| |
| #ifdef HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| |
| #ifdef Unix |
| # include <netdb.h> |
| # include <sys/socket.h> |
| # include <netinet/in.h> |
| #endif |
| |
| #ifdef HAVE_FCNTL_H |
| #include <fcntl.h> |
| #endif |
| #ifdef HAVE_ERRNO_H |
| #include <errno.h> |
| #endif |
| #ifdef HAVE_SYS_TIME_H |
| #include <sys/time.h> |
| #endif |
| #ifdef HAVE_SYS_SELECT_H |
| #include <sys/select.h> |
| #endif |
| #ifdef SUPPORT_IP6 |
| #include <resolv.h> |
| #endif |
| #ifdef HAVE_ZLIB_H |
| #include <zlib.h> |
| #endif |
| |
| /* #define DEBUG_HTTP */ |
| |
| #define BAD_CAST (unsigned char *) |
| |
| #if !defined(strdup) && defined(HAVE_DECL_STRDUP) && !HAVE_DECL_STRDUP |
| extern char *strdup(const char *s1); |
| #endif |
| |
| #define xmlFree free |
| #define xmlMalloc malloc |
| #define xmlRealloc realloc |
| #define xmlMemStrdup strdup |
| #define xmlStrndup(a, b) strdup(a) |
| #define xmlStrstr(a, b) strstr((const char *)a, (const char *)b) |
| #define xmlStrdup(a) strdup((char *)a) |
| |
| static void RxmlNanoHTTPScanProxy(const char *URL); |
| static void *RxmlNanoHTTPMethod(const char *URL, const char *method, |
| const char *input, char **contentType, |
| const char *headers, const int cacheOK); |
| |
| #ifdef Unix |
| #include <R_ext/eventloop.h> |
| |
| /* modified from src/unix/sys-std.c */ |
| static int |
| setSelectMask(InputHandler *handlers, fd_set *readMask) |
| { |
| int maxfd = -1; |
| InputHandler *tmp = handlers; |
| FD_ZERO(readMask); |
| |
| while(tmp) { |
| if(tmp->fileDescriptor > 0) { |
| FD_SET(tmp->fileDescriptor, readMask); |
| maxfd = maxfd < tmp->fileDescriptor ? tmp->fileDescriptor : maxfd; |
| } |
| tmp = tmp->next; |
| } |
| |
| return(maxfd); |
| } |
| #endif |
| |
| /** |
| * A couple of portability macros |
| */ |
| #ifndef _WINSOCKAPI_ |
| #define closesocket(s) close(s) |
| #define SOCKET int |
| #endif |
| |
| /* where strncasecmp is defined seems system-specific, |
| and on Windows the cross-compiler doesn't find strings.h */ |
| #if defined(HAVE_STRINGS_H) && !defined(Win32) |
| # include <strings.h> |
| #endif |
| #if !defined(strncasecmp) && defined(HAVE_DECL_STRNCASECMP) && !HAVE_DECL_STRNCASECMP |
| extern int strncasecmp(const char *s1, const char *s2, size_t n); |
| #endif |
| #define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n) |
| |
| #define XML_NANO_HTTP_MAX_REDIR 10 |
| |
| #define XML_NANO_HTTP_CHUNK 4096 |
| |
| #define XML_NANO_HTTP_CLOSED 0 |
| #define XML_NANO_HTTP_WRITE 1 |
| #define XML_NANO_HTTP_READ 2 |
| #define XML_NANO_HTTP_NONE 4 |
| |
| typedef struct RxmlNanoHTTPCtxt { |
| char *protocol; /* the protocol name */ |
| char *hostname; /* the host name */ |
| int port; /* the port */ |
| char *path; /* the path within the URL */ |
| char *query; /* the query string */ |
| SOCKET fd; /* the file descriptor for the socket */ |
| int state; /* WRITE / READ / CLOSED */ |
| char *out; /* buffer sent (zero terminated) */ |
| char *outptr; /* index within the buffer sent */ |
| char *in; /* the receiving buffer */ |
| char *content; /* the start of the content */ |
| char *inptr; /* the next byte to read from network */ |
| char *inrptr; /* the next byte to give back to the client */ |
| int inlen; /* len of the input buffer */ |
| int last; /* return code for last operation */ |
| int returnValue; /* the protocol return value */ |
| char *statusMsg; /* the protocol status message */ |
| char *contentType; /* the MIME type for the input */ |
| DLsize_t contentLength; /* the reported length */ |
| char *location; /* the new URL in case of redirect */ |
| char *authHeader; /* contents of {WWW,Proxy}-Authenticate header */ |
| char *encoding; /* encoding extracted from the contentType */ |
| char *mimeType; /* Mime-Type extracted from the contentType */ |
| #ifdef HAVE_ZLIB_H |
| z_stream *strm; /* Zlib stream object */ |
| int usesGzip; /* "Content-Encoding: gzip" was detected */ |
| #endif |
| } RxmlNanoHTTPCtxt, *RxmlNanoHTTPCtxtPtr; |
| |
| static int initialized = 0; |
| static char *proxy = NULL; /* the proxy name if any */ |
| static int proxyPort; /* the proxy port if any */ |
| static char *proxyUser; /* the proxy user:pwd if any */ |
| static unsigned int timeout = 60;/* the select() timeout in seconds */ |
| |
| /** |
| * A portability function |
| */ |
| static int socket_errno(void) |
| { |
| #ifdef _WINSOCKAPI_ |
| return(WSAGetLastError()); |
| #else |
| return(errno); |
| #endif |
| } |
| |
| /** |
| * RxmlNanoHTTPInit: |
| * |
| * Initialize the HTTP protocol layer. |
| * Currently it just checks for proxy informations |
| */ |
| |
| #ifdef Win32 |
| # include "graphapp.h" |
| #endif |
| |
| static void |
| RxmlNanoHTTPInit(void) |
| { |
| const char *env; |
| #ifdef _WINSOCKAPI_ |
| WSADATA wsaData; |
| #endif |
| |
| if (initialized) |
| return; |
| |
| #ifdef _WINSOCKAPI_ |
| if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) |
| return; |
| #endif |
| |
| if (proxy == NULL) { |
| proxyPort = 80; |
| env = getenv("no_proxy"); |
| if (env && ((env[0] == '*') && (env[1] == 0))) |
| goto done; |
| env = getenv("http_proxy"); |
| if (env != NULL) { |
| RxmlNanoHTTPScanProxy(env); |
| goto chkuser; |
| } |
| env = getenv("HTTP_PROXY"); |
| if (env != NULL) { |
| RxmlNanoHTTPScanProxy(env); |
| goto chkuser; |
| } |
| chkuser: |
| if((env = getenv("http_proxy_user")) != NULL) { |
| if (proxyUser != NULL) {xmlFree(proxyUser); proxyUser = NULL;} |
| #ifdef Win32 |
| if (strcmp(env, "ask") == 0) |
| env = askUserPass("Proxy Authentication"); |
| #endif |
| proxyUser = xmlMemStrdup(env); |
| } |
| } |
| done: |
| initialized = 1; |
| } |
| |
| /** |
| * RxmlNanoHTTPClenup: |
| * |
| * Cleanup the HTTP protocol layer. |
| */ |
| |
| void |
| RxmlNanoHTTPCleanup(void) |
| { |
| if (proxy != NULL) xmlFree(proxy); |
| if (proxyUser != NULL) xmlFree(proxyUser); |
| #ifdef _WINSOCKAPI_ |
| if (initialized) |
| WSACleanup(); |
| #endif |
| initialized = 0; |
| return; |
| } |
| |
| /** |
| * RxmlNanoHTTPTimeout: |
| * @delay: the delay in seconds |
| * |
| * Set the HTTP timeout, (default is 60secs). 0 means immediate |
| * return, while -1 infinite. |
| */ |
| |
| void |
| RxmlNanoHTTPTimeout(int delay) |
| { |
| timeout = (unsigned int) delay; |
| } |
| |
| /** |
| * RxmlNanoHTTPScanURL: |
| * @ctxt: an HTTP context |
| * @URL: The URL used to initialize the context |
| * |
| * (Re)Initialize an HTTP context by parsing the URL and finding |
| * the protocol host port and path it indicates. |
| */ |
| |
| /* GET URLs can be very long, so use more generous limit here */ |
| |
| static void |
| RxmlNanoHTTPScanURL(RxmlNanoHTTPCtxtPtr ctxt, const char *URL) |
| { |
| const char *cur = URL; |
| char buf[40960]; |
| int indx = 0; |
| const int indxMax = 40960 - 1; |
| int port = 0; |
| |
| if (ctxt->protocol != NULL) { |
| xmlFree(ctxt->protocol); |
| ctxt->protocol = NULL; |
| } |
| if (ctxt->hostname != NULL) { |
| xmlFree(ctxt->hostname); |
| ctxt->hostname = NULL; |
| } |
| if (ctxt->path != NULL) { |
| xmlFree(ctxt->path); |
| ctxt->path = NULL; |
| } |
| if (URL == NULL) return; |
| buf[indx] = 0; |
| while (*cur != 0 && (indx < indxMax)) { |
| if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) { |
| buf[indx] = 0; |
| ctxt->protocol = xmlMemStrdup(buf); |
| indx = 0; |
| cur += 3; |
| break; |
| } |
| buf[indx++] = *cur++; |
| } |
| if (*cur == 0) return; |
| |
| buf[indx] = 0; |
| while (indx < indxMax) { |
| if (cur[0] == ':') { |
| buf[indx] = 0; |
| ctxt->hostname = xmlMemStrdup(buf); |
| indx = 0; |
| cur += 1; |
| while ((*cur >= '0') && (*cur <= '9')) { |
| port *= 10; |
| port += *cur - '0'; |
| cur++; |
| } |
| if (port != 0) ctxt->port = port; |
| while ((cur[0] != '/') && (*cur != 0)) |
| cur++; |
| break; |
| } |
| if ((*cur == '/') || (*cur == 0)) { |
| buf[indx] = 0; |
| ctxt->hostname = xmlMemStrdup(buf); |
| indx = 0; |
| break; |
| } |
| buf[indx++] = *cur++; |
| } |
| if (*cur == 0) |
| ctxt->path = xmlMemStrdup("/"); |
| else { |
| indx = 0; |
| buf[indx] = 0; |
| while (*cur != 0 && (indx < indxMax)) { |
| buf[indx++] = *cur++; |
| } |
| buf[indx] = 0; |
| ctxt->path = xmlMemStrdup(buf); |
| } |
| } |
| |
| /** |
| * RxmlNanoHTTPScanProxy: |
| * @URL: The proxy URL used to initialize the proxy context |
| * |
| * (Re)Initialize the HTTP Proxy context by parsing the URL and finding |
| * the protocol host port it indicates. |
| * Should be like http://myproxy/ or http://myproxy:3128/ |
| * A NULL URL cleans up proxy informations. |
| */ |
| |
| void |
| RxmlNanoHTTPScanProxy(const char *URL) |
| { |
| const char *cur = URL; |
| char buf[4096]; |
| int indx = 0; |
| const int indxMax = 4096 - 1; |
| int port = 0; |
| |
| if (proxy != NULL) { |
| xmlFree(proxy); |
| proxy = NULL; |
| } |
| /*if (proxyPort != 0) { |
| proxyPort = 0; |
| }*/ |
| if (URL == NULL) |
| RxmlMessage(0, _("removing HTTP proxy info")); |
| else |
| RxmlMessage(1, _("using HTTP proxy '%s'"), URL); |
| if (URL == NULL) return; |
| buf[indx] = 0; |
| while (*cur != 0 && (indx < indxMax)) { |
| if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) { |
| buf[indx] = 0; |
| indx = 0; |
| cur += 3; |
| break; |
| } |
| buf[indx++] = *cur++; |
| } |
| if (*cur == 0) return; |
| |
| if(strchr(cur, '@')) { |
| strcpy(buf, cur); |
| *(strchr(buf, '@')) = '\0'; |
| if(proxyUser) xmlFree(proxyUser); |
| proxyUser = xmlMemStrdup(buf); |
| cur += (int)strlen(buf) + 1; |
| } |
| buf[indx] = 0; |
| while (indx < indxMax) { |
| if (cur[0] == ':') { |
| buf[indx] = 0; |
| proxy = xmlMemStrdup(buf); |
| indx = 0; |
| cur += 1; |
| while ((*cur >= '0') && (*cur <= '9')) { |
| port *= 10; |
| port += *cur - '0'; |
| cur++; |
| } |
| if (port != 0) proxyPort = port; |
| while ((cur[0] != '/') && (*cur != 0)) |
| cur++; |
| break; |
| } |
| if ((*cur == '/') || (*cur == 0)) { |
| buf[indx] = 0; |
| proxy = xmlMemStrdup(buf); |
| indx = 0; |
| break; |
| } |
| buf[indx++] = *cur++; |
| } |
| } |
| |
| /** |
| * RxmlNanoHTTPNewCtxt: |
| * @URL: The URL used to initialize the context |
| * |
| * Allocate and initialize a new HTTP context. |
| * |
| * Returns an HTTP context or NULL in case of error. |
| */ |
| |
| static RxmlNanoHTTPCtxtPtr |
| RxmlNanoHTTPNewCtxt(const char *URL) |
| { |
| RxmlNanoHTTPCtxtPtr ret; |
| |
| ret = (RxmlNanoHTTPCtxtPtr) xmlMalloc(sizeof(RxmlNanoHTTPCtxt)); |
| if (ret == NULL) { |
| RxmlMessage(1, "error allocating context"); |
| return(NULL); |
| } |
| |
| memset(ret, 0, sizeof(RxmlNanoHTTPCtxt)); |
| ret->port = 80; |
| ret->returnValue = 0; |
| ret->statusMsg = NULL; |
| ret->contentLength = -1; |
| ret->fd = -1; |
| |
| RxmlNanoHTTPScanURL(ret, URL); |
| |
| return(ret); |
| } |
| |
| /** |
| * RxmlNanoHTTPFreeCtxt: |
| * @ctxt: an HTTP context |
| * |
| * Frees the context after closing the connection. |
| */ |
| |
| static void |
| RxmlNanoHTTPFreeCtxt(RxmlNanoHTTPCtxtPtr ctxt) |
| { |
| if (ctxt == NULL) return; |
| if (ctxt->hostname != NULL) xmlFree(ctxt->hostname); |
| if (ctxt->protocol != NULL) xmlFree(ctxt->protocol); |
| if (ctxt->path != NULL) xmlFree(ctxt->path); |
| if (ctxt->query != NULL) xmlFree(ctxt->query); |
| if (ctxt->out != NULL) xmlFree(ctxt->out); |
| if (ctxt->in != NULL) xmlFree(ctxt->in); |
| if (ctxt->contentType != NULL) xmlFree(ctxt->contentType); |
| if (ctxt->encoding != NULL) xmlFree(ctxt->encoding); |
| if (ctxt->mimeType != NULL) xmlFree(ctxt->mimeType); |
| if (ctxt->location != NULL) xmlFree(ctxt->location); |
| if (ctxt->authHeader != NULL) xmlFree(ctxt->authHeader); |
| if (ctxt->statusMsg != NULL) xmlFree(ctxt->statusMsg); |
| #ifdef HAVE_ZLIB_H |
| if (ctxt->strm != NULL) { |
| inflateEnd(ctxt->strm); |
| xmlFree(ctxt->strm); |
| } |
| #endif |
| |
| ctxt->state = XML_NANO_HTTP_NONE; |
| if (ctxt->fd >= 0) closesocket(ctxt->fd); |
| ctxt->fd = -1; |
| xmlFree(ctxt); |
| } |
| |
| /** |
| * RxmlNanoHTTPSend: |
| * @ctxt: an HTTP context |
| * |
| * Send the input needed to initiate the processing on the server side |
| */ |
| |
| static void |
| RxmlNanoHTTPSend(RxmlNanoHTTPCtxtPtr ctxt) |
| { |
| if (ctxt->state & XML_NANO_HTTP_WRITE) { |
| unsigned int total_sent = 0; |
| while (total_sent <strlen(ctxt->outptr)) { |
| DLsize_t nsent = send(ctxt->fd, ctxt->outptr+total_sent, |
| strlen(ctxt->outptr)-total_sent, 0); |
| if (nsent > 0) |
| total_sent += nsent; |
| } |
| ctxt->last = total_sent; |
| } |
| } |
| |
| /** |
| * RxmlNanoHTTPRecv: |
| * @ctxt: an HTTP context |
| * |
| * Read information coming from the HTTP connection. |
| * This is a blocking call (but it blocks in select(), not read()). |
| * |
| * Returns the number of byte read or -1 in case of error. |
| */ |
| |
| static int |
| RxmlNanoHTTPRecv(RxmlNanoHTTPCtxtPtr ctxt) |
| { |
| fd_set rfd; |
| struct timeval tv; |
| double used = 0.0; |
| |
| |
| if (ctxt->state & XML_NANO_HTTP_READ) { |
| if (ctxt->in == NULL) { |
| ctxt->in = (char *) xmlMalloc(65000 * sizeof(char)); |
| if (ctxt->in == NULL) { |
| RxmlMessage(1, "error allocating input"); |
| ctxt->last = -1; |
| return(-1); |
| } |
| ctxt->inlen = 65000; |
| ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in; |
| } |
| if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) { |
| ssize_t delta = ctxt->inrptr - ctxt->in; |
| size_t len = ctxt->inptr - ctxt->inrptr; |
| |
| memmove(ctxt->in, ctxt->inrptr, len); |
| ctxt->inrptr -= delta; |
| ctxt->content -= delta; |
| ctxt->inptr -= delta; |
| } |
| if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) { |
| ssize_t d_inptr = ctxt->inptr - ctxt->in; |
| ssize_t d_content = ctxt->content - ctxt->in; |
| ssize_t d_inrptr = ctxt->inrptr - ctxt->in; |
| char * tmp_ptr = ctxt->in; |
| |
| ctxt->inlen *= 2; |
| ctxt->in = (char *) xmlRealloc(tmp_ptr, ctxt->inlen); |
| if (ctxt->in == NULL) { |
| RxmlMessage(1, "error allocating input buffer"); |
| xmlFree( tmp_ptr ); |
| ctxt->last = -1; |
| return(-1); |
| } |
| ctxt->inptr = ctxt->in + d_inptr; |
| ctxt->content = ctxt->in + d_content; |
| ctxt->inrptr = ctxt->in + d_inrptr; |
| } |
| |
| while(1) { |
| int maxfd = 0, howmany; |
| struct timeval tv_save; /* select() is destructive on Linux, so save tv */ |
| R_ProcessEvents(); |
| #ifdef Unix |
| if(R_wait_usec > 0) { |
| tv.tv_sec = 0; |
| tv.tv_usec = R_wait_usec; |
| } else { |
| tv.tv_sec = timeout; |
| tv.tv_usec = 0; |
| } |
| #elif defined(Win32) |
| tv.tv_sec = 0; |
| tv.tv_usec = 2e5; |
| #else |
| tv.tv_sec = timeout; |
| tv.tv_usec = 0; |
| #endif |
| |
| tv_save = tv; |
| |
| #ifdef Unix |
| maxfd = setSelectMask(R_InputHandlers, &rfd); |
| #else |
| FD_ZERO(&rfd); |
| #endif |
| FD_SET(ctxt->fd, &rfd); |
| if(maxfd < ctxt->fd) maxfd = ctxt->fd; |
| |
| howmany = R_SelectEx(maxfd+1, &rfd, NULL, NULL, &tv, NULL); |
| |
| if (howmany < 0) { |
| #ifdef DEBUG_HTTP |
| perror("select in RxmlNanoHTTPRecv"); |
| #endif |
| return(0); |
| } |
| if (howmany == 0) { |
| used += tv_save.tv_sec + 1e-6 * tv_save.tv_usec; |
| if(used >= timeout) return(0); |
| continue; |
| } |
| |
| #ifdef Unix |
| if(!FD_ISSET(ctxt->fd, &rfd) || howmany > 1) { |
| /* was one of the extras */ |
| InputHandler *what; |
| what = getSelectedHandler(R_InputHandlers, &rfd); |
| if(what != NULL) what->handler((void*) NULL); |
| continue; |
| } |
| #endif |
| |
| /* was the socket */ |
| ctxt->last = (int) recv(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK, 0); |
| if (ctxt->last > 0) { |
| ctxt->inptr += ctxt->last; |
| return(ctxt->last); |
| } |
| if (ctxt->last == 0) { |
| return(0); |
| } |
| if (ctxt->last == -1) { |
| switch (socket_errno()) { |
| case EINPROGRESS: |
| case EWOULDBLOCK: |
| #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK |
| case EAGAIN: |
| #endif |
| #if defined(WSAEINPROGRESS) && WSAEINPROGRESS != EINPROGRESS |
| case WSAEINPROGRESS: |
| #endif |
| #if defined(WSAEWOULDBLOCK) && WSAEWOULDBLOCK != EWOULDBLOCK |
| case WSAEWOULDBLOCK: |
| #endif |
| break; |
| |
| default: |
| return(0); |
| } |
| } |
| } |
| } |
| |
| return(0); |
| } |
| |
| /** |
| * RxmlNanoHTTPReadLine: |
| * @ctxt: an HTTP context |
| * |
| * Read one line in the HTTP server output, usually for extracting |
| * the HTTP protocol informations from the answer header. |
| * |
| * Returns a newly allocated string with a copy of the line, or NULL |
| * which indicate the end of the input. |
| */ |
| |
| static char * |
| RxmlNanoHTTPReadLine(RxmlNanoHTTPCtxtPtr ctxt) |
| { |
| char buf[4096]; |
| char *bp = buf; |
| int rc; |
| |
| while (bp - buf < 4095) { |
| if (ctxt->inrptr == ctxt->inptr) { |
| if ( (rc = RxmlNanoHTTPRecv(ctxt)) == 0) { |
| if (bp == buf) |
| return(NULL); |
| else |
| *bp = 0; |
| return(xmlMemStrdup(buf)); |
| } |
| else if ( rc == -1 ) { |
| return ( NULL ); |
| } |
| } |
| *bp = *ctxt->inrptr++; |
| if (*bp == '\n') { |
| *bp = 0; |
| return(xmlMemStrdup(buf)); |
| } |
| if (*bp != '\r') |
| bp++; |
| } |
| buf[4095] = 0; |
| return(xmlMemStrdup(buf)); |
| } |
| |
| |
| /** |
| * RxmlNanoHTTPScanAnswer: |
| * @ctxt: an HTTP context |
| * @line: an HTTP header line |
| * |
| * Try to extract useful informations from the server answer. |
| * We currently parse and process: |
| * - The HTTP revision/ return code |
| * - The Content-Type, Mime-Type and charset used |
| * - The Location for redirect processing. |
| * |
| * Returns -1 in case of failure, the file descriptor number otherwise |
| */ |
| |
| static void |
| RxmlNanoHTTPScanAnswer(RxmlNanoHTTPCtxtPtr ctxt, const char *line) |
| { |
| const char *cur = line; |
| |
| if (line == NULL) return; |
| |
| if (!strncmp(line, "HTTP/", 5)) { |
| int version = 0; |
| int ret = 0; |
| |
| cur += 5; |
| while ((*cur >= '0') && (*cur <= '9')) { |
| version *= 10; |
| version += *cur - '0'; |
| cur++; |
| } |
| if (*cur == '.') { |
| cur++; |
| if ((*cur >= '0') && (*cur <= '9')) { |
| version *= 10; |
| version += *cur - '0'; |
| cur++; |
| } |
| while ((*cur >= '0') && (*cur <= '9')) |
| cur++; |
| } else |
| version *= 10; |
| if ((*cur != ' ') && (*cur != '\t')) return; |
| while ((*cur == ' ') || (*cur == '\t')) cur++; |
| if ((*cur < '0') || (*cur > '9')) return; |
| while ((*cur >= '0') && (*cur <= '9')) { |
| ret *= 10; |
| ret += *cur - '0'; |
| cur++; |
| } |
| if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return; |
| ctxt->returnValue = ret; |
| if ((*cur == ' ') || (*cur == '\t')) cur++; |
| if(ctxt->statusMsg) xmlFree(ctxt->statusMsg); |
| ctxt->statusMsg = xmlMemStrdup(cur); |
| } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Content-Type:", 13)) { |
| const char *charset, *last, *mime; |
| cur += 13; |
| while ((*cur == ' ') || (*cur == '\t')) cur++; |
| if (ctxt->contentType != NULL) |
| xmlFree(ctxt->contentType); |
| ctxt->contentType = xmlMemStrdup(cur); |
| mime = (const char *) cur; |
| last = mime; |
| while ((*last != 0) && (*last != ' ') && (*last != '\t') && |
| (*last != ';') && (*last != ',')) |
| last++; |
| if (ctxt->mimeType != NULL) |
| xmlFree(ctxt->mimeType); |
| ctxt->mimeType = (char *) xmlStrndup(mime, last - mime); |
| charset = xmlStrstr(BAD_CAST ctxt->contentType, BAD_CAST "charset="); |
| if (charset != NULL) { |
| charset += 8; |
| last = charset; |
| while ((*last != 0) && (*last != ' ') && (*last != '\t') && |
| (*last != ';') && (*last != ',')) |
| last++; |
| if (ctxt->encoding != NULL) |
| xmlFree(ctxt->encoding); |
| ctxt->encoding = (char *) xmlStrndup(charset, last - charset); |
| } |
| } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"ContentType:", 12)) { |
| const char *charset, *last, *mime; |
| cur += 12; |
| if (ctxt->contentType != NULL) return; |
| while ((*cur == ' ') || (*cur == '\t')) cur++; |
| ctxt->contentType = xmlMemStrdup(cur); |
| mime = (const char *) cur; |
| last = mime; |
| while ((*last != 0) && (*last != ' ') && (*last != '\t') && |
| (*last != ';') && (*last != ',')) |
| last++; |
| if (ctxt->mimeType != NULL) |
| xmlFree(ctxt->mimeType); |
| ctxt->mimeType = (char *) xmlStrndup(mime, last - mime); |
| charset = xmlStrstr(BAD_CAST ctxt->contentType, BAD_CAST "charset="); |
| if (charset != NULL) { |
| charset += 8; |
| last = charset; |
| while ((*last != 0) && (*last != ' ') && (*last != '\t') && |
| (*last != ';') && (*last != ',')) |
| last++; |
| if (ctxt->encoding != NULL) |
| xmlFree(ctxt->encoding); |
| ctxt->encoding = (char *) xmlStrndup(charset, last - charset); |
| } |
| } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Content-Length:", 15)) { |
| cur += 15; |
| while ((*cur == ' ') || (*cur == '\t')) cur++; |
| { |
| // was atoi, but DLsize_t may be > long, let alone int. |
| char *endp; |
| double len = strtod(cur, &endp); |
| ctxt->contentLength = (DLsize_t) len; |
| } |
| } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Location:", 9)) { |
| cur += 9; |
| while ((*cur == ' ') || (*cur == '\t')) cur++; |
| if (ctxt->location != NULL) |
| xmlFree(ctxt->location); |
| if (*cur == '/') { |
| char buf[1000]; |
| snprintf(buf, 1000, "http://%s%s", ctxt->hostname, cur); |
| ctxt->location = xmlMemStrdup(buf); |
| } else { |
| ctxt->location = xmlMemStrdup(cur); |
| } |
| } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"WWW-Authenticate:", 17)) { |
| cur += 17; |
| while ((*cur == ' ') || (*cur == '\t')) cur++; |
| if (ctxt->authHeader != NULL) |
| xmlFree(ctxt->authHeader); |
| ctxt->authHeader = xmlMemStrdup(cur); |
| } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Proxy-Authenticate:", 19)) { |
| cur += 19; |
| while ((*cur == ' ') || (*cur == '\t')) cur++; |
| if (ctxt->authHeader != NULL) |
| xmlFree(ctxt->authHeader); |
| ctxt->authHeader = xmlMemStrdup(cur); |
| #ifdef HAVE_ZLIB_H |
| } else if ( !xmlStrncasecmp( BAD_CAST line, BAD_CAST"Content-Encoding:", 17) ) { |
| cur += 17; |
| while ((*cur == ' ') || (*cur == '\t')) cur++; |
| if ( !xmlStrncasecmp( BAD_CAST cur, BAD_CAST"gzip", 4) ) { |
| ctxt->usesGzip = 1; |
| |
| ctxt->strm = xmlMalloc(sizeof(z_stream)); |
| |
| if (ctxt->strm != NULL) { |
| ctxt->strm->zalloc = Z_NULL; |
| ctxt->strm->zfree = Z_NULL; |
| ctxt->strm->opaque = Z_NULL; |
| ctxt->strm->avail_in = 0; |
| ctxt->strm->next_in = Z_NULL; |
| |
| inflateInit2( ctxt->strm, 31 ); |
| } |
| } |
| #endif |
| } |
| } |
| |
| /** |
| * RxmlNanoHTTPConnectAttempt: |
| * @addr: a socket address structure |
| * |
| * Attempt a connection to the given IP:port endpoint. It forces |
| * non-blocking semantic on the socket, and allow 60 seconds for |
| * the host to answer. |
| * |
| * Returns -1 in case of failure, the file descriptor number otherwise |
| */ |
| |
| static int |
| RxmlNanoHTTPConnectAttempt(struct sockaddr *addr) |
| { |
| SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); |
| fd_set wfd, rfd; |
| struct timeval tv; |
| int status = 0; |
| double used = 0.0; |
| |
| if (s==-1) { |
| #ifdef DEBUG_HTTP |
| perror("socket"); |
| #endif |
| RxmlMessage(0, "socket failed"); |
| return(-1); |
| } |
| |
| #ifdef _WINSOCKAPI_ |
| { |
| u_long one = 1; |
| |
| status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0; |
| } |
| #else /* _WINSOCKAPI_ */ |
| #if defined(VMS) |
| { |
| int enable = 1; |
| status = ioctl(s, FIONBIO, &enable); |
| } |
| #else /* VMS */ |
| #ifdef HAVE_FCNTL |
| if ((status = fcntl(s, F_GETFL, 0)) != -1) { |
| #ifdef O_NONBLOCK |
| status |= O_NONBLOCK; |
| #else /* O_NONBLOCK */ |
| #ifdef F_NDELAY |
| status |= F_NDELAY; |
| #endif /* F_NDELAY */ |
| #endif /* !O_NONBLOCK */ |
| status = fcntl(s, F_SETFL, status); |
| } |
| #endif |
| if (status < 0) { |
| #ifdef DEBUG_HTTP |
| perror("nonblocking"); |
| #endif |
| RxmlMessage(0, "error setting non-blocking IO"); |
| closesocket(s); |
| return(-1); |
| } |
| #endif /* !VMS */ |
| #endif /* !_WINSOCKAPI_ */ |
| |
| |
| if ((connect(s, addr, sizeof(*addr))==-1)) { |
| |
| switch (socket_errno()) { |
| case EINPROGRESS: |
| case EWOULDBLOCK: |
| #if defined(WSAEINPROGRESS) && WSAEINPROGRESS != EINPROGRESS |
| case WSAEINPROGRESS: |
| #endif |
| #if defined(WSAEWOULDBLOCK) && WSAEWOULDBLOCK != EWOULDBLOCK |
| case WSAEWOULDBLOCK: |
| #endif |
| break; |
| default: |
| perror("connect"); |
| closesocket(s); |
| return(-1); |
| } |
| } |
| |
| while(1) { |
| int maxfd = 0; |
| struct timeval tv_save; /* select() is destructive on Linux, so save tv */ |
| R_ProcessEvents(); |
| #ifdef Unix |
| if(R_wait_usec > 0) { |
| tv.tv_sec = 0; |
| tv.tv_usec = R_wait_usec; |
| } else { |
| tv.tv_sec = timeout; |
| tv.tv_usec = 0; |
| } |
| #elif defined(Win32) |
| tv.tv_sec = 0; |
| tv.tv_usec = 2e5; |
| #else |
| tv.tv_sec = timeout; |
| tv.tv_usec = 0; |
| #endif |
| |
| tv_save = tv; |
| |
| #ifdef Unix |
| maxfd = setSelectMask(R_InputHandlers, &rfd); |
| #else |
| FD_ZERO(&rfd); |
| #endif |
| FD_ZERO(&wfd); |
| FD_SET(s, &wfd); |
| if(maxfd < s) maxfd = s; |
| |
| switch(R_SelectEx(maxfd+1, &rfd, &wfd, NULL, &tv, NULL)) |
| { |
| case 0: |
| /* Time out */ |
| RxmlMessage(0, "Connect attempt timed out"); |
| used += tv_save.tv_sec + 1e-6 * tv_save.tv_usec; |
| if(used < timeout) continue; |
| closesocket(s); |
| return(-1); |
| case -1: |
| /* Ermm.. ?? */ |
| RxmlMessage(0, "Connect failed"); |
| closesocket(s); |
| return(-1); |
| } |
| |
| if ( FD_ISSET(s, &wfd) ) { |
| R_SOCKLEN_T len; |
| len = sizeof(status); |
| if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&status, &len) < 0){ |
| /* Solaris error code */ |
| return (-1); |
| } |
| if ( status ) { |
| RxmlMessage(0, "Error connecting to remote host"); |
| closesocket(s); |
| errno = status; |
| return (-1); |
| } else return(s); |
| #ifdef Unix |
| } else { /* some other handler needed */ |
| InputHandler *what; |
| what = getSelectedHandler(R_InputHandlers, &rfd); |
| if(what != NULL) what->handler((void*) NULL); |
| continue; |
| #endif |
| } |
| } |
| /* not reached |
| return(-1); */ |
| } |
| |
| /** |
| * RxmlNanoHTTPConnectHost: |
| * @host: the host name |
| * @port: the port number |
| * |
| * Attempt a connection to the given host:port endpoint. It tries |
| * the multiple IP provided by the DNS if available. |
| * |
| * Returns -1 in case of failure, the file descriptor number otherwise |
| */ |
| |
| static int |
| RxmlNanoHTTPConnectHost(const char *host, int port) |
| { |
| struct hostent *h; |
| struct sockaddr *addr; |
| struct in_addr ia; |
| struct sockaddr_in sockin; |
| #ifdef SUPPORT_IP6 |
| struct in6_addr ia6; |
| struct sockaddr_in6 sockin6; |
| #endif |
| int i; |
| int s; |
| |
| #if defined(SUPPORT_IP6) && defined(RES_USE_INET6) |
| if (!(_res.options & RES_INIT)) |
| res_init(); |
| _res.options |= RES_USE_INET6; |
| #endif |
| h=gethostbyname(host); |
| if (h==NULL) |
| { |
| RxmlMessage(2, _("unable to resolve '%s'"), host); |
| return(-1); |
| } |
| |
| for(i=0; h->h_addr_list[i]; i++) |
| { |
| if (h->h_addrtype == AF_INET) { |
| /* A records (IPv4) */ |
| if ((unsigned int)h->h_length > sizeof(ia)) |
| continue; |
| memcpy(&ia, h->h_addr_list[i], h->h_length); |
| #ifdef _WIN32 |
| // this is a u_short |
| sockin.sin_family = h->h_addrtype; |
| #else |
| // this is usually a unsigned char. |
| sockin.sin_family = (sa_family_t) h->h_addrtype; |
| #endif |
| sockin.sin_addr = ia; |
| sockin.sin_port = htons(port); |
| addr = (struct sockaddr *)&sockin; |
| #ifdef SUPPORT_IP6 |
| } else if (h->h_addrtype == AF_INET6) { |
| /* AAAA records (IPv6) */ |
| if ((unsigned int)h->h_length > sizeof(ia6)) |
| continue; |
| memcpy(&ia6, h->h_addr_list[i], h->h_length); |
| sockin6.sin6_family = h->h_addrtype; |
| sockin6.sin6_addr = ia6; |
| sockin6.sin6_port = htons(port); |
| addr = (struct sockaddr *)&sockin6; |
| #endif |
| } else |
| break; /* for */ |
| |
| s = RxmlNanoHTTPConnectAttempt(addr); |
| if (s != -1) { |
| RxmlMessage(1, _("connected to '%s' on port %d."), host, port); |
| return(s); |
| } |
| } |
| |
| RxmlMessage(2, _("unable to connect to '%s' on port %d."), host, port); |
| return(-1); |
| } |
| |
| |
| /** |
| * RxmlNanoHTTPOpen: |
| * @URL: The URL to load |
| * @contentType: if available the Content-Type information will be |
| * returned at that location |
| * |
| * @headers: headers to be used in the HTTP request. These must be name/value |
| * pairs separated by ':', each on their own line. |
| * |
| * This function try to open a connection to the indicated resource |
| * via HTTP GET. |
| * |
| * Returns NULL in case of failure, otherwise a request handler. |
| * The contentType, if provided must be freed by the caller |
| */ |
| |
| void* |
| RxmlNanoHTTPOpen(const char *URL, char **contentType, const char *headers, |
| int cacheOK) |
| { |
| if (contentType != NULL) *contentType = NULL; |
| return RxmlNanoHTTPMethod(URL, NULL, NULL, contentType, headers, cacheOK); |
| } |
| |
| /** |
| * RxmlNanoHTTPRead: |
| * @ctx: the HTTP context |
| * @dest: a buffer |
| * @len: the buffer length |
| * |
| * This function tries to read @len bytes from the existing HTTP connection |
| * and saves them in @dest. This is a blocking call. |
| * |
| * Returns the number of byte read. 0 is an indication of an end of connection. |
| * -1 indicates a parameter error. |
| */ |
| int |
| RxmlNanoHTTPRead(void *ctx, void *dest, int len) |
| { |
| RxmlNanoHTTPCtxtPtr ctxt = (RxmlNanoHTTPCtxtPtr) ctx; |
| |
| #ifdef HAVE_ZLIB_H |
| int bytes_read = 0; |
| int orig_avail_in; |
| int z_ret; |
| #endif |
| |
| if (ctx == NULL) return(-1); |
| if (dest == NULL) return(-1); |
| if (len <= 0) return(0); |
| |
| #ifdef HAVE_ZLIB_H |
| if (ctxt->usesGzip == 1) { |
| if (ctxt->strm == NULL) return(0); |
| |
| ctxt->strm->next_out = dest; |
| ctxt->strm->avail_out = len; |
| |
| do { |
| orig_avail_in = ctxt->strm->avail_in = ctxt->inptr - ctxt->inrptr - bytes_read; |
| ctxt->strm->next_in = BAD_CAST (ctxt->inrptr + bytes_read); |
| |
| z_ret = inflate(ctxt->strm, Z_NO_FLUSH); |
| bytes_read += orig_avail_in - ctxt->strm->avail_in; |
| |
| if (z_ret != Z_OK) break; |
| } while (ctxt->strm->avail_out > 0 && RxmlNanoHTTPRecv(ctxt) > 0); |
| |
| ctxt->inrptr += bytes_read; |
| return(len - ctxt->strm->avail_out); |
| } |
| #endif |
| |
| while (ctxt->inptr - ctxt->inrptr < len) { |
| if (RxmlNanoHTTPRecv(ctxt) <= 0) break; |
| } |
| if (ctxt->inptr - ctxt->inrptr < len) |
| len = (int)(ctxt->inptr - ctxt->inrptr); |
| memcpy(dest, ctxt->inrptr, len); |
| ctxt->inrptr += len; |
| return(len); |
| } |
| |
| static void base64_encode(char *proxyUser, char *out) |
| { |
| /* Conversion table. */ |
| static char tbl[64] = { |
| 'A','B','C','D','E','F','G','H', |
| 'I','J','K','L','M','N','O','P', |
| 'Q','R','S','T','U','V','W','X', |
| 'Y','Z','a','b','c','d','e','f', |
| 'g','h','i','j','k','l','m','n', |
| 'o','p','q','r','s','t','u','v', |
| 'w','x','y','z','0','1','2','3', |
| '4','5','6','7','8','9','+','/' |
| }; |
| char *s = proxyUser; |
| int i, length; |
| unsigned char *p = (unsigned char *)out; |
| |
| length = (int) strlen(s); |
| for (i = 0; i < length; i += 3) { |
| *p++ = tbl[s[i] >> 2]; |
| *p++ = tbl[((s[i] & 3) << 4) + (s[i+1] >> 4)]; |
| *p++ = tbl[((s[i+1] & 0xf) << 2) + (s[i+2] >> 6)]; |
| *p++ = tbl[s[i+2] & 0x3f]; |
| } |
| /* Pad the result if necessary... */ |
| if (i == length + 1) *(p - 1) = '='; |
| else if (i == length + 2) *(p - 1) = *(p - 2) = '='; |
| /* ...and zero-terminate it. */ |
| *p = '\0'; |
| } |
| |
| /** |
| * RxmlNanoHTTPClose: |
| * @ctx: the HTTP context |
| * |
| * This function closes an HTTP context, it ends up the connection and |
| * free all data related to it. |
| */ |
| void |
| RxmlNanoHTTPClose(void *ctx) |
| { |
| RxmlNanoHTTPCtxtPtr ctxt = (RxmlNanoHTTPCtxtPtr) ctx; |
| |
| if (ctx == NULL) return; |
| |
| RxmlNanoHTTPFreeCtxt(ctxt); |
| } |
| |
| /** |
| * RxmlNanoHTTPMethod: |
| * @URL: The URL to load |
| * @method: the HTTP method to use |
| * @input: the input string if any |
| * @contentType: the Content-Type information IN and OUT |
| * @headers: the extra headers |
| * |
| * This function try to open a connection to the indicated resource |
| * via HTTP using the given @method, adding the given extra headers |
| * and the input buffer for the request content. |
| * |
| * Returns NULL in case of failure, otherwise a request handler. |
| * The contentType, if provided must be freed by the caller |
| */ |
| |
| void* |
| RxmlNanoHTTPMethod(const char *URL, const char *method, const char *input, |
| char **contentType, const char *headers, const int cacheOK) |
| { |
| RxmlNanoHTTPCtxtPtr ctxt; |
| char *bp, *p; |
| int blen, ilen, ret; |
| int head; |
| int nbRedirects = 0; |
| #ifdef Win32 |
| int nAuthenticate = 0; |
| #endif |
| char *redirURL = NULL; |
| char buf[1000]; |
| |
| if (URL == NULL) return(NULL); |
| if (method == NULL) method = "GET"; |
| RxmlNanoHTTPInit(); |
| |
| retry: |
| if (redirURL == NULL) |
| ctxt = RxmlNanoHTTPNewCtxt(URL); |
| else { |
| ctxt = RxmlNanoHTTPNewCtxt(redirURL); |
| xmlFree(redirURL); |
| redirURL = NULL; |
| } |
| |
| if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) { |
| RxmlMessage(0, "Not a valid HTTP URI"); |
| RxmlNanoHTTPFreeCtxt(ctxt); |
| if (redirURL != NULL) xmlFree(redirURL); |
| return(NULL); |
| } |
| if (ctxt->hostname == NULL) { |
| RxmlMessage(0, "Failed to identify host in URI"); |
| RxmlNanoHTTPFreeCtxt(ctxt); |
| return(NULL); |
| } |
| if (proxy) { |
| blen = (int) strlen(ctxt->hostname) * 2 + 16; |
| ret = RxmlNanoHTTPConnectHost(proxy, proxyPort); |
| } |
| else { |
| blen = (int) strlen(ctxt->hostname); |
| ret = RxmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port); |
| } |
| if (ret < 0) { |
| RxmlNanoHTTPFreeCtxt(ctxt); |
| return(NULL); |
| } |
| ctxt->fd = ret; |
| |
| if (input != NULL) { |
| ilen = (int) strlen(input); |
| blen += ilen + 32; |
| } |
| else |
| ilen = 0; |
| if (!cacheOK) |
| blen += 20; |
| if (headers != NULL) |
| blen += (int) strlen(headers) + 2; |
| if (contentType && *contentType) |
| blen += (int) strlen(*contentType) + 16; |
| if (proxy && proxyUser) { |
| base64_encode(proxyUser, buf); |
| blen += (int )strlen(buf) + 50; |
| } |
| blen += (int) strlen(method) + (int) strlen(ctxt->path) + 23; |
| #ifdef HAVE_ZLIB_H |
| blen += 23; |
| #endif |
| p = bp = xmlMalloc(blen + 8); |
| memset(p, 0, blen + 8); |
| if (proxy) { |
| if (ctxt->port != 80) { |
| p += snprintf( p, blen - (p - bp), "%s http://%s:%d%s", |
| method, ctxt->hostname, |
| ctxt->port, ctxt->path ); |
| } |
| else |
| p += snprintf( p, blen - (p - bp), "%s http://%s%s", method, |
| ctxt->hostname, ctxt->path); |
| } |
| else |
| p += snprintf(p, blen - (p - bp), "%s %s", method, ctxt->path); |
| |
| p += snprintf(p, blen - (p - bp), " HTTP/1.0\r\nHost: %s\r\n", |
| ctxt->hostname); |
| |
| #ifdef HAVE_ZLIB_H |
| p += snprintf(p, blen - (p - bp), "Accept-Encoding: gzip\r\n"); |
| #endif |
| |
| if(!cacheOK) |
| p += snprintf(p, blen - (p - bp), "Pragma: no-cache\r\n"); |
| if(proxy && proxyUser) |
| p += snprintf(p, blen - (p - bp),"Proxy-Authorization: Basic %s\r\n", |
| buf); |
| if (contentType != NULL && *contentType) |
| p += snprintf(p, blen - (p - bp), "Content-Type: %s\r\n", *contentType); |
| if (headers != NULL) |
| p += snprintf(p, blen - (p - bp), "%s", headers); |
| if (input != NULL) |
| |
| snprintf(p, blen - (p - bp), "Content-Length: %d\r\n\r\n%s", |
| ilen, input); |
| else |
| snprintf(p, blen - (p - bp), "\r\n"); |
| RxmlMessage(0, "-> %s%s", proxy? "(Proxy) " : "", bp); |
| if ((blen -= (int) strlen(bp)+1) < 0) |
| RxmlMessage(0, "ERROR: overflowed buffer by %d bytes\n", -blen); |
| ctxt->outptr = ctxt->out = bp; |
| ctxt->state = XML_NANO_HTTP_WRITE; |
| RxmlNanoHTTPSend(ctxt); |
| ctxt->state = XML_NANO_HTTP_READ; |
| head = 1; |
| |
| while ((p = RxmlNanoHTTPReadLine(ctxt)) != NULL) { |
| if (head && (*p == 0)) { |
| head = 0; |
| ctxt->content = ctxt->inrptr; |
| xmlFree(p); |
| break; |
| } |
| RxmlNanoHTTPScanAnswer(ctxt, p); |
| |
| RxmlMessage(0, "<- %s\n", p); |
| xmlFree(p); |
| } |
| |
| #ifdef Win32 |
| /* Prompt for username/password again if status was proxy |
| authentication failure */ |
| if(proxy && !nAuthenticate && ctxt->returnValue == 407) { |
| char *env; |
| REprintf("%s\n%s\n", "Proxy authentication failed:", |
| "\tplease re-enter the credentials or hit Cancel"); |
| R_FlushConsole(); R_ProcessEvents(); |
| env = askUserPass("Proxy Authentication"); |
| if(strlen(env)) { |
| if (proxyUser != NULL) {xmlFree(proxyUser); proxyUser = NULL;} |
| proxyUser = xmlMemStrdup(env); |
| nAuthenticate++; |
| goto retry; |
| } |
| } |
| #endif |
| |
| if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) && |
| (ctxt->returnValue < 400)) { |
| if(strncmp(ctxt->location, "https://", 8) == 0) |
| RxmlMessage(2, _("\"internal\" method cannot handle https redirection to: '%s'"), |
| ctxt->location); |
| else |
| RxmlMessage(1, _("redirect to: '%s'"), ctxt->location); |
| while (RxmlNanoHTTPRecv(ctxt)) |
| ; // clang likes this on a separate line |
| if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) { |
| nbRedirects++; |
| redirURL = xmlMemStrdup(ctxt->location); |
| fflush(stdout); |
| RxmlNanoHTTPFreeCtxt(ctxt); |
| goto retry; |
| } |
| RxmlNanoHTTPFreeCtxt(ctxt); |
| RxmlMessage(2, _("too many redirects, aborting ...")); |
| return(NULL); |
| |
| } |
| |
| if (contentType != NULL) { |
| if (ctxt->contentType != NULL) |
| *contentType = xmlMemStrdup(ctxt->contentType); |
| else |
| *contentType = NULL; |
| } |
| |
| if (ctxt->contentType != NULL) |
| RxmlMessage(1, "Code %d, content-type '%s'", |
| ctxt->returnValue, ctxt->contentType); |
| else |
| RxmlMessage(1, "Code %d, no content-type", |
| ctxt->returnValue); |
| |
| return((void *) ctxt); |
| } |
| |
| |
| |
| /** |
| * RxmlNanoHTTPReturnCode: |
| * @ctx: the HTTP context |
| * |
| * Returns the HTTP return code for the request. |
| */ |
| int |
| RxmlNanoHTTPReturnCode(void *ctx) |
| { |
| RxmlNanoHTTPCtxtPtr ctxt = (RxmlNanoHTTPCtxtPtr) ctx; |
| |
| if (ctxt == NULL) return(-1); |
| |
| return(ctxt->returnValue); |
| } |
| |
| char * |
| RxmlNanoHTTPStatusMsg(void *ctx) |
| { |
| RxmlNanoHTTPCtxtPtr ctxt = (RxmlNanoHTTPCtxtPtr) ctx; |
| return(ctxt->statusMsg); |
| } |
| |
| DLsize_t |
| RxmlNanoHTTPContentLength(void *ctx) |
| { |
| RxmlNanoHTTPCtxtPtr ctxt = (RxmlNanoHTTPCtxtPtr) ctx; |
| |
| if (ctxt == NULL) return(-1); |
| |
| return(ctxt->contentLength); |
| } |
| |
| char * |
| RxmlNanoHTTPContentType(void *ctx) |
| { |
| RxmlNanoHTTPCtxtPtr ctxt = (RxmlNanoHTTPCtxtPtr) ctx; |
| return(ctxt->contentType); |
| } |