/***************************************
This is part of frox: A simple transparent FTP proxy
Copyright (C) 2000 James Hollingshead
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
httpcache.c -- Stuff for requesting files through an external cache
(eg. squid)
***************************************/
#include <fcntl.h>
#include "common.h"
#include "cache.h"
#include "control.h"
#include "vscan.h"
static enum { STARTING, SUCCEEDING, SUCCESS, FAILURE, NONE } cache_status =
NONE;
static int offset;
static int proxy_dl_ok(int fd);
/* ------------------------------------------------------------- **
** If we are to retrieve through squid then return a fd. If not, or on
** error, return -1.
** ------------------------------------------------------------- */
int s_retr_start(const sstr * host, const sstr * file, const sstr * mdtm,
int size, int offst, int type)
{
sstr *buf;
int i, squidfd;
offset = offst;
if(config.mincachesize > size) {
cache_status = NONE;
write_log(VERBOSE,
"File too small for cache - retrieving directly");
return (-1);
}
/*Set up the HTTP command */
cache_status = STARTING;
buf = sstr_init(0);
i = sstr_apprintf(buf,
"GET ftp://%s%s%s%s%s%s%s HTTP/1.0\r\n"
"Host: %s\r\n"
"User-Agent: %s\r\n"
"X-Forwarded-For: %s\r\n",
info->anonymous ? "" : sstr_buf(info->username),
info->anonymous ? "" : ":",
info->anonymous ? "" : sstr_buf(info->passwd),
info->anonymous ? "" : "@",
sstr_buf(host), sstr_buf(file),
type == 1 ? (config.strictcache ? ";type=i" : "")
: ";type=an",
sstr_buf(host), "Frox/0.7",
inet_ntoa(info->client_control.address.sin_addr));
if(offset > 0)
sstr_apprintf(buf, "Range: bytes=%d-\r\n", offset);
sstr_ncat2(buf, "\r\n", 2);
if(i < 0 || i > BUF_LEN)
die(ERROR, "Failure building HTTP string",
421, "Failure building HTTP string", -1);
write_log(VERBOSE, "HTTP string = %s", sstr_buf(buf));
if((squidfd = connect_to_socket(&config.httpproxy, NULL,
config.pasvports)) == -1) {
write_log(ERROR,
"Unable to contact HTTP proxy. Retrieving directly");
cache_status = NONE;
sstr_free(buf);
return (-1);
}
/*Send the message to squid. We could block for a while here,
* but no-one should be sending us anything important...*/
/* fcntl(squidfd, F_SETFL, O_NONBLOCK);*/
sstr_write(squidfd, buf, 0);
sstr_free(buf);
/* Now wait for squid's answer. This is a nasty hack as it means
* more blocking.*/
if(!proxy_dl_ok(squidfd))
return -1;
vscan_new(size);
if(!vscan_parsed_reply(150, NULL))
send_cmessage(150, "Starting transfer");
/*And set everything up as if the connection was normal */
return (squidfd);
}
/* Start downloading from proxy. If failure then close fd. If success
* then wait until header is downloaded and return. All processing of
* inc_data stream can be done from here.
*
* Nasty hack. Proper solution involves changing cache.c and
* control.c/data.c to allow deferring this decision to later. Then we
* could use the central select loop rather than blocking here.*/
static int proxy_dl_ok(int fd)
{
int i;
sstr *buf = sstr_init(4096);
do {
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(fd, &readfds);
if(select(fd + 1, &readfds, NULL, NULL, NULL) == -1) {
if(errno == EINTR)
continue;
debug_perr("select");
die(0, NULL, 0, NULL, -1);
}
i = sstr_append_read(fd, buf, 4095);
if(i == -1) {
write_log(ERROR, "Error reading from http cache. "
"Aborting caching.");
cache_status = NONE;
close(fd);
sstr_free(buf);
return 0;
}
s_inc_data(buf);
} while(cache_status == STARTING || cache_status == SUCCEEDING);
switch (cache_status) {
case SUCCESS:
sstr_cat(info->client_data.buf, buf);
sstr_free(buf);
return 1;
case FAILURE:
close(fd);
cache_status = NONE;
write_log(ERROR, "Http cache unable to download file. "
"Aborting caching.");
sstr_free(buf);
return 0;
default:
die(ERROR, "Internal error, proxy_dl_ok()", 0, 0, -1);
break;
}
return 0;
}
/* ------------------------------------------------------------- **
** Strip out HTTP lines from inc, and set cache_status according
** to whether the requests fails or not. Leave any message body
** in inc.
** ------------------------------------------------------------- */
void s_inc_data(sstr * inc)
{
int i;
sstr *buf = NULL;
if(cache_status == NONE)
return;
if(cache_status == SUCCESS)
return;
if(cache_status == FAILURE) { /*Discard message body */
write_log(VERBOSE, "Discarding HTTP body after failure.");
sstr_empty(inc);
return;
}
/*Parse HTTP reply. FIXME - parse it properly! */
do {
i = sstr_chr(inc, '\n');
if(i == -1)
return; /*Incomplete line */
buf = sstr_init(MAX_LINE_LEN);
sstr_split(inc, buf, 0, i + 1);
write_log(VERBOSE, "HTTP: %s", sstr_buf(buf));
if(!sstr_ncasecmp2(buf, "HTTP", 4)) { /*Status Line */
sstr_token(buf, NULL, " ", 0);
i = sstr_atoi(buf);
if((i / 100) == 2 && (offset == 0 || i == 206))
cache_status = SUCCEEDING;
else { /*Failure - discard rest of message. */
sstr_empty(inc);
cache_status = FAILURE;
sstr_free(buf);
return;
}
} else if(sstr_getchar(buf, 0) == '\r')
cache_status =
(cache_status ==
SUCCEEDING) ? SUCCESS : FAILURE;
} while(cache_status != SUCCESS && cache_status != FAILURE);
sstr_free(buf);
return;
}
int s_retr_end(void)
{
if(cache_status == NONE)
return (-1);
if(cache_status == SUCCESS) {
if(!vscan_parsed_reply(226, NULL))
send_cmessage(226, "Transfer complete");
} else {
if(!vscan_parsed_reply(226, NULL))
send_cmessage(426, "Unable to transfer file");
}
cache_status = NONE;
return (-1);
}
syntax highlighted by Code2HTML, v. 0.9.1