/* 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"
#define LISTALL 1
#define LISTDIR 2
#define LISTRECURSE 4
int ftplist_parseflags(char *params)
{
int count, output = 0;
for (count = 0; count < strlen(params); count++)
{
if (params[count] == 'a')
output = output | LISTALL;
if (params[count] == 'd')
output = output | LISTDIR;
if (params[count] == 'R')
output = output | LISTRECURSE;
if (params[count] == ' ')
return(output);
}
return(output);
}
char *listmakeline(FTPSTATE *peer, char *longname, char *filename, struct stat *fileinfo, STRCACHE *uidcache, STRCACHE *gidcache, int year)
{
char *output = NULL;
char *datastr;
char permissions[11];
char date[20];
char size[40];
char *username;
char *group;
int links;
/* make sure the stat did work */
if (fileinfo->st_nlink > 0)
{
char numuserstr[20];
char numgroupstr[20];
/* insert the permissions/ filetype */
if (peer->fakemode != -1)
{
int dirmode = (peer->fakemode & 0777) | S_IFDIR;
dirmode |= (dirmode & 0444) >> 2;
if (S_ISDIR(fileinfo->st_mode))
convertperms(permissions, dirmode);
else
convertperms(permissions, peer->fakemode | S_IFREG);
}
else
convertperms(permissions, fileinfo->st_mode);
permissions[10] = 0;
/* Insert links */
links = MINIMUM(fileinfo->st_nlink, 999);
/* Insert size */
if (S_ISCHR(fileinfo->st_mode) || S_ISBLK(fileinfo->st_mode))
{
snprintf(size, 39, "%3d, %3d", (int)(fileinfo->st_rdev) >> 8,
(int)(fileinfo->st_rdev) & 255);
}
else
snprintf(size, 39, "%8s", offt_tostr(fileinfo->st_size));
/* Insert date */
datastr = ctime(&(fileinfo->st_mtime));
memcpy(date, datastr + 4, 12);
if (memcmp(&year, datastr + 20, 4) != 0)
{
date[7] = ' ';
memcpy(date + 8, datastr + 20, 4);
}
date[12] = 0;
/* do owner */
if (peer->fakename)
username = peer->fakename;
else if ((username = strcache_check(uidcache, (int)fileinfo->st_uid)) == NULL)
{
username = get_passwdname(fileinfo->st_uid, peer->chroot);
if (username == NULL)
{
sprintf(numuserstr, "%d", (int)fileinfo->st_uid);
username = numuserstr;
}
strcache_add(uidcache, (int)fileinfo->st_uid, username);
}
username[8] = 0;
/* do group */
if (peer->fakegroup)
group = peer->fakegroup;
else if ((group = strcache_check(gidcache, fileinfo->st_gid)) == NULL)
{
group = get_groupname(fileinfo->st_gid, peer->chroot);
if (group == NULL)
{
sprintf(numgroupstr, "%d", (int)fileinfo->st_gid);
group = numgroupstr;
}
strcache_add(gidcache, (int)fileinfo->st_gid, group);
}
group[8] = 0;
/* Do symbolic links */
if (permissions[0] == 'l')
{
char linkname[BUFFERSIZE];
int linklen = readlink(filename, linkname, BUFFERSIZE - 1);
if (linklen != -1)
{
linkname[linklen] = 0;
output = safe_snprintf("%s %3d %-8s %-8s %s %12s %s -> %s\r\n",
permissions, links, username, group, size, date, filename, linkname);
}
}
if (!output)
output = safe_snprintf("%s %3d %-8s %-8s %s %12s %s\r\n",
permissions, links, username, group, size, date, filename);
}
else
output = safe_snprintf("---------- 1 unknown unknown 0 Jan 1 00:00 %s\r\n", filename);
return(output);
}
void list_decodesubdirs(LISTHANDLE *lh)
{
int count;
/* no need to do this if no files in this dir */
if (lh->dirdata->namecount == 0)
return;
reallocwrapper(sizeof(char *) * (lh->dirdata->namecount + lh->numsubdirs), (void *)&(lh->subdirs));
for (count = lh->dirdata->namecount - 1; count >= 0; count--)
{
if (S_ISDIR(lh->dirdata->stat_buf[count].st_mode))
{
if ((strcmp(lh->dirdata->name[count], "..") != 0)
&& (strcmp(lh->dirdata->name[count], ".") != 0))
{
lh->subdirs[lh->numsubdirs] = safe_snprintf("%s/%s", lh->dir, lh->dirdata->name[count]);
lh->numsubdirs++;
}
}
}
}
int buildnextdir(FTPSTATE *peer, LISTHANDLE *lh)
{
MYGLOBDATA *dirlisting = NULL;
myglobfree(lh->dirdata);
lh->dirdata = NULL;
/* loop while there are still subdirectories to look at until an accessable one
is found */
while ((!dirlisting) && (lh->numsubdirs > 0))
{
freewrapper(lh->dir);
lh->numsubdirs--;
lh->dir = lh->subdirs[lh->numsubdirs];
dirlisting = file_glob(peer, lh->dir, "*", lh->all);
lh->subdirs[lh->numsubdirs] = NULL;
}
if (dirlisting)
{
lh->dirdata = dirlisting;
lh->pos = -1;
list_decodesubdirs(lh);
return(TRUE);
}
return(FALSE);
}
LISTHANDLE *getlisthandle(FTPSTATE *peer, char *dirpattern, int nlist, char *reldir, int params)
{
char *mypat = strdupwrapper(dirpattern);
char *dir, *pattern;
char *t;
time_t ct;
LISTHANDLE *lh;
/* manufacture original listing dir and pattern */
dir = mypat;
*(strrchr(mypat, '/')) = 0;
pattern = strrchr(dirpattern, '/') + 1;
lh = mallocwrapper(sizeof(LISTHANDLE));
lh->dirdata = file_glob(peer, dir, pattern, params & LISTALL);
if (!lh->dirdata)
{
freewrapper(lh);
freewrapper(mypat);
return(NULL);
}
ct = time(NULL);
t = ctime(&ct);
memcpy(&(lh->year), t + 20, 4);
lh->uidcache = strcache_new();
lh->gidcache = strcache_new();
lh->pos = 0;
lh->nlist = nlist;
lh->rdir = reldir;
lh->odirlen = strlen(dir);
lh->dir = dir;
lh->recursive = params & LISTRECURSE;
lh->all = params & LISTALL;
lh->numsubdirs = 0;
lh->subdirs = NULL;
if (lh->recursive)
{
list_decodesubdirs(lh);
lh->pos = -1;
}
return(lh);
}
char *getlisthandleline(FTPSTATE *peer, LISTHANDLE *lh)
{
char *longname;
char *ret;
char *relativename;
if (lh == NULL)
return(NULL);
if (lh->pos == -1)
{
relativename = lh->dir + lh->odirlen;
if (relativename[0] == 0)
{
if (lh->rdir[0] == 0)
ret = strdupwrapper(".:\r\n");
else
ret = safe_snprintf("%s:\r\n", lh->rdir);
}
else
{
if (lh->rdir[0] == 0)
ret = safe_snprintf("%s:\r\n", relativename + 1);
else if ((lh->rdir[0] == '/') && (lh->rdir[1] == 0))
ret = safe_snprintf("%s:\r\n", relativename);
else
ret = safe_snprintf("%s%s:\r\n", lh->rdir, relativename);
}
lh->pos++;
return(ret);
}
if (lh->pos == 0)
{
lh->pos++;
if (!(lh->nlist))
return(safe_snprintf("total %d\r\n", lh->dirdata->blocks));
}
if (lh->pos > lh->dirdata->namecount)
{
if (lh->recursive)
if (buildnextdir(peer, lh))
return(strdupwrapper("\r\n"));
return(NULL);
}
if (lh->nlist)
{
ret = safe_snprintf("%s\r\n", lh->dirdata->name[lh->pos-1]);
lh->pos++;
return(ret);
}
longname = safe_snprintf("%s/%s", lh->dir, lh->dirdata->name[lh->pos-1]);
ret = listmakeline(peer, longname, lh->dirdata->name[lh->pos-1],
&(lh->dirdata->stat_buf[lh->pos-1]), lh->uidcache, lh->gidcache, lh->year);
lh->pos++;
freewrapper(longname);
return(ret);
}
void freelisthandle(LISTHANDLE *lh)
{
if (!lh)
return;
while(lh->numsubdirs > 0)
{
lh->numsubdirs--;
freewrapper(lh->subdirs[lh->numsubdirs]);
}
freeifnotnull(lh->subdirs);
if (lh->dirdata)
myglobfree(lh->dirdata);
freewrapper(lh->dir);
freewrapper(lh->rdir);
strcache_free(lh->uidcache);
strcache_free(lh->gidcache);
freewrapper(lh);
return;
}
int list_write(SELECTER *sel, int fd, void *peerv)
{
FTPSTATE *peer = (FTPSTATE *)peerv;
DATAPORT *d = peer->dport;
int finish = FALSE;
int size = 0;
int maxsize;
if (STRLENGTH(d->buffer) == 0)
{
int ret = FALSE;
while (!ret)
{
char *line = getlisthandleline(peer, d->lhandle);
if (line)
{
string_cat(&(d->buffer), line, -1);
freewrapper(line);
if (STRLENGTH(d->buffer) > BUFFERSIZE)
ret = TRUE;
}
else
{
ret = TRUE;
finish = TRUE;
}
}
}
if (STRLENGTH(d->buffer) > 0)
{
if (peer->maxtranspd_down)
maxsize = MINIMUM(peer->maxtranspd_down, STRLENGTH(d->buffer));
else if (peer->maxtranspd)
maxsize = MINIMUM(peer->maxtranspd, STRLENGTH(d->buffer));
else
maxsize = STRLENGTH(d->buffer);
size = write(d->socketfd, STRTOCHAR(d->buffer), maxsize);
}
if (size > 0)
{
peer->listdownloadedbytes += size;
d->transbytes += size;
string_dropfront(&(d->buffer), size);
}
if ((size <= 0) || (finish && (STRLENGTH(d->buffer) == 0)))
{
if ((peer->maxtranspd) || (peer->maxtranspd_down))
limiter_add(d->download_limiter, 0, TRUE);
socket_flush_wait(d->socketfd, peer->timeout);
close(d->socketfd);
if (finish)
ftp_write(peer, FALSE, 226, REPLY_TRANSDONE(d->transbytes));
else
ftp_write(peer, FALSE, 426, REPLY_TRANSABORT(d->transbytes));
closedatasocket(peer);
return(2);
}
if ((peer->maxtranspd) || (peer->maxtranspd_down))
limiter_add(d->download_limiter, size, FALSE);
return(FALSE);
}
char *listmakepattern(FTPSTATE *peer, char *parm, char **rdir, int flags)
{
char *dir;
char *parm2;
int l;
if (parm == NULL)
parm2 = strdupwrapper("*");
else
parm2 = strdupwrapper(parm);
dir = file_expand(peer, parm2);
freewrapper(parm2);
if (parm)
*rdir = strdupwrapper(parm);
else
*rdir = strdupwrapper("");
/* This will check for directory names and changes them to the files
in the directory instead unless user specifiess -d */
if (checkdir(dir) && !(flags & LISTDIR))
{
int a = strlen(dir);
reallocwrapper(a + 6, (void *)&dir);
dir[a] = '/'; dir[a+1] = '*'; dir[a+2] = 0;
}
else
{
char *pos = strrchr(*rdir, '/');
if (pos)
*(strrchr(*rdir, '/')) = 0;
else
**rdir = 0;
}
l = strlen(*rdir);
if (l > 1)
while((*rdir)[l - 1] == '/')
{
(*rdir)[l - 1] = 0;
l--;
}
return(dir);
}
int ftp_lister(FTPSTATE *peer, char *parm, int nlist, int params)
{
char *fpattern;
char *reldir;
/* if -d used, recursion gets turned off */
if ((params & LISTDIR) && (params & LISTRECURSE))
params = params & ~LISTRECURSE;
fpattern = listmakepattern(peer, parm, &reldir, params);
if (startdatasocket(peer, -1, TRANS_LIST, -1) == 0)
{
peer->dport->lhandle = getlisthandle(peer, fpattern, nlist, reldir, params);
peer->listconns++;
}
freewrapper(fpattern);
return(FALSE);
}
syntax highlighted by Code2HTML, v. 0.9.1