/***************************************
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
localcache.c -- cache code if we are doing our own caching. This is
experimental.
***************************************/
#include <sys/stat.h>
#include <sys/un.h>
#include <fcntl.h>
#include "common.h"
#include "cache.h"
#include "control.h"
#include "vscan.h"
extern sstr *cachemgr_init(char *cd, int cs);
int file_cached(const sstr * uri, const sstr * mdtm, int size, int offset);
static sstr *socket_file;
/*FIXME These two should really not be global*/
static int fd;
static enum { CACHED, CACHING, NONE } cache_status = NONE;
int l_geninit(void)
{
socket_file = cachemgr_init(config.chroot, config.cachesize * 1024);
if(!socket_file)
return (-1);
write_log(VERBOSE, "Cachemgr socket file == %s",
sstr_buf(socket_file));
return (0);
}
/* ------------------------------------------------------------- **
** If we have the file return a fd to it. Otherwise return -1.
** ------------------------------------------------------------- */
int l_retr_start(const sstr * host, const sstr * file, const sstr * mdtm,
int size, int offset, int type)
{
static sstr *uri = NULL;
if(!uri)
uri = sstr_init(512);
if(sstr_len(mdtm) == 0) {
write_log(VERBOSE, "Server didn't like MDTM. Can't cache :(");
cache_status = NONE;
return (-1);
}
sstr_cpy2(uri, "ftp://");
if(!info->anonymous)
sstr_apprintf(uri, "%s@", sstr_buf(info->username));
sstr_cat(uri, host);
sstr_cat(uri, file);
if(type == 0)
sstr_ncat2(uri, ";type=an", 8);
if(!file_cached(uri, mdtm, size, offset)) {
write_log(INFO, "Cache miss for %s", sstr_buf(uri));
info->cached = 0;
return (-1);
}
write_log(INFO, "Cache hit for %s.", sstr_buf(uri));
info->cached = 1;
vscan_new(size);
if(!vscan_parsed_reply(150, NULL))
send_cmessage(150, "Starting transfer");
return (fd);
}
/* ------------------------------------------------------------- **
** Called whenever we get incoming data from server.
** Write data to cache.
** ------------------------------------------------------------- */
void l_inc_data(sstr * inc)
{
if(cache_status == CACHING) {
vscan_inc(inc);
sstr_write(fd, inc, 0);
}
}
int l_retr_end(void)
{
if(cache_status == CACHED) {
if(!vscan_parsed_reply(226, NULL))
send_cmessage(226, "Transfer complete");
}
if(cache_status == CACHING) {
vscan_end();
close(fd);
}
cache_status = NONE;
return (-1);
}
/* ------------------------------------------------------------- **
** Return TRUE if we have an up to date copy of the file.
** Also set global variable fd to the file if we have it, or to where
** we should write the file if we don't.
**
** Each cache file has a header of "NNNN MDTM SIZE TYPE URI\n" where
** NNNN is the number of bytes in the header not including the "NNNN ".
** MDTM and SIZE are the returns from those functions, and TYPE is 0
** for ascii, and 1 for binary.
**
** REST handling is fairly simplistic. If offset>0 and we have the
** portion of the file which is not requested then we seek to offset
** in it and overwrite with what we retrieve to complete our cached
** copy. Otherwise we give up.
** ------------------------------------------------------------- */
int file_cached(const sstr * uri, const sstr * mdtm, int size, int offset)
{
int type = 1;
/*FIXME*/ int cmgrfd;
struct sockaddr_un addr;
sstr *sbuf;
sbuf = sstr_init(MAX_LINE_LEN * 2);
sstr_apprintf(sbuf, "G %s %s %d %d %d\n", sstr_buf(uri),
sstr_buf(mdtm), size, type, offset);
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, sstr_buf(socket_file));
if((cmgrfd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
debug_perr("socket");
die(ERROR, "Error creating socket for caching", 0, NULL, -1);
}
if(connect(cmgrfd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
debug_perr("Error connecting to cache manager");
write_log(ERROR, "Unable to connect to cache manager."
"Abandoning caching");
return FALSE;
}
write(cmgrfd, sstr_buf(sbuf), sstr_len(sbuf));
switch (recv_fd(cmgrfd, &fd)) {
case 0:
debug_err("cache error");
cache_status = NONE;
close(cmgrfd);
return FALSE;
case 'A':
write_log(VERBOSE, "Unable to use caching for this d/l.");
cache_status = NONE;
close(cmgrfd);
return FALSE;
case 'R':
/*No need to set read lock. We are only here if the
complete file is cached. Cachemgr will never try and
overwrite part of a complete file -- it will unlink
it and create a new one (and we'll continue
accessing the old file) */
cache_status = CACHED;
close(cmgrfd);
return TRUE;
case 'W':
set_write_lock(fd);
cache_status = CACHING;
close(cmgrfd);
return FALSE;
default:
debug_err("Shouldn't get here");
close(cmgrfd);
die(0, NULL, 0, NULL, -1);
}
return (-1);
}
syntax highlighted by Code2HTML, v. 0.9.1