/* httpsync.c version 3.00 Copyright 2001 Forrest J. Cavalier III
See http://www.mibsoftware.com/httpsync/ for documentation and
original copies of this software.
Contact Mib Software (mibsoft@mibsoftware.com) to discuss custom
and enhanced versions of this and other software. We would appreciate
that you notify us of defect discoveries and enhancements so that
others can benefit from work on this software.
- - - - - - - - - - - - - License - - - - - - - - - - - - - - - - -
Copyright (c) 2001, Forrest J. Cavalier III, doing business as Mib Software
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
Neither the name of the author nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*****************************************************************/
/* CHANGES
Versopm 3.00 [imp] SSL features
Version 2.01 [imp] HTTP authorization
Version 2.00 [imp] Feature enhanced for automated interface.
Version 1.14 [cri] HTTPAccess_ReadHead() would hang when buffer read
so far ended \n but not \n\n
[def,opt]".." in comments (not just file names) would cause
termination with error. Using memstr() instead of strstr()
also saves CPU time in long file lists.
[def] Files ending with comments would cause unrecognized
line errors.
Version 1.13 [mai] Syntax clean up to keep some compilers happy.
Dropped register keyword in ndays(). Added casts
for a printf argument based on mode_t, and off_t.
Version 1.12 [cri] HTTP/1.0 bodies received after a delay would
write a zero length buffer through PackListAppend().
Added test for empty buffer.
Version 1.11 [cri] Modified URLs requested to eliminate '/./' within
HTTP requests. Netscape Enterprise server, for one,
rejects them.
Version 1.10 [mai] Rewrote HTTP handling, other changes for DDJ article.
Version 1.03 GENMODMASK used when generating mode values.
Preserve R lines when generating lists.
chmod to allow distribution of mode 04xx files.
Need to chmod after transfer.
Incorporate umask into chmod call.
Version 1.02: When could not open destination file for writing,
close socket before continuing with next file.
Obsolete processing: rmdir() and unlink() return
codes checked and warning printed.
MODMASK allows x bit to be ignored on MS-DOS file
systems.
Version 1.01: Features added. Obsolete file removal, file modes,
HTTP/1.1 transfers.
Version 1.00: Introduced.
*/
/*****************************************************************/
/* Setup HTTPSYNC compilation environment. Handle various operating
system differences.
*/
#ifdef NeXT
#define NO_INC_MALLOC
#endif
#ifndef NO_INC_MALLOC
#include <malloc.h>
#endif
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <memory.h>
#include <stddef.h>
#include <stdlib.h>
#include <sys/types.h>
/**********************************************************/
/* WinSock and BSD-style sockets portability */
/* Macros and conditionals for portable sockets code
* 1. Write code using readsocket, writesocket, closesocket,
* INVALID_SOCKET, SOCKET_ERROR, SOCKET, and INADDR_NONE
* 2. Always use SocketStartup() and SocketCleanup()
* 3. Conditional includes and definitions for those macros
* allow operation under Windows and BSD-style sockets.
*/
#ifdef WIN32 /* Windows systems */
#include <winsock.h>
#include <io.h>
#define readsocket(a,b,c) recv(a,b,c,0)
#define writesocket(a,b,c) send(a,b,c,0)
/* closesocket() does not need a macro.
* INVALID_SOCKET, SOCKET_ERROR, SOCKET, and INADDR_NONE
* are already defined in winsock.h
*/
WSADATA libmibWSAdata;
#define SocketStartup() \
if (WSAStartup(0x101,&libmibWSAdata)) exit(-1)
#define SocketCleanup() WSACleanup()
#else /* Unix-style systems */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define readsocket read
#define writesocket write
#define closesocket close
#define SocketStartup()
#define SocketCleanup()
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#define SOCKET int
/* define INADDR_NONE if not already */
#ifndef INADDR_NONE
#define INADDR_NONE ((unsigned long) -1)
#endif
#endif
/*****************************/
/* Additional macros and environment for portable
* HTTPsync code
*/
#ifdef WIN32
#include <direct.h>
#include <sys/utime.h>
#define TDIFFLIMIT 2 /* MS-DOS file timestamp resolution */
#define MODMASK 0600 /* When comparing, don't check Execute, group,
or other bits */
#define GENMODMASK 0644 /* When generating, use only rw-r-r bits */
#define strncasecmp strnicmp
#define portable_mkdir(d,perm) mkdir(d)
#define FOPEN_WRITE_BINARY "wb"
#ifdef __MINGW32__ /* Mingw32/egcs on Win32.Thx to Ron Aaron */
#include <errno.h>
#endif
#else /* Unix-like systems */
#include <sys/ioctl.h>
#include <unistd.h>
#include <utime.h>
#include <errno.h>
#define portable_mkdir(d,perm) mkdir(d,perm)
#define FOPEN_WRITE_BINARY "w"
#define TDIFFLIMIT 1
#define MODMASK 0700 /* Check only that owner modes match */
#define GENMODMASK 0755 /* For generating packing lists. */
#endif
#include <assert.h>
#ifdef NeXT
#include <objc/hashtable.h>
#include <bsd/sys/time.h>
#define utimbuf myutimbuf
struct myutimbuf {
time_t actime;
time_t modtime;
};
#define strdup NXCopyStringBuffer
#endif
/*****************************************************************/
/* -- End setup of compilation environment. There should be no O/S
* dependent conditionals below this line.
*/
/*****************************************************************/
#ifdef USE_SSLEAY
#include "rsa.h" /* SSLeay stuff */
#include "crypto.h"
#include "x509.h"
#include "pem.h"
#include "ssl.h"
#include "err.h"
#define do_SSL_CTX_new() SSL_CTX_new()
#define ENABLE_SSL
#endif
#ifdef USE_OPENSSL
#include "openssl/rsa.h"
#include "openssl/crypto.h"
#include "openssl/x509.h"
#include "openssl/pem.h"
#include "openssl/ssl.h"
#include "openssl/err.h"
#define do_SSL_CTX_new() SSL_CTX_new(SSLv23_client_method())
#define ENABLE_SSL
#endif
#ifndef ENABLE_SSL
#define SSL_CTX void
#define SSL void
#define SSL_free(a)
#define SSL_CTX_free(a)
#define do_SSL_CTX_new()
#define SSL_load_error_strings() 0
#define SSL_read(a,b,c) 0
#define SSL_write(a,b,c) 0
#endif
/* NOTE: At runtime, must be able to find crypt32.dll and ssl32.dll
from SSLeay.
*/
/*****************************************************************/
/* Predeclarations. These functions are defined in this file after
they are referenced.
*/
int ssgmtime(const char *sptr,time_t *time);
int countbl(const char *pStart); /* count consecutive ' ' and '\t' */
int counttoch(const char *pStart,char ch); /* count to \0 or ch */
char *memstr(const char *pBlock,const char *pSubstring,int cbBlock);
int counttowh(const char *pStart); /* count to \0 ' ' '\t' '\r' or '\n' */
char *astrn0cpy(char **asz,const char *src,int len);
char *astrn0cat(char **asz,const char *src,int len);
char *URIunescape(char *asz); /* converts %xx */
time_t tm_to_time (struct tm *tp);
#define PACKLISTVER 200 /* 100 does not support 'R', 'O' tags
101 still the default format
200 supports % escaped file names
*/
int USEVER = PACKLISTVER;
int bVERBOSE = 0;
int umaskval;
/**************************************************************/
int bAUTOMATED = 0; /* -a option */
char *aszWorkDest = 0; /* -t option */
int cbTempdir;
long cbAllFiles = 0;
long cAllFiles = 0;
long cbCompleted = 0;
long cFileCompleted = 0;
/**************************************************************/
/**************************************************************/
/* Arguments which are processed from command line */
char *pszURI;
/**************************************************************/
/* HTTPaccess implements a reusable method of making multiple
requests to an HTTP server. Tries HTTP/1.1 and falls back
to HTTP/1.0 after a number of failed HTTP/1.1 attempts.
For example use, see mainHTTPSYNC(), basically call
HTTPaccess_Initialize() to initialize a structure with
the host names of servers, then make multiple requests
through HTTPaccess_Retrieve()
For advanced usage, customize HTTPaccess_Retrieve() or
HTTPaccess_SendRequest()
*/
typedef char EXPLAIN; /* For return values that are error
explanations including a decimal ASCII
number at position 2. Success is
returned as 0 (NULL).
*/
struct HTTPaccess_s {
SOCKET s;
int bPERSIST; /* already a connection open */
int bHTTP1_1; /* Attempt connection as HTTP/1.1 */
struct sockaddr_in saddr;
int bSKIPDNS; /* Set nonzero when saddr is filled in,
the lookup is done once */
const char *pszHost;
int nPort;
char *pszAuthUser;
char *pszAuthPass;
const char *pszProxy;
int nProxyPort;
long cbContent; /* Content length, or one of the following */
#define HTTPaccess_CHUNKED -1
#define HTTPaccess_UNTILCLOSE -2
int cbInbuf;
char *buf;
int cbBuf;
int cAttempt;
SSL_CTX* ctx;
SSL* ssl;
};
void HTTPaccess_Initialize(struct HTTPaccess_s *pHTA,
char * buf,int cbBuf)
{ /* buf must be large enough to hold all response
headers, or error will be returned.
*/
pHTA->bHTTP1_1 = 1; /* default. Falls back automatically. */
pHTA->bSKIPDNS = 0;
pHTA->pszProxy = 0;
pHTA->pszHost = 0;
pHTA->bPERSIST = 0;
pHTA->nPort = 80; /* default */
pHTA->buf = buf;
pHTA->cbBuf = cbBuf;
pHTA->ssl = 0;
pHTA->pszAuthUser = 0;
pHTA->pszAuthPass = 0;
} /* HTTPaccess_Initialize */
const char *HTTPaccess_ParseURL(struct HTTPaccess_s *pHTA,const char *pszURL,const char *pszProxy)
{
char *ptr;
pHTA->nPort = 80; /* http default */
if (!strncasecmp(pszURL,"http://",7)) {
pHTA->pszHost = pszURL+7;
} else if (!strncasecmp(pszURL,"https://",8)) {
pHTA->pszHost = pszURL+8;
pHTA->ssl = (struct ssl_st *) 1; /* Will be replaced at OpenConn */
pHTA->nPort = 443;
} else {
return "E-1-Invalid URL syntax";
}
/* Strip out the host */
if (!(ptr = strchr(pHTA->pszHost,'/'))) {
return "E-1-Invalid URL syntax";
}
*ptr = '\0';
pszURI = ptr+1;
if ((ptr = strchr(pHTA->pszHost,':'))) {
*ptr = 0;
pHTA->nPort = atoi(ptr+1);
}
if (pszProxy) {
pHTA->nProxyPort = 443;
if ((ptr = strchr(pszProxy,':'))) {
*ptr = 0;
pHTA->nProxyPort = atoi(ptr+1);
}
}
return 0;
} /* HTTPaccess_ParseURL */
EXPLAIN *HTTPaccess_OpenConn(struct HTTPaccess_s *pHTA)
{ /* A "helper" function called by HTTPaccess_SendRequest */
/* Returns error explanation */
/* Lookup host name */
const char *pszHost = pHTA->pszHost;
int nPort = pHTA->nPort;
struct hostent *phostent;
if (pHTA->bPERSIST) { /* Already a HTTP/1.1 persistent
connection open */
return 0;
}
if (pHTA->pszProxy) {
pszHost = pHTA->pszProxy;
nPort = pHTA->nProxyPort;
}
if (!pHTA->bSKIPDNS) {
phostent = gethostbyname(pszHost);
pHTA->saddr.sin_family = PF_INET;
pHTA->saddr.sin_port = htons(nPort);
if (!phostent) {
pHTA->saddr.sin_addr.s_addr = inet_addr(pszHost);
if (pHTA->saddr.sin_addr.s_addr == INADDR_NONE) {
if (bVERBOSE) {
fprintf(stderr,"Could not resolve hostname '%s'\n",pszHost);
}
return "E-3-HTTPaccess_OpenConn: could not resolve name";
}
} else {
pHTA->saddr.sin_addr.s_addr =
*((u_long *) phostent->h_addr);
}
}
pHTA->s = socket(PF_INET,SOCK_STREAM,0);
if (pHTA->s == INVALID_SOCKET) {
return "E-1-HTTPaccess_OpenConn: could not get socket";
}
/* And connect */
pHTA->bSKIPDNS = 0;
if (bVERBOSE) {
printf("[connect]");
}
if (connect(pHTA->s,
(struct sockaddr *) &(pHTA->saddr),
sizeof(pHTA->saddr)) == SOCKET_ERROR) {
closesocket(pHTA->s);
if (pHTA->ssl && (pHTA->ssl != (void *) 1)) {
SSL_free (pHTA->ssl);
SSL_CTX_free (pHTA->ctx);
}
return "E-2-HTTPaccess_OpenConn: could not connect";
}
pHTA->bSKIPDNS = 1;
#ifdef ENABLE_SSL
if (pHTA->ssl) {
int err;
if (pHTA->pszProxy) {
int i;
int imax = strlen(pHTA->pszHost)+1024 ;
/* Use the CONNECT method, as documented at
http://developer.netscape.com/docs/manuals/proxy/ProxyUnx/SSL-TUNL.HTM
*/
char *buf = malloc(imax);
if (!buf) {
return "E-6-HTTPaccess_OpenConn: Could not malloc";
}
sprintf(buf,"CONNECT %s:%d HTTP/1.0\r\n\r\n",pHTA->pszHost,pHTA->nPort);
writesocket(pHTA->s,buf,strlen(buf));
if (bVERBOSE) {
fprintf(stderr,"%s",buf);
}
/* Read until get a blank line or socket close */
i = 0;
while(i < imax-1) {
if (readsocket(pHTA->s,buf+i,1) != 1) {
break;
}
i++;
if ((i >= 4) && !strncmp(buf+i-4,"\r\n\r\n",4)) {
break;
}
if ((i >= 2) && !strncmp(buf+i-2,"\n\n",4)) {
break;
}
}
buf[i] = '\0';
if (bVERBOSE) {
fprintf(stderr,"%s",buf);
}
i = 0;
while(buf[i] > ' ') {
i++;
}
if (!buf[i]) {
free(buf);
return "E-8-HTTPaccess_OpenConn: No response from proxy";
}
if (buf[i+1] != '2') {
if (bVERBOSE) {
while(1) {
int cnt;
cnt = readsocket(pHTA->s,buf,sizeof(buf));
if (cnt <= 0) {
break;
}
fwrite(buf,1,cnt,stderr);
}
}
free(buf);
return "E-8-HTTPaccess_OpenConn: CONNECT method did not return status 2xx";
}
free(buf);
}
pHTA->ctx = do_SSL_CTX_new ();
if (!pHTA->ctx) {
if (bVERBOSE) {
char buf[256];
/* ERR_error_string_n(ERR_get_error(),buf,sizeof(buf)); */
ERR_error_string(ERR_get_error(),buf);
fprintf(stderr,"%s\n",buf);
}
return "E-4-HTTPaccess_OpenConn: SSL setup failed";
}
pHTA->ssl = SSL_new (pHTA->ctx);
if (!pHTA->ctx) {
return "E-5-HTTPaccess_OpenConn: SSL setup failed";
}
SSL_set_fd (pHTA->ssl, pHTA->s);
if (bVERBOSE) {
printf("Trying SSL_connect");
}
err = SSL_connect (pHTA->ssl);
if (err==-1) {
return "E-3-HTTPaccess_OpenConn: SSL negotiation failed";
}
if (bVERBOSE) {
X509* server_cert;
char* str;
char buf[256];
/* Following two steps are optional and not required for
data exchange to be successful. */
/* Get the cipher - opt */
printf ("SSL connection using %s\n", SSL_get_cipher (pHTA->ssl));
/* Get server's certificate (note: beware of dynamic allocation) - opt */
server_cert = SSL_get_peer_certificate (pHTA->ssl);
/* DEBUG: We should do certificate verification */
printf ("Server certificate:\n");
#ifdef USE_SSLEAY
str = X509_NAME_oneline (X509_get_subject_name (server_cert));
printf ("\t unverified subject: %s\n", str);
Free (str);
str = X509_NAME_oneline (X509_get_issuer_name (server_cert));
printf ("\t unverified issuer: %s\n", str);
Free (str);
#endif
#ifdef USE_OPENSSL
X509_NAME_oneline (X509_get_subject_name (server_cert),buf,sizeof(buf));
printf ("\t unverified subject: %s\n", buf);
str = X509_NAME_oneline (X509_get_issuer_name (server_cert),buf,sizeof(buf));
printf ("\t unverified issuer: %s\n", buf);
#endif
X509_free (server_cert);
}
}
#endif
return 0;
} /* HTTPaccess_OpenConn */
static char *vector = {
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"};
int ToBase64(char *wptr, /* Output buffer. Must have room for 4 characters. */
long *paccum,
char ch)
{ /* paccum should be initialized to 1 or 0 on the first call,
then just keep calling, At end of block, caller must pad with '='
so that the length is a multiple of 3.
Returns the number of characters written at wptr.
*/
if (!*paccum) {
*paccum = 1;
}
*paccum <<= 8;
*paccum |= ch & 0xff;
if (*paccum & 0x01000000) {
wptr[3] = vector[*paccum&0x3f];
*paccum >>= 6;
wptr[2] = vector[*paccum&0x3f];
*paccum >>= 6;
wptr[1] = vector[*paccum&0x3f];
*paccum >>= 6;
wptr[0] = vector[*paccum&0x3f];
*paccum >>= 6;
return 4;
}
return 0;
}
EXPLAIN *HTTPaccess_GenRequest(struct HTTPaccess_s *pHTA,
const char *pszURI)
{/* Generate headers for an HTTP 1.0 or 1.1 request.
NOTE: If HTTP/1.1 the caller must append a Content-length
header. See HTTPaccess_Retrieve() for an example
*/
const char *ptr;
char *wptr;
long accum;
if (strlen(pszURI)+strlen(pHTA->pszHost)*2+100+strlen(pHTA->pszAuthUser ? pHTA->pszAuthUser :"")*2+strlen(pHTA->pszAuthPass ? pHTA->pszAuthPass : "")*2 >
(unsigned) pHTA->cbBuf) {
return "E-1-HTTPaccess_GenRequest buffer too small";
}
if (pHTA->pszProxy && !pHTA->ssl) {
if (pszURI[0] == '/') {
sprintf(pHTA->buf,"GET http://%s%s",
pHTA->pszHost,pszURI);
} else {
sprintf(pHTA->buf,"GET http://%s/%s",
pHTA->pszHost,pszURI);
}
} else {
if (pszURI[0] == '/') {
sprintf(pHTA->buf,"GET %s",pszURI);
} else {
sprintf(pHTA->buf,"GET /%s",pszURI);
}
}
if (pHTA->bHTTP1_1) {
strcat(pHTA->buf," HTTP/1.1\r\n");
strcat(pHTA->buf,"Host: ");
strcat(pHTA->buf,pHTA->pszHost);
strcat(pHTA->buf,"\r\n");
} else {
strcat(pHTA->buf," HTTP/1.0\r\n");
}
if (pHTA->pszAuthUser) {
strcat(pHTA->buf,"Authorization: Basic ");
/* Make sure not overrun pHTA->buf */
accum = 1;
wptr = pHTA->buf + strlen(pHTA->buf);
if ((int) (pHTA->buf+pHTA->cbBuf - wptr) - 128 < (int) (2*(strlen(pHTA->pszAuthPass)+strlen(pHTA->pszAuthUser)))) {
return "E-2-Request too large for buffer";
}
ptr = pHTA->pszAuthUser;
while(*ptr) {
wptr += ToBase64(wptr,&accum,*ptr++);
}
wptr += ToBase64(wptr,&accum,':');
ptr = pHTA->pszAuthPass;
while(*ptr) {
wptr += ToBase64(wptr,&accum,*ptr++);
}
if (accum & 0x10000) {
ToBase64(wptr,&accum,0);
wptr[3] = '=';
wptr += 4;
} else if (accum & 0x100) {
ToBase64(wptr,&accum,0);
ToBase64(wptr,&accum,0);
wptr[2] = '=';
wptr[3] = '=';
wptr += 4;
}
*wptr = '\0';
strcat(pHTA->buf,"\r\n");
}
return 0;
}
EXPLAIN *HTTPaccess_SendRequest(struct HTTPaccess_s *pHTA,
const char *pszRequest)
{ /* Returns error explanation */
/* Ensure connection */
int ret;
char *reterr;
if ((reterr=HTTPaccess_OpenConn(pHTA))) {
if (bVERBOSE) {
if (!bAUTOMATED) {
printf("[s] 132 {%s}\n",reterr);
} else {
printf("%s\n",reterr);
}
}
return "E-1-HTTPaccess_SendRequest: Could not open connection";
}
/* Send request */
if (pHTA->ssl) {
ret = SSL_write(pHTA->ssl,pszRequest,strlen(pszRequest));
} else {
ret = writesocket(pHTA->s,pszRequest,strlen(pszRequest));
}
if (ret == SOCKET_ERROR) {
if (pHTA->bHTTP1_1) {
return "E-3-HTTPaccess_SendRequest. HTTP/1.1 write error";
}
return "E-2-HTTPaccess_SendRequest: Could not write socket";
closesocket(pHTA->s);
if (pHTA->ssl) {
SSL_free (pHTA->ssl);
SSL_CTX_free (pHTA->ctx);
}
}
return 0;
} /* HTTPaccess_SendRequest */
EXPLAIN * HTTPaccess_ReadHead(struct HTTPaccess_s *pHTA,int *pcbHead)
{/* Read from a socket, appending to a buffer until entire HTTP
* header is obtained. (In HTTP the end of header is marked by
* a blank line.)
* Returns "E-3-..." if some problem which indicates a retry is
* indicated.
* Returns "E-1-..." if some problem which indicates to not
* retry.
*/
int cnt;
char *ptr;
while(1) { /* Until there is a blank line in the buffer, which
* marks the end of headers
*/
/* See if a blank line in what was read so far */
ptr = pHTA->buf;
if ((pHTA->cbInbuf > 0) &&
((*ptr == '\r')||(*ptr == '\n')) ) {
*pcbHead = 0;
return 0;
}
while(ptr) {
ptr = memchr(ptr,'\n',pHTA->buf+pHTA->cbInbuf-ptr);
if (ptr) {
if (ptr+1 < pHTA->buf + pHTA->cbInbuf) {
ptr++;
if ((*ptr == '\r')||(*ptr == '\n')) {
*pcbHead = ptr - pHTA->buf;
return 0;
}
} else { /* 8/27/99 critical bug fix */
break; /* Need to read more */
}
}
}
if (pHTA->cbBuf - pHTA->cbInbuf <= 0) {
/* Buffer won't hold all headers */
return "E-1-HTTPaccess_ReadHead. Excessive response header\n";
}
if (pHTA->ssl) {
cnt = SSL_read(pHTA->ssl,
pHTA->buf+pHTA->cbInbuf,
pHTA->cbBuf- pHTA->cbInbuf);
} else {
cnt = readsocket(pHTA->s,
pHTA->buf+pHTA->cbInbuf,
pHTA->cbBuf- pHTA->cbInbuf);
}
if (bVERBOSE) {
printf("R%d",cnt);
}
if (cnt <= 0) {
return "E-3-HTTPaccess_ReadHead. read error\n";
}
pHTA->cbInbuf += cnt;
if (strncasecmp(pHTA->buf,"HTTP/1.",7)) {
/* No headers (or response not understood */
return "E-3-HTTPaccess_ReadHead. Confused headers\n";
}
}
} /* HTTPaccess_ReadHead */
EXPLAIN * HTTPaccess_ProcessHead(struct HTTPaccess_s *pHTA,int cbHead)
{
char *ptr;
/*******************************************/
/* Process header part of response. */
pHTA->cbContent = HTTPaccess_UNTILCLOSE; /* Default: Until socket closes */
ptr = pHTA->buf;
ptr += 7;
if (pHTA->bHTTP1_1 && (*ptr != '0')) {
/* HTTP 1.1 connections can be left open */
pHTA->bPERSIST = 1;
}
while(ptr < pHTA->buf + cbHead) {
ptr = memchr(ptr,'\n',pHTA->buf + cbHead - ptr);
if (!ptr) {
return 0;
}
ptr++;
if (!strncasecmp(ptr,"Transfer-Encoding: ",19)) {
/* For HTTP/1.1 only */
ptr += 19;
ptr += countbl(ptr);
if (strncasecmp(ptr,"chunked",7)) {
/* Don't know how to deal with anything else */
pHTA->bHTTP1_1 = 0;
closesocket(pHTA->s);
if (pHTA->ssl) {
SSL_free (pHTA->ssl);
SSL_CTX_free (pHTA->ctx);
}
pHTA->bPERSIST = 0;
return "E-3-HTTPaccess_ProcessHead. Unsupported Transfer-Encoding\n";
}
pHTA->cbContent = HTTPaccess_CHUNKED;
} else if (!strncasecmp(ptr,"Connection: ",12)) {
ptr += 12;
ptr += countbl(ptr);
if (!strncasecmp(ptr,"close",5)) {
pHTA->bPERSIST = 0;
if (bVERBOSE) {
printf("[C]");
}
}
} else if (!strncasecmp(ptr,"Content-Length: ",16)) {
ptr += 16;
ptr += countbl(ptr);
pHTA->cbContent = atoi(ptr);
if (bVERBOSE) {
printf("L%ld",pHTA->cbContent);
}
}
if (*ptr == '\r') {
ptr++;
}
if (*ptr == '\n') {
ptr++;
return 0;
}
}
return 0;
} /* HTTPaccess_ProcessHead */
EXPLAIN *HTTPaccess_ReadBody(struct HTTPaccess_s *pHTA,
int (*fn)(void *,const char *,int),
void *pID) /* passed to fn */
{ /* Read a HTTP body from a buffer and socket.
Unprocessed data left in the buffer is processed first,
then data is read from the socket.
Will stop reading when reach specified length (cbContent) or
got a chunk-size of 0.
*/
char *ptr;
int cbChunk;
int cnt;
if (pHTA->cbContent == 0) {
return 0;
}
if (pHTA->cbContent == HTTPaccess_CHUNKED) { /* Chunked Transfer Coding. */
/* See RFC 2068 Section 3.6 and 19.4.6 */
cbChunk = 0; /* At start of chunk */
while(1) {
if (cbChunk == 0) { /* Need to read chunk size */
ptr = memchr(pHTA->buf,'\n',pHTA->cbInbuf);
if (!ptr) {
goto readmore;
}
ptr++;
cbChunk = strtol(pHTA->buf,0,16);
if (bVERBOSE) {
printf("C%d",cbChunk);
}
cnt = pHTA->cbInbuf - (ptr - pHTA->buf); /* Remainder */
if (cbChunk == 0) { /* End of chunks, start of entity headers */
if (cnt > 0) {
/* Move rest of data down to base of buffer */
memmove(pHTA->buf,ptr,cnt);
pHTA->cbInbuf = cnt;
}
if (HTTPaccess_ReadHead(pHTA,&cnt)) {
return "E-3-HTTPaccess_ReadBody bad chunked encoding reading entity-headers";
}
/* Discard headers */
ptr = pHTA->buf + cnt;
if (*ptr == '\r') {
ptr++;
}
ptr++;
cnt = pHTA->cbInbuf + pHTA->buf - ptr;
if (cnt > 0) {
memmove(pHTA->buf,ptr,cnt); /* Move data down to base */
}
pHTA->cbInbuf = cnt;
return 0;
}
} else { /* Still processing a chunk */
ptr = pHTA->buf;
cnt = pHTA->cbInbuf;
}
/* Here with ptr pointing to cnt bytes of unprocessed data */
if (cnt >= cbChunk + 2) {
/* More data than chunk. There must be a CRLF following before
processing can be done.
*/
if ((*fn)(pID,ptr,cbChunk)) {
return "E-2-HTTPaccess_ReadBody callback error";
}
ptr += cbChunk;
cnt = pHTA->cbInbuf - (ptr - pHTA->buf); /* Remainder */
ptr = memchr(ptr,'\n',cnt);
if (!ptr) {
return "E-3-HTTPaccess_ReadBody bad chunked coding no CRLF at end of chunk";
}
ptr++;
cnt = pHTA->cbInbuf - (ptr - pHTA->buf); /* Remainder */
pHTA->cbInbuf = cnt;
memmove(pHTA->buf,ptr,cnt); /* Move data down to base */
cbChunk = 0;
continue; /* don't readmore yet.*/
} else { /* Write everything we have, possibly reserving 1 byte */
if (cbChunk - cnt < 1) {
cnt--; /* preserve one byte */
}
if ((*fn)(pID,ptr,cnt)) {
return "E-2-HTTPaccess_ReadBody callback error";
}
cbChunk -= cnt;
pHTA->cbInbuf = ptr + cnt - (pHTA->buf + pHTA->cbInbuf);
if (pHTA->cbInbuf > 0) {
/* Move rest of data down to base of buffer */
memmove(pHTA->buf,ptr+cnt,pHTA->cbInbuf);
}
}
readmore:;
/* Need to read more */
if (pHTA->cbInbuf >= pHTA->cbBuf) { /* No room */
return "E-3-HTTPaccess_ReadBody bad chunked coding";
}
if (pHTA->ssl) {
cnt = SSL_read(pHTA->ssl,
pHTA->buf+pHTA->cbInbuf,
pHTA->cbBuf - pHTA->cbInbuf);
} else {
cnt = readsocket(pHTA->s,
pHTA->buf+pHTA->cbInbuf,
pHTA->cbBuf - pHTA->cbInbuf);
}
if (cnt < 0) {
return "E-1-HTTPaccess_ReadBody socket read error";
}
pHTA->cbInbuf += cnt;
}
} else if (pHTA->cbContent == HTTPaccess_UNTILCLOSE) { /* Read until close */
while(1) {
if (pHTA->cbInbuf > 0) { /* Write what we have */
if ((*fn)(pID,pHTA->buf,pHTA->cbInbuf)) {
return "E-2-HTTPaccess_ReadBody callback error";
}
}
if (pHTA->ssl) {
cnt = SSL_read(pHTA->ssl,pHTA->buf,pHTA->cbBuf);
} else {
cnt = readsocket(pHTA->s,pHTA->buf,pHTA->cbBuf);
}
if (cnt < 0) {
return "E-1-HTTPaccess_ReadBody socket read error";
}
if (cnt == 0) {
return 0;
}
pHTA->cbInbuf = cnt;
}
} else { /* Read cbContent */
while(1) {
if (pHTA->cbInbuf >= pHTA->cbContent) {
/* Write partial */
if ((*fn)(pID,pHTA->buf,pHTA->cbContent)) {
return "E-2-HTTPaccess_ReadBody callback error";
}
ptr = pHTA->buf + pHTA->cbContent;
cnt = pHTA->cbInbuf - (ptr - pHTA->buf); /* Remainder */
if (cnt > 0) {
memmove(pHTA->buf,ptr,cnt);
}
pHTA->cbInbuf = cnt;
return 0;
} else { /* write everything in the buffer */
if ((pHTA->cbInbuf > 0) && (*fn)(pID,pHTA->buf,pHTA->cbInbuf)) { /* 5-25-99 */
return "E-2-HTTPaccess_ReadBody callback error";
}
pHTA->cbContent -= pHTA->cbInbuf;
}