/* ratio.c Basic ratio implementation for muddleftpd
Copyright (C) 1999 Beau Kuiper
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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "ftpd.h"
#include "reply.h"
extern int doesdint;
int lockarea(int fd, int pos, int len, int locktype, int do_wait)
{
struct flock lock;
register int err;
int lockt;
lockt = (do_wait == TRUE ? F_SETLKW : F_SETLK);
lock.l_type = locktype;
lock.l_whence = SEEK_SET;
lock.l_start = pos;
lock.l_len = len;
NOSIGNALINTR(err = fcntl(fd, lockt, &lock));
if (err == -1)
{
if (errno != EAGAIN)
ERRORMSGFATAL(safe_snprintf("Locking failed: %s", strerror(errno)));
else
return(FALSE);
}
return(TRUE);
}
void lock_n_read(RATIOHANDLE *rh)
{
if (rh->ratiofilefd != 0)
{
lockarea(rh->ratiofilefd, rh->filepos, sizeof(RATIOFILEDATA), F_WRLCK, TRUE);
lseek(rh->ratiofilefd, rh->filepos, SEEK_SET);
read(rh->ratiofilefd, rh->rdat, sizeof(RATIOFILEDATA));
}
}
void write_n_unlock(RATIOHANDLE *rh)
{
if (rh->ratiofilefd != 0)
{
lseek(rh->ratiofilefd, rh->filepos, SEEK_SET);
write(rh->ratiofilefd, rh->rdat, sizeof(RATIOFILEDATA));
lockarea(rh->ratiofilefd, rh->filepos, sizeof(RATIOFILEDATA), F_UNLCK, TRUE);
}
}
int openratiofile(char *filename)
{
int fd;
if (filename == NULL)
return(-1);
fd = open(filename, O_CREAT | O_RDWR, 0600);
if (fd == -1)
{
char *err = safe_snprintf("Error opening ratio file '%s': %s", filename, strerror(errno));
log_addentry(MYLOG_INFO, NULL, err);
freewrapper(err);
}
return(fd);
}
int decoderatiostr(char *ratstr, int *pa, int *pb)
{
if (ratstr == NULL)
return(FALSE);
strtrimspace(ratstr);
return(sscanf(ratstr, "%d:%d", pa, pb) == 2);
}
/* this function really is too complex and too long, fix later */
RATIOHANDLE *ratio_loaduser(char *username, int section)
{
RATIOHANDLE *newrh = mallocwrapper(sizeof(RATIOHANDLE));
RATIOFILEDATA *filedata = mallocwrapper(sizeof(RATIOFILEDATA));
char *data, *defint, *byteratio, *fileratio;
int pos = 0;
long long defaultdownload;
int upbytes, downbytes, upfiles, downfiles, initfiles;
int flags = 0;
if (!doesdint)
goto error;
/* load the config */
loadstrfromconfig(config->configfile, section, "ratiofile", &data,
NULL);
loadstrfromconfig(config->configfile, section, "byteratios",
&byteratio, NULL);
loadstrfromconfig(config->configfile, section, "fileratios",
&fileratio, NULL);
if (fileratio)
{
if (!decoderatiostr(fileratio, &upfiles, &downfiles))
goto error;
flags |= FILECREDITS;
loadintfromconfig(config->configfile, section, "initalfiles",
&initfiles, 0);
initfiles *= downfiles;
}
if (byteratio)
{
if (!decoderatiostr(byteratio, &upbytes, &downbytes))
goto error;
flags |= BYTECREDITS;
loadstrfromconfig(config->configfile, section,
"initalbytes", &defint, "0");
sscanf(defint, "%lld", &defaultdownload);
defaultdownload *= downbytes;
}
newrh->rdat = filedata;
newrh->ratiofilefd = 0;
/* this is for non-persistant ratios */
if (!data)
goto newrecord;
newrh->ratiofilefd = openratiofile(data);
if (newrh->ratiofilefd == -1)
goto error;
lockarea(newrh->ratiofilefd, pos, sizeof(RATIOFILEDATA), F_WRLCK, TRUE);
while(read(newrh->ratiofilefd, filedata, sizeof(RATIOFILEDATA)) ==
sizeof(RATIOFILEDATA))
{
lockarea(newrh->ratiofilefd, pos, sizeof(RATIOFILEDATA), F_UNLCK, TRUE);
if (strcmp(filedata->username, username) == 0)
{
newrh->filepos = pos;
return(newrh);
}
pos += sizeof(RATIOFILEDATA);
lockarea(newrh->ratiofilefd, pos, sizeof(RATIOFILEDATA), F_WRLCK, TRUE);
}
newrecord:
/* we need to create a record, the area we want is already locked */
/* if the area gets written to disk, make sure it is clear so we don't
accidently write a clear password to disk that may have been in
the memory! */
memset(filedata, 0, sizeof(RATIOFILEDATA));
strncpy(filedata->username, username, MAXNAMELEN);
filedata->username[MAXNAMELEN] = 0;
filedata->flags = flags;
if (flags & FILECREDITS)
{
filedata->filecredits = initfiles;
filedata->fileinmult = (short int)upfiles;
filedata->fileoutmult = (short int)downfiles;
}
if (flags & BYTECREDITS)
{
filedata->downloadcredits = defaultdownload;
filedata->byteinmult = (short int)upbytes;
filedata->byteoutmult = (short int)downbytes;
}
newrh->filepos = pos;
write_n_unlock(newrh);
return(newrh);
error:
freewrapper(newrh);
freewrapper(filedata);
return(NULL);
}
/* This looks expensive!, but isn't really too bad because of kernel
writeback cache. */
void ratio_uploadbytes(RATIOHANDLE *rh, int bytes)
{
lock_n_read(rh);
if (rh->rdat->flags & BYTECREDITS)
rh->rdat->downloadcredits += bytes * rh->rdat->byteinmult;
write_n_unlock(rh);
}
int ratio_downloadbytes(RATIOHANDLE *rh, int bytes)
{
int ret = FALSE;
lock_n_read(rh);
if (rh->rdat->flags & BYTECREDITS)
{
ret = ((rh->rdat->downloadcredits - (bytes * rh->rdat->byteoutmult)) < 0LL);
if (!ret)
rh->rdat->downloadcredits -= bytes * rh->rdat->byteoutmult;
}
write_n_unlock(rh);
return(ret);
}
void ratio_uploadfile(RATIOHANDLE *rh)
{
lock_n_read(rh);
if (rh->rdat->flags & FILECREDITS)
rh->rdat->filecredits += rh->rdat->fileinmult;
write_n_unlock(rh);
}
int ratio_downloadfile(RATIOHANDLE *rh)
{
int ret = FALSE;
lock_n_read(rh);
if (rh->rdat->flags & FILECREDITS)
{
ret = (rh->rdat->fileoutmult > rh->rdat->filecredits);
if (!ret)
rh->rdat->filecredits -= rh->rdat->fileoutmult;
}
write_n_unlock(rh);
return(ret);
}
void ratio_reread(RATIOHANDLE *rh)
{
lock_n_read(rh);
write_n_unlock(rh);
}
void ratio_stat(FTPSTATE *peer, RATIOHANDLE *rh, char *replystr)
{
ratio_reread(rh);
if (rh->rdat->flags & BYTECREDITS)
{
ftp_write(peer, TRUE, 0, REPLY_RATIOBYTECREDITS(replystr, rh->rdat->downloadcredits / rh->rdat->byteoutmult));
ftp_write(peer, TRUE, 0, REPLY_RATIOBYTERATIO(replystr, rh->rdat->byteoutmult, rh->rdat->byteinmult));
}
if (rh->rdat->flags & FILECREDITS)
{
ftp_write(peer, TRUE, 0, REPLY_RATIOFILECREDITS(replystr, rh->rdat->filecredits / rh->rdat->fileoutmult));
ftp_write(peer, TRUE, 0, REPLY_RATIOFILERATIO(replystr, rh->rdat->fileoutmult, rh->rdat->fileinmult));
}
}
void ratio_settokens(RATIOHANDLE *rh, TOKENSET *ts)
{
ratio_reread(rh);
if (rh->rdat->flags & BYTECREDITS)
tokenset_settoken(ts, 'a', safe_snprintf("%lld", rh->rdat->downloadcredits / rh->rdat->byteoutmult));
if (rh->rdat->flags & FILECREDITS)
tokenset_settoken(ts, 'A', safe_snprintf("%d", rh->rdat->filecredits / rh->rdat->fileoutmult));
}
void ratio_finish(RATIOHANDLE *rh)
{
freewrapper(rh->rdat);
if (rh->ratiofilefd != 0)
close(rh->ratiofilefd);
freewrapper(rh);
}
syntax highlighted by Code2HTML, v. 0.9.1