/* 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 "ftpcmd.h"
#include "reply.h"
extern FTPCMD ftpcommandtable[];
int filter_toascii(char *data, int *len)
{
int count, len2 = *len;
char buffer2[BUFFERSIZE * 2];
int last = 0;
char *b2ptr = buffer2;
for (count = 0; count < len2; count++)
{
if ((data[count] == 10) && (last != 13))
{
*b2ptr = 13;
b2ptr++;
(*len)++;
}
*b2ptr = data[count];
last = *b2ptr;
b2ptr++;
}
memcpy(data, buffer2, *len);
return(*len);
}
int filter_fromascii(char *data, int *len)
{
char *dptr = data;
int count, len2 = *len;
for (count = 0; count < len2; count++)
{
if (data[count] != 13)
{
*dptr = data[count];
dptr++;
}
else
(*len)--;
}
return(len2);
}
int download_write(SELECTER *sel, int fd, void *peerv)
{
FTPSTATE *peer = (FTPSTATE *)peerv;
DATAPORT *d = peer->dport;
int startp, size, size2, finished = FALSE;
char indata[BUFFERSIZE * 2];
int maxsize;
/* determine maximum transfer size */
if (peer->maxtranspd_down)
maxsize = MINIMUM(peer->maxtranspd_down, BUFFERSIZE);
else if (peer->maxtranspd)
maxsize = MINIMUM(peer->maxtranspd, BUFFERSIZE);
else
maxsize = BUFFERSIZE;
/* if we have binary mode and no ratios to worry about,
use sendfile to improve performace. A huge amount of data
may be transmitted at a time using sendfile. But at the moment
sendfile in Linux REALLY SUCKS. Must keep size small otherwise
I hog processor/machine from other processes. Even other CPUS
are halted during a sendfile! (also any file operation) */
#ifdef HAVE_SENDFILE
#ifdef Linux /* Don't know if this will work all the time */
if ((d->binary) && (!peer->ratioinfo))
{
if (d->startpos != 0)
{
d->pos = d->startpos;
d->startpos = 0;
}
size2 = size = sendfile(d->socketfd, d->filefd, &(d->pos), maxsize);
} else
#endif
#ifdef FreeBSD
if ((d->binary) && (!peer->ratioinfo))
{
int result;
if (d->startpos != 0)
{
d->pos = d->startpos;
d->startpos = 0;
}
result = sendfile(d->filefd, d->socketfd, d->pos, maxsize,
NULL, (off_t *)&size, 0);
d->pos += size;
if ((result == -1) && (errno != EAGAIN))
size = -1;
size2 = size;
} else
#endif
#endif
if (STRLENGTH(d->buffer) == 0)
{
size = read(d->filefd, indata, maxsize);
size2 = 0;
if ((size > 0) && (!d->binary))
filter_toascii(indata, &size);
d->pos += size;
if (d->pos < d->startpos)
return(FALSE);
else
startp = MAXIMUM(0, size - (d->pos - d->startpos));
if (((size - startp) > 0) && (peer->ratioinfo))
if (ratio_downloadbytes(peer->ratioinfo, (size - startp)))
{
finished = TRUE;
size = 0;
ftp_write(peer, TRUE, 0, REPLY_BYTELIMIT);
}
if ((size > 0) && (startp != size))
size2 = write(d->socketfd, indata + startp, size - startp);
if ((size2 > 0) && ((size2 + startp) < size))
string_cat(&(d->buffer), indata + startp + size2, size - (startp + size2));
}
else
{
size = size2 = write(d->socketfd, STRTOCHAR(d->buffer), STRLENGTH(d->buffer));
if (size2 > 0)
string_dropfront(&(d->buffer), size);
}
if ((size <= 0) || (size2 <= 0) || finished)
{
if (d->download_limiter)
limiter_add(d->download_limiter, 0, TRUE);
select_delfd(sel, d->filefd);
if ((size == 0) && (!finished))
ftp_write(peer, FALSE, 226, REPLY_TRANSDONE(peer->dport->transbytes));
else
ftp_write(peer, FALSE, 426, REPLY_TRANSABORT(peer->dport->transbytes));
closedatasocket(peer);
return(2);
}
if (d->download_limiter)
limiter_add(d->download_limiter, size, FALSE);
d->transbytes += size;
peer->downloadedfilebytes += size;
return(FALSE);
}
int upload_read(SELECTER *sel, int fd, void *peerv)
{
FTPSTATE *peer = (FTPSTATE *)peerv;
DATAPORT *d = peer->dport;
int size, size2;
int finished = FALSE;
char indata[BUFFERSIZE * 2];
int maxsize;
/* determine maximum read size */
if (peer->maxtranspd_up)
maxsize = MINIMUM(peer->maxtranspd_up, BUFFERSIZE);
else if (peer->maxtranspd)
maxsize = MINIMUM(peer->maxtranspd, BUFFERSIZE);
else
maxsize = BUFFERSIZE;
size = read(d->socketfd, indata, maxsize);
/* false alarm, no data, return */
if ((size == -1) && (errno == EAGAIN))
return FALSE;
if (size > 0)
{
d->transbytes += size;
peer->uploadedfilebytes += size;
if (peer->maxtranspd || peer->maxtranspd_up)
limiter_add(d->upload_limiter, size, FALSE);
if (peer->ratioinfo)
ratio_uploadbytes(peer->ratioinfo, size);
}
if ((size > 0) && (!d->binary))
filter_fromascii(indata, &size);
if (size > 0)
{
int re = 0;
size2 = 0;
while(((size - size2) > 0) && (re != -1))
{
re = write(d->filefd, indata + size2, size - size2);
if (re == -1)
finished = TRUE;
else
size2 += re;
}
}
if ((size <= 0) || finished)
{
select_delfd(sel, d->filefd);
if (peer->maxtranspd || peer->maxtranspd_up)
limiter_add(d->upload_limiter, size, FALSE);
if ((size == 0) && (!finished))
ftp_write(peer, FALSE, 226, REPLY_TRANSDONE(peer->dport->transbytes));
else
ftp_write(peer, FALSE, 426, REPLY_TRANSABORT(peer->dport->transbytes));
closedatasocket(peer);
return(2);
}
return(FALSE);
}
int ftp_retr(FTPSTATE *peer, char *filename)
{
int filefd;
off_t size;
char *fullname;
if ((filefd = file_readopen(peer, filename, &fullname)) < 0)
reporterror(peer, filename, errno);
else
{
if (peer->ratioinfo)
if (ratio_downloadfile(peer->ratioinfo))
{
ftp_write(peer, FALSE, 550, REPLY_FILELIMIT);
return(FALSE);
}
peer->downloadedfiles++;
/* if ascii transfer size is set to -1, else size is binary filesize */
size = -1;
if (peer->binary)
{
size = lseek(filefd, 0, SEEK_END);
lseek(filefd, MINIMUM(size, peer->restartpos), SEEK_SET);
}
if (startdatasocket(peer, filefd, TRANS_DOWNLOAD, size) == 0)
log_giveentry(MYLOG_FTRANS, peer, safe_snprintf("retrieve %s", fullname));
}
freewrapper(fullname);
return(FALSE);
}
int ftp_stor_core(FTPSTATE *peer, char *filename, int unique)
{
int filefd;
int nounique = FALSE;
char *fullname;
/* There is 2 good reasons thus is here:
1) My code won't work for resume ASCII upload.
2) It is impossible to do. Telnet ascii to unix ascii is
irreversible. Therefore you don't know where the user
wants to start saving.
*/
if ((peer->restartpos != 0) && (!peer->binary))
return(ftp_write(peer, FALSE, 500, REPLY_RESUMEASCIIUP));
/* if the user asked for a unique filename, find one */
if (unique)
filefd = file_ustoreopen(peer, filename, &nounique, &fullname);
else
filefd = file_storeopen(peer, filename, &fullname);
if (nounique)
ftp_write(peer, FALSE, 500, REPLY_STOUNOUNIQUE);
else if (filefd < 0)
reporterror(peer, filename, errno);
else
{
peer->uploadedfiles++;
if (startdatasocket(peer, filefd, TRANS_UPLOAD, -1) == 0)
{
log_giveentry(MYLOG_FTRANS, peer, safe_snprintf("store %s", fullname));
/* now truncate the file to the restart pos length */
ftruncate(filefd, peer->dport->pos);
lseek(filefd, peer->dport->pos, SEEK_SET);
}
}
freewrapper(fullname);
return(FALSE);
}
int ftp_stor(FTPSTATE *peer, char *filename)
{
return(ftp_stor_core(peer, filename, FALSE));
}
int ftp_stou(FTPSTATE *peer, char *filename)
{
return(ftp_stor_core(peer, filename, TRUE));
}
int ftp_appe(FTPSTATE *peer, char *filename)
{
int filefd;
char *fullname;
peer->restartpos = 0;
filefd = file_storeopen(peer, filename, &fullname);
if (filefd < 0)
reporterror(peer, filename, errno);
else
{
peer->uploadedfiles++;
if (startdatasocket(peer, filefd, TRANS_UPLOAD, -1) == 0)
{
log_giveentry(MYLOG_FTRANS, peer, safe_snprintf("append %s", fullname));
/* this is for ratios, If the final file position is 0, then it is
a fresh upload. */
if (lseek(filefd, 0, SEEK_END) == 0) /* set to append */
peer->dport->trans_type = TRANS_SUPLOAD;
}
}
freewrapper(fullname);
return(FALSE);
}
syntax highlighted by Code2HTML, v. 0.9.1