/*
** Copyright 2000-2004 University of Illinois Board of Trustees
** Copyright 2000-2004 Mark D. Roth
** All rights reserved.
**
** list_parse_mlsd.c - MSLD-style FTP directory parsing code
**
** Mark D. Roth <roth@feep.net>
*/
#include <internal.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifdef STDC_HEADERS
# include <string.h>
# include <stdlib.h>
#endif
#ifdef MAJOR_IN_MKDEV
# include <sys/mkdev.h>
#else
# ifdef MAJOR_IN_SYSMACROS
# include <sys/sysmacros.h>
# endif
#endif
/* needed for strcasecmp() on some platforms */
#include <strings.h>
/*
** keep this list in sync with facts supported by _ftp_list_parse_mlsd()
*/
int
_ftp_opts_mlsd(FTP *ftp)
{
int i;
i = _ftp_opts(ftp, "MLST",
"size;modify;type;perm;"
"UNIX.mode;UNIX.owner;UNIX.group;UNIX.uid;UNIX.gid;");
/*
** if the server is broken enough to support FEAT but
** not OPTS, then we'll punt by ignoring the error
** and doing the best we can with whatever facts it provides
*/
if (i == -1
&& errno == ENOSYS)
i = 0;
return i;
}
/* parse MLSD directories */
int
_ftp_list_parse_mlsd(FTP *ftp, char *buf, file_info_t *fip)
{
char *factp, *valp, *nextp = buf;
unsigned long ul;
unsigned int ui;
struct tm tt;
#ifdef DEBUG
printf("==> _ftp_list_parse_mlsd(ftp=0x%lx (%s), buf=0x%lx, "
"fip=0x%lx)\n", ftp, ftp->ftp_host, buf, fip);
#endif
factp = strchr(buf, ' ');
if (factp == NULL)
{
#ifdef DEBUG
printf("<== _ftp_list_parse_mlsd(): no space character "
"in entry - returning FLP_IGNORE\n");
#endif
return FLP_IGNORE;
}
*factp++ = '\0';
strlcpy(fip->fi_filename, factp, sizeof(fip->fi_filename));
while ((factp = strsep(&nextp, ";")) != NULL)
{
if (*factp == '\0')
continue;
valp = factp;
factp = strsep(&valp, "=");
if (strcasecmp(factp, "size") == 0)
{
sscanf(valp, "%lu", &ul);
fip->fi_stat.fs_size = (off_t)ul;
continue;
}
if (strcasecmp(factp, "modify") == 0)
{
memset(&tt, 0, sizeof(tt));
sscanf(valp, "%4d%2d%2d%2d%2d%2d",
&tt.tm_year, &tt.tm_mon, &tt.tm_mday,
&tt.tm_hour, &tt.tm_min, &tt.tm_sec);
tt.tm_year -= 1900;
tt.tm_mon--;
fip->fi_stat.fs_mtime = mktime(&tt);
continue;
}
if (strcasecmp(factp, "type") == 0)
{
/*
** clear S_IFMT bits, in case the type was set before
*/
fip->fi_stat.fs_mode &= ~S_IFMT;
if (strcasecmp(valp, "cdir") == 0)
{
strlcpy(fip->fi_filename, ".",
sizeof(fip->fi_filename));
fip->fi_stat.fs_mode |= S_IFDIR;
continue;
}
if (strcasecmp(valp, "pdir") == 0)
{
strlcpy(fip->fi_filename, "..",
sizeof(fip->fi_filename));
fip->fi_stat.fs_mode |= S_IFDIR;
continue;
}
if (strcasecmp(valp, "dir") == 0)
{
fip->fi_stat.fs_mode |= S_IFDIR;
continue;
}
if (strcasecmp(valp, "file") == 0)
{
fip->fi_stat.fs_mode |= S_IFREG;
continue;
}
if (strncasecmp(valp, "os.unix=", 8) == 0)
{
valp += 8;
if (strncasecmp(valp, "slink:", 6) == 0)
{
fip->fi_stat.fs_mode |= S_IFLNK;
valp += 6;
strlcpy(fip->fi_linkto, valp,
sizeof(fip->fi_linkto));
continue;
}
if (strncasecmp(valp, "chr-", 4) == 0)
{
fip->fi_stat.fs_mode |= S_IFCHR;
/* FIXME: add fs_rdev field? */
continue;
}
if (strncasecmp(valp, "blk-", 4) == 0)
{
fip->fi_stat.fs_mode |= S_IFCHR;
/* FIXME: add fs_rdev field? */
continue;
}
continue;
}
/* for unknown types, ignore the entire entry */
#ifdef DEBUG
printf("<== _ftp_list_parse_mlsd(): "
"unknown type \"%s\" - returning FLP_IGNORE\n",
valp);
#endif
return FLP_IGNORE;
}
if (strcasecmp(factp, "perm") == 0)
{
/*
** if the mode bits are already set, don't
** overwrite them
** (this allows the UNIX.mode fact to take precedence)
*/
if (fip->fi_stat.fs_mode & ~S_IFMT != 0)
continue;
for (; *valp != '\0'; valp++)
{
switch (*valp)
{
case 'a': /* file: APPE */
case 'w': /* file: STOR */
case 'c': /* [cp]?dir: STOU, STOR */
fip->fi_stat.fs_mode |= 0222;
break;
case 'e': /* [cp]?dir: CWD */
fip->fi_stat.fs_mode |= 0111;
break;
case 'l': /* [cp]?dir: MLSD, LIST, NLST */
case 'r': /* file: RETR */
fip->fi_stat.fs_mode |= 0444;
break;
default:
case 'd': /* any: DELE, RMD */
case 'f': /* any: RNFR */
case 'm': /* [cp]?dir: MKD (subdirs) */
case 'p': /* [cp]?dir: RMD (subdirs) */
break;
}
}
continue;
}
if (strncasecmp(factp, "UNIX.", 5) == 0)
{
factp += 5;
if (strcasecmp(factp, "mode") == 0)
{
/* clear any bits that may already be set */
fip->fi_stat.fs_mode &= S_IFMT;
sscanf(valp, "%lo", &ui);
fip->fi_stat.fs_mode |= (mode_t)ui;
continue;
}
if (strcasecmp(factp, "owner") == 0)
{
strlcpy(fip->fi_stat.fs_username, valp,
sizeof(fip->fi_stat.fs_username));
continue;
}
if (strcasecmp(factp, "group") == 0)
{
strlcpy(fip->fi_stat.fs_groupname, valp,
sizeof(fip->fi_stat.fs_groupname));
continue;
}
if (strcasecmp(factp, "uid") == 0)
{
/*
** skip if already set
** (this allows the UNIX.owner fact
** to take precedence)
*/
if (fip->fi_stat.fs_username[0] != '\0')
continue;
strlcpy(fip->fi_stat.fs_username, valp,
sizeof(fip->fi_stat.fs_username));
continue;
}
if (strcasecmp(factp, "gid") == 0)
{
/*
** skip if already set
** (this allows the UNIX.group fact
** to take precedence)
*/
if (fip->fi_stat.fs_groupname[0] != '\0')
continue;
strlcpy(fip->fi_stat.fs_groupname, valp,
sizeof(fip->fi_stat.fs_groupname));
continue;
}
continue;
}
/* skip any other facts */
continue;
}
if (fip->fi_stat.fs_username[0] == '\0')
strlcpy(fip->fi_stat.fs_username, "-1",
sizeof(fip->fi_stat.fs_username));
if (fip->fi_stat.fs_groupname[0] == '\0')
strlcpy(fip->fi_stat.fs_groupname, "-1",
sizeof(fip->fi_stat.fs_groupname));
if (fip->fi_stat.fs_nlink == 0)
fip->fi_stat.fs_nlink = 1;
#ifdef DEBUG
printf("<== _ftp_list_parse_mlsd(): success\n");
#endif
return FLP_VALID;
}
syntax highlighted by Code2HTML, v. 0.9.1