1
0
mirror of https://github.com/moparisthebest/wget synced 2024-07-03 16:38:41 -04:00

[svn] Committed Jan's ftpparse patch with Hrvoje's modifications.

<sxshf518e72.fsf@florida.arsdigita.de>.
This commit is contained in:
hniksic 2000-11-21 08:48:39 -08:00
parent d6a57bc2a6
commit c3d636db03
8 changed files with 838 additions and 25 deletions

View File

@ -1,3 +1,31 @@
2000-11-21 Hrvoje Niksic <hniksic@arsdigita.com>
* ftp.c (getftp): Reformat Jan's code according to GNU coding
standards; remove (debugging?) printf's; use '\0' for the ASCII
zero character. Use alloca() instead of malloc() for
inter-function temporary allocations.
2000-11-18 Jan Prikryl <prikryl@cg.tuwien.ac.at>
* ftpparse.c, ftpparse.h: New files.
* ftp-ls.c (ftp_parse_ls): Use ftp_parse_unix_ls for UNIX servers
only. Use ftp_parse_nonunix_ls otherwise.
(ftp_parse_nonunix_ls): Stub to the ftpparse library handling all
exotic FTP servers.
* ftp.h (stype): New enum, distinguishes UNIX, VMS, and "other"
FTP servers.
* ftp.c: New static wariables host_type, pwd, and pwd_len.
(getftp): Support for VMS. Support for FTP servers that do not
place you in the root directory after login.
(ftp_retrieve_list): VMS is silent about the real file size, issue
a more appropriate message.
(ftp_get_listing): Pass host_type to ftp_parse_ls.
* ftp-basic.c (ftp_pwd, ftp_syst): New functions.
2000-11-21 Hrvoje Niksic <hniksic@arsdigita.com> 2000-11-21 Hrvoje Niksic <hniksic@arsdigita.com>
* hash.c (hash_table_put): Don't overwrite deleted mappings. * hash.c (hash_table_put): Don't overwrite deleted mappings.

View File

@ -57,10 +57,10 @@ MD5_OBJ = @MD5_OBJ@
OPIE_OBJ = @OPIE_OBJ@ OPIE_OBJ = @OPIE_OBJ@
OBJ = $(ALLOCA) cmpt$o connect$o fnmatch$o ftp$o ftp-basic$o \ OBJ = $(ALLOCA) cmpt$o connect$o fnmatch$o ftp$o ftp-basic$o \
ftp-ls$o $(OPIE_OBJ) getopt$o hash$o headers$o host$o \ ftp-ls$o $(OPIE_OBJ) ftpparse$o getopt$o hash$o \
html-parse$o html-url$o http$o init$o log$o main$o \ headers$o host$o html-parse$o html-url$o http$o init$o \
$(MD5_OBJ) netrc$o rbuf$o recur$o retr$o snprintf$o \ log$o main$o $(MD5_OBJ) netrc$o rbuf$o recur$o retr$o \
url$o utils$o version$o snprintf$o url$o utils$o version$o
.SUFFIXES: .SUFFIXES:
.SUFFIXES: .c .o ._c ._o .SUFFIXES: .c .o ._c ._o

View File

@ -41,6 +41,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "rbuf.h" #include "rbuf.h"
#include "connect.h" #include "connect.h"
#include "host.h" #include "host.h"
#include "ftp.h"
#ifndef errno #ifndef errno
extern int errno; extern int errno;
@ -537,3 +538,98 @@ ftp_list (struct rbuf *rbuf, const char *file)
/* All OK. */ /* All OK. */
return FTPOK; return FTPOK;
} }
/* Sends the SYST command to the server. */
uerr_t
ftp_syst (struct rbuf *rbuf, enum stype *host_type)
{
char *request, *respline;
int nwritten;
uerr_t err;
/* Send SYST request. */
request = ftp_request ("SYST", NULL);
nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
if (nwritten < 0)
{
free (request);
return WRITEFAILED;
}
free (request);
/* Get appropriate response. */
err = ftp_response (rbuf, &respline);
if (err != FTPOK)
{
free (respline);
return err;
}
if (*respline == '5')
{
free (respline);
return FTPSRVERR;
}
/* Skip the number (215, but 200 (!!!) in case of VMS) */
strtok (respline, " ");
/* Which system type has been reported (we are interested just in the
first word of the server response)? */
request = strtok (NULL, " ");
if (!strcasecmp (request, "VMS"))
*host_type = ST_VMS;
else
if (!strcasecmp (request, "UNIX"))
*host_type = ST_UNIX;
else
*host_type = ST_OTHER;
free (respline);
/* All OK. */
return FTPOK;
}
/* Sends the PWD command to the server. */
uerr_t
ftp_pwd (struct rbuf *rbuf, char **pwd)
{
char *request, *respline;
int nwritten;
uerr_t err;
/* Send PWD request. */
request = ftp_request ("PWD", NULL);
nwritten = iwrite (RBUF_FD (rbuf), request, strlen (request));
if (nwritten < 0)
{
free (request);
return WRITEFAILED;
}
free (request);
/* Get appropriate response. */
err = ftp_response (rbuf, &respline);
if (err != FTPOK)
{
free (respline);
return err;
}
if (*respline == '5')
{
free (respline);
return FTPSRVERR;
}
/* Skip the number (257), leading citation mark, trailing citation mark
and everything following it. */
strtok (respline, "\"");
request = strtok (NULL, "\"");
/* Has the `pwd' been already allocated? Free! */
if (*pwd) free (*pwd);
*pwd = xstrdup (request);
free (respline);
/* All OK. */
return FTPOK;
}

View File

@ -1,5 +1,5 @@
/* Parsing FTP `ls' output. /* Parsing FTP `ls' output.
Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc. Copyright (C) 1995, 1996, 1997, 2000 Free Software Foundation, Inc.
This file is part of Wget. This file is part of Wget.
@ -38,6 +38,15 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "ftp.h" #include "ftp.h"
#include "url.h" #include "url.h"
/* Undef this if FTPPARSE is not available. In that case, Wget will
still work with Unix FTP servers, which covers most cases. */
#define HAVE_FTPPARSE
#ifdef HAVE_FTPPARSE
#include "ftpparse.h"
#endif
/* Converts symbolic permissions to number-style ones, e.g. string /* Converts symbolic permissions to number-style ones, e.g. string
rwxr-xr-x to 755. For now, it knows nothing of rwxr-xr-x to 755. For now, it knows nothing of
setuid/setgid/sticky. ACLs are ignored. */ setuid/setgid/sticky. ACLs are ignored. */
@ -376,18 +385,111 @@ ftp_parse_unix_ls (const char *file)
return dir; return dir;
} }
/* This function is just a stub. It should actually accept some kind #ifdef HAVE_FTPPARSE
of information what system it is running on -- e.g. FPL_UNIX,
FPL_DOS, FPL_NT, FPL_VMS, etc. and a "guess-me" value, like
FPL_GUESS. Then it would call the appropriate parsers to fill up
fileinfos.
Since we currently support only the Unix FTP servers, this function /* This is a "glue function" that connects the ftpparse interface to
simply returns the result of ftp_parse_unix_ls(). */ the interface Wget expects. ftpparse is used to parse listings
struct fileinfo * from servers other than Unix, like those running VMS or NT. */
ftp_parse_ls (const char *file)
static struct fileinfo *
ftp_parse_nonunix_ls (const char *file)
{ {
return ftp_parse_unix_ls (file); FILE *fp;
int len;
char *line; /* tokenizer */
struct fileinfo *dir, *l, cur; /* list creation */
fp = fopen (file, "rb");
if (!fp)
{
logprintf (LOG_NOTQUIET, "%s: %s\n", file, strerror (errno));
return NULL;
}
dir = l = NULL;
/* Line loop to end of file: */
while ((line = read_whole_line (fp)))
{
struct ftpparse fp;
DEBUGP (("%s\n", line));
len = strlen (line);
/* Destroy <CR><LF> if present. */
if (len && line[len - 1] == '\n')
line[--len] = '\0';
if (len && line[len - 1] == '\r')
line[--len] = '\0';
if (ftpparse(&fp, line, len))
{
cur.size = fp.size;
cur.name = (char *)xmalloc (fp.namelen + 1);
memcpy (cur.name, fp.name, fp.namelen);
cur.name[fp.namelen] = '\0';
DEBUGP (("%s\n", cur.name));
/* No links on non-UNIX systems */
cur.linkto = NULL;
/* ftpparse won't tell us correct permisions. So lets just invent
something. */
if (fp.flagtrycwd)
{
cur.type = FT_DIRECTORY;
cur.perms = 0755;
}
else
{
cur.type = FT_PLAINFILE;
cur.perms = 0644;
}
if (!dir)
{
l = dir = (struct fileinfo *)xmalloc (sizeof (struct fileinfo));
memcpy (l, &cur, sizeof (cur));
l->prev = l->next = NULL;
}
else
{
cur.prev = l;
l->next = (struct fileinfo *)xmalloc (sizeof (struct fileinfo));
l = l->next;
memcpy (l, &cur, sizeof (cur));
l->next = NULL;
}
l->tstamp = fp.mtime;
}
free (line);
}
fclose (fp);
return dir;
}
#endif
/* This function switches between the correct parsing routine
depending on the SYSTEM_TYPE. If system type is ST_UNIX, we use
our home-grown ftp_parse_unix_ls; otherwise, we use our interface
to ftpparse, also known as ftp_parse_nonunix_ls. The system type
should be based on the result of the "SYST" response of the FTP
server. */
struct fileinfo *
ftp_parse_ls (const char *file, const enum stype system_type)
{
if (system_type == ST_UNIX)
{
return ftp_parse_unix_ls (file);
}
else
{
#ifdef HAVE_FTPPARSE
return ftp_parse_nonunix_ls (file);
#else
/* #### Maybe log some warning here? */
return ftp_parse_unix_ls (file);
#endif
}
} }
/* Stuff for creating FTP index. */ /* Stuff for creating FTP index. */

131
src/ftp.c
View File

@ -57,6 +57,10 @@ extern int h_errno;
extern char ftp_last_respline[]; extern char ftp_last_respline[];
static enum stype host_type=ST_UNIX;
static char *pwd;
static int pwd_len;
/* Look for regexp "( *[0-9]+ *byte" (literal parenthesis) anywhere in /* Look for regexp "( *[0-9]+ *byte" (literal parenthesis) anywhere in
the string S, and return the number converted to long, if found, 0 the string S, and return the number converted to long, if found, 0
otherwise. */ otherwise. */
@ -240,7 +244,64 @@ Error in server response, closing control connection.\n"));
exit (1); exit (1);
break; break;
} }
/* Third: Set type to Image (binary). */ /* Third: Get the system type */
if (!opt.server_response)
logprintf (LOG_VERBOSE, "==> SYST ... ");
err = ftp_syst (&con->rbuf, &host_type);
/* FTPRERR */
switch (err)
{
case FTPRERR:
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET, _("\
Error in server response, closing control connection.\n"));
CLOSE (csock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
case FTPSRVERR:
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET,
_("Server error, can't determine system type.\n"));
break;
case FTPOK:
/* Everything is OK. */
break;
default:
abort ();
break;
}
if (!opt.server_response)
logputs (LOG_VERBOSE, _("done. "));
/* Fourth: Find the initial ftp directory */
if (!opt.server_response)
logprintf (LOG_VERBOSE, "==> PWD ... ");
err = ftp_pwd(&con->rbuf, &pwd);
pwd_len = strlen(pwd);
/* FTPRERR */
switch (err)
{
case FTPRERR || FTPSRVERR :
logputs (LOG_VERBOSE, "\n");
logputs (LOG_NOTQUIET, _("\
Error in server response, closing control connection.\n"));
CLOSE (csock);
rbuf_uninitialize (&con->rbuf);
return err;
break;
case FTPOK:
/* Everything is OK. */
break;
default:
abort ();
break;
}
if (!opt.server_response)
logputs (LOG_VERBOSE, _("done.\n"));
/* Fifth: Set the FTP type. */
if (!opt.server_response) if (!opt.server_response)
logprintf (LOG_VERBOSE, "==> TYPE %c ... ", TOUPPER (u->ftp_type)); logprintf (LOG_VERBOSE, "==> TYPE %c ... ", TOUPPER (u->ftp_type));
err = ftp_type (&con->rbuf, TOUPPER (u->ftp_type)); err = ftp_type (&con->rbuf, TOUPPER (u->ftp_type));
@ -288,10 +349,55 @@ Error in server response, closing control connection.\n"));
logputs (LOG_VERBOSE, _("==> CWD not needed.\n")); logputs (LOG_VERBOSE, _("==> CWD not needed.\n"));
else else
{ {
/* Change working directory. */ /* Change working directory. If the FTP host runs VMS and
if (!opt.server_response) the path specified is absolute, we will have to convert
logprintf (LOG_VERBOSE, "==> CWD %s ... ", u->dir); it to VMS style as VMS does not like leading slashes */
err = ftp_cwd (&con->rbuf, u->dir); if (*(u->dir) == '/')
{
char *result = (char *)alloca (strlen (u->dir) + pwd_len + 10);
*result = '\0';
switch (host_type)
{
case ST_VMS:
{
char *tmp_dir, *tmpp;
STRDUP_ALLOCA (tmp_dir, u->dir);
for (tmpp = tmp_dir; *tmpp; tmpp++)
if (*tmpp=='/')
*tmpp = '.';
strcpy (result, pwd);
/* pwd ends with ']', we have to get rid of it */
result[pwd_len - 1]= '\0';
strcat (result, tmp_dir);
strcat (result, "]");
}
break;
case ST_UNIX:
/* pwd_len == 1 means pwd = "/", but u->dir begins with '/'
already */
if (pwd_len > 1)
strcpy (result, pwd);
strcat (result, u->dir);
/* These look like debugging messages to me. */
#if 0
logprintf (LOG_VERBOSE, "\npwd=\"%s\"", pwd);
logprintf (LOG_VERBOSE, "\nu->dir=\"%s\"", u->dir);
#endif
break;
default:
abort ();
break;
}
if (!opt.server_response)
logprintf (LOG_VERBOSE, "==> CWD %s ... ", result);
err = ftp_cwd (&con->rbuf, result);
}
else
{
if (!opt.server_response)
logprintf (LOG_VERBOSE, "==> CWD %s ... ", u->dir);
err = ftp_cwd (&con->rbuf, u->dir);
}
/* FTPRERR, WRITEFAILED, FTPNSFOD */ /* FTPRERR, WRITEFAILED, FTPNSFOD */
switch (err) switch (err)
{ {
@ -1080,7 +1186,7 @@ ftp_get_listing (struct urlinfo *u, ccon *con)
err = ftp_loop_internal (u, NULL, con); err = ftp_loop_internal (u, NULL, con);
u->local = olocal; u->local = olocal;
if (err == RETROK) if (err == RETROK)
f = ftp_parse_ls (list_filename); f = ftp_parse_ls (list_filename, host_type);
else else
f = NULL; f = NULL;
if (opt.remove_listing) if (opt.remove_listing)
@ -1177,13 +1283,22 @@ ftp_retrieve_list (struct urlinfo *u, struct fileinfo *f, ccon *con)
if (local_size == f->size && tml >= f->tstamp) if (local_size == f->size && tml >= f->tstamp)
{ {
logprintf (LOG_VERBOSE, _("\ logprintf (LOG_VERBOSE, _("\
Server file no newer than local file `%s' -- not retrieving.\n\n"), u->local); Server file not newer than local file `%s' -- not retrieving.\n\n"), u->local);
dlthis = 0; dlthis = 0;
} }
else if (local_size != f->size) else if (local_size != f->size)
{ {
logprintf (LOG_VERBOSE, _("\ if (host_type == ST_VMS)
{
logprintf (LOG_VERBOSE, _("\
Cannot compare sizes, remote system is VMS.\n"));
dlthis = 0;
}
else
{
logprintf (LOG_VERBOSE, _("\
The sizes do not match (local %ld) -- retrieving.\n"), local_size); The sizes do not match (local %ld) -- retrieving.\n"), local_size);
}
} }
} }
} /* opt.timestamping && f->type == FT_PLAINFILE */ } /* opt.timestamping && f->type == FT_PLAINFILE */

View File

@ -1,5 +1,5 @@
/* Declarations for FTP support. /* Declarations for FTP support.
Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc. Copyright (C) 1995, 1996, 1997, 2000 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -21,6 +21,14 @@
/* Need it for struct rbuf. */ /* Need it for struct rbuf. */
#include "rbuf.h" #include "rbuf.h"
/* System types. */
enum stype
{
ST_UNIX,
ST_VMS,
ST_OTHER
};
uerr_t ftp_response PARAMS ((struct rbuf *, char **)); uerr_t ftp_response PARAMS ((struct rbuf *, char **));
uerr_t ftp_login PARAMS ((struct rbuf *, const char *, const char *)); uerr_t ftp_login PARAMS ((struct rbuf *, const char *, const char *));
uerr_t ftp_port PARAMS ((struct rbuf *)); uerr_t ftp_port PARAMS ((struct rbuf *));
@ -30,6 +38,8 @@ uerr_t ftp_cwd PARAMS ((struct rbuf *, const char *));
uerr_t ftp_retr PARAMS ((struct rbuf *, const char *)); uerr_t ftp_retr PARAMS ((struct rbuf *, const char *));
uerr_t ftp_rest PARAMS ((struct rbuf *, long)); uerr_t ftp_rest PARAMS ((struct rbuf *, long));
uerr_t ftp_list PARAMS ((struct rbuf *, const char *)); uerr_t ftp_list PARAMS ((struct rbuf *, const char *));
uerr_t ftp_syst PARAMS ((struct rbuf *, enum stype *));
uerr_t ftp_pwd PARAMS ((struct rbuf *, char **));
struct urlinfo; struct urlinfo;
@ -89,7 +99,7 @@ typedef struct
long dltime; /* time of the download */ long dltime; /* time of the download */
} ccon; } ccon;
struct fileinfo *ftp_parse_ls PARAMS ((const char *)); struct fileinfo *ftp_parse_ls PARAMS ((const char *, enum stype));
uerr_t ftp_loop PARAMS ((struct urlinfo *, int *)); uerr_t ftp_loop PARAMS ((struct urlinfo *, int *));
uerr_t ftp_index (const char *, struct urlinfo *, struct fileinfo *); uerr_t ftp_index (const char *, struct urlinfo *, struct fileinfo *);

411
src/ftpparse.c Normal file
View File

@ -0,0 +1,411 @@
/* This file is not part of Wget proper, but is included here with the
permission of the author. If you wish to use Wget without
ftpparse, undefine HAVE_FTPPARSE in ftp-ls.c. */
/* ftpparse.c, ftpparse.h: library for parsing FTP LIST responses
D. J. Bernstein, djb@pobox.com
19970712 (doc updated 19970810)
Commercial use is fine, if you let me know what programs you're using this in.
Currently covered:
EPLF.
UNIX ls, with or without gid.
Microsoft FTP Service.
Windows NT FTP Server.
VMS.
WFTPD (DOS).
NetPresenz (Mac).
NetWare.
Definitely not covered:
Long VMS filenames, with information split across two lines.
NCSA Telnet FTP server. Has LIST = NLST (and bad NLST for directories).
Written for maximum portability, but tested only under UNIX so far. */
#include <time.h>
#include "ftpparse.h"
static long totai(year,month,mday)
long year;
long month;
long mday;
{
/* adapted from datetime_untai() */
/* about 100x faster than typical mktime() */
long result;
if (month >= 2) month -= 2;
else { month += 10; --year; }
result = (mday - 1) * 10 + 5 + 306 * month;
result /= 10;
if (result == 365) { year -= 3; result = 1460; }
else result += 365 * (year % 4);
year /= 4;
result += 1461 * (year % 25);
year /= 25;
if (result == 36524) { year -= 3; result = 146096; }
else { result += 36524 * (year % 4); }
year /= 4;
result += 146097 * (year - 5);
result += 11017;
return result * 86400;
}
static int flagneedbase = 1;
static time_t base; /* time() value on this OS at the beginning of 1970 TAI */
static long now; /* current time */
static int flagneedcurrentyear = 1;
static long currentyear; /* approximation to current year */
static void initbase()
{
struct tm *t;
if (!flagneedbase) return;
base = 0;
t = gmtime(&base);
base = -(totai(t->tm_year + 1900,t->tm_mon,t->tm_mday) + t->tm_hour * 3600 + t->tm_min * 60 + t->tm_sec);
/* time_t is assumed to be measured in TAI seconds. */
/* base may be slightly off if time_t is measured in UTC seconds. */
/* Typical software naively claims to use UTC but actually uses TAI. */
flagneedbase = 0;
}
static void initnow()
{
long day;
long year;
initbase();
now = time((time_t *) 0) - base;
if (flagneedcurrentyear) {
/* adapted from datetime_tai() */
day = now / 86400;
if ((now % 86400) < 0) --day;
day -= 11017;
year = 5 + day / 146097;
day = day % 146097;
if (day < 0) { day += 146097; --year; }
year *= 4;
if (day == 146096) { year += 3; day = 36524; }
else { year += day / 36524; day %= 36524; }
year *= 25;
year += day / 1461;
day %= 1461;
year *= 4;
if (day == 1460) { year += 3; day = 365; }
else { year += day / 365; day %= 365; }
day *= 10;
if ((day + 5) / 306 >= 10) ++year;
currentyear = year;
flagneedcurrentyear = 0;
}
}
/* UNIX ls does not show the year for dates in the last six months. */
/* So we have to guess the year. */
/* Apparently NetWare uses ``twelve months'' instead of ``six months''; ugh. */
/* Some versions of ls also fail to show the year for future dates. */
static long guesstai(month,mday)
long month;
long mday;
{
long year;
long t;
initnow();
for (year = currentyear - 1;year < currentyear + 100;++year) {
t = totai(year,month,mday);
if (now - t < 350 * 86400)
return t;
}
}
static int check(buf,monthname)
char *buf;
char *monthname;
{
if ((buf[0] != monthname[0]) && (buf[0] != monthname[0] - 32)) return 0;
if ((buf[1] != monthname[1]) && (buf[1] != monthname[1] - 32)) return 0;
if ((buf[2] != monthname[2]) && (buf[2] != monthname[2] - 32)) return 0;
return 1;
}
static char *months[12] = {
"jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"
} ;
static int getmonth(buf,len)
char *buf;
int len;
{
int i;
if (len == 3)
for (i = 0;i < 12;++i)
if (check(buf,months[i])) return i;
return -1;
}
static long getlong(buf,len)
char *buf;
int len;
{
long u = 0;
while (len-- > 0)
u = u * 10 + (*buf++ - '0');
return u;
}
int ftpparse(fp,buf,len)
struct ftpparse *fp;
char *buf;
int len;
{
int i;
int j;
int state;
long size;
long year;
long month;
long mday;
long hour;
long minute;
fp->name = 0;
fp->namelen = 0;
fp->flagtrycwd = 0;
fp->flagtryretr = 0;
fp->sizetype = FTPPARSE_SIZE_UNKNOWN;
fp->size = 0;
fp->mtimetype = FTPPARSE_MTIME_UNKNOWN;
fp->mtime = 0;
fp->idtype = FTPPARSE_ID_UNKNOWN;
fp->id = 0;
fp->idlen = 0;
if (len < 2) /* an empty name in EPLF, with no info, could be 2 chars */
return 0;
switch(*buf) {
/* see http://pobox.com/~djb/proto/eplf.txt */
/* "+i8388621.29609,m824255902,/,\tdev" */
/* "+i8388621.44468,m839956783,r,s10376,\tRFCEPLF" */
case '+':
i = 1;
for (j = 1;j < len;++j) {
if (buf[j] == 9) {
fp->name = buf + j + 1;
fp->namelen = len - j - 1;
return 1;
}
if (buf[j] == ',') {
switch(buf[i]) {
case '/':
fp->flagtrycwd = 1;
break;
case 'r':
fp->flagtryretr = 1;
break;
case 's':
fp->sizetype = FTPPARSE_SIZE_BINARY;
fp->size = getlong(buf + i + 1,j - i - 1);
break;
case 'm':
fp->mtimetype = FTPPARSE_MTIME_LOCAL;
initbase();
fp->mtime = base + getlong(buf + i + 1,j - i - 1);
break;
case 'i':
fp->idtype = FTPPARSE_ID_FULL;
fp->id = buf + i + 1;
fp->idlen = j - i - 1;
}
i = j + 1;
}
}
return 0;
/* UNIX-style listing, without inum and without blocks */
/* "-rw-r--r-- 1 root other 531 Jan 29 03:26 README" */
/* "dr-xr-xr-x 2 root other 512 Apr 8 1994 etc" */
/* "dr-xr-xr-x 2 root 512 Apr 8 1994 etc" */
/* "lrwxrwxrwx 1 root other 7 Jan 25 00:17 bin -> usr/bin" */
/* Also produced by Microsoft's FTP servers for Windows: */
/* "---------- 1 owner group 1803128 Jul 10 10:18 ls-lR.Z" */
/* "d--------- 1 owner group 0 May 9 19:45 Softlib" */
/* Also WFTPD for MSDOS: */
/* "-rwxrwxrwx 1 noone nogroup 322 Aug 19 1996 message.ftp" */
/* Also NetWare: */
/* "d [R----F--] supervisor 512 Jan 16 18:53 login" */
/* "- [R----F--] rhesus 214059 Oct 20 15:27 cx.exe" */
/* Also NetPresenz for the Mac: */
/* "-------r-- 326 1391972 1392298 Nov 22 1995 MegaPhone.sit" */
/* "drwxrwxr-x folder 2 May 10 1996 network" */
case 'b':
case 'c':
case 'd':
case 'l':
case 'p':
case 's':
case '-':
if (*buf == 'd') fp->flagtrycwd = 1;
if (*buf == '-') fp->flagtryretr = 1;
if (*buf == 'l') fp->flagtrycwd = fp->flagtryretr = 1;
state = 1;
i = 0;
for (j = 1;j < len;++j)
if ((buf[j] == ' ') && (buf[j - 1] != ' ')) {
switch(state) {
case 1: /* skipping perm */
state = 2;
break;
case 2: /* skipping nlink */
state = 3;
if ((j - i == 6) && (buf[i] == 'f')) /* for NetPresenz */
state = 4;
break;
case 3: /* skipping uid */
state = 4;
break;
case 4: /* getting tentative size */
size = getlong(buf + i,j - i);
state = 5;
break;
case 5: /* searching for month, otherwise getting tentative size */
month = getmonth(buf + i,j - i);
if (month >= 0)
state = 6;
else
size = getlong(buf + i,j - i);
break;
case 6: /* have size and month */
mday = getlong(buf + i,j - i);
state = 7;
break;
case 7: /* have size, month, mday */
if ((j - i == 4) && (buf[i + 1] == ':')) {
hour = getlong(buf + i,1);
minute = getlong(buf + i + 2,2);
fp->mtimetype = FTPPARSE_MTIME_REMOTEMINUTE;
initbase();
fp->mtime = base + guesstai(month,mday) + hour * 3600 + minute * 60;
} else if ((j - i == 5) && (buf[i + 2] == ':')) {
hour = getlong(buf + i,2);
minute = getlong(buf + i + 3,2);
fp->mtimetype = FTPPARSE_MTIME_REMOTEMINUTE;
initbase();
fp->mtime = base + guesstai(month,mday) + hour * 3600 + minute * 60;
}
else if (j - i >= 4) {
year = getlong(buf + i,j - i);
fp->mtimetype = FTPPARSE_MTIME_REMOTEDAY;
initbase();
fp->mtime = base + totai(year,month,mday);
}
else
return 0;
fp->name = buf + j + 1;
fp->namelen = len - j - 1;
state = 8;
break;
case 8: /* twiddling thumbs */
break;
}
i = j + 1;
while ((i < len) && (buf[i] == ' ')) ++i;
}
if (state != 8)
return 0;
fp->size = size;
fp->sizetype = FTPPARSE_SIZE_ASCII;
if (*buf == 'l')
for (i = 0;i + 3 < fp->namelen;++i)
if (fp->name[i] == ' ')
if (fp->name[i + 1] == '-')
if (fp->name[i + 2] == '>')
if (fp->name[i + 3] == ' ') {
fp->namelen = i;
break;
}
/* eliminate extra NetWare spaces */
if ((buf[1] == ' ') || (buf[1] == '['))
if (fp->namelen > 3)
if (fp->name[0] == ' ')
if (fp->name[1] == ' ')
if (fp->name[2] == ' ') {
fp->name += 3;
fp->namelen -= 3;
}
return 1;
}
/* MultiNet (some spaces removed from examples) */
/* "00README.TXT;1 2 30-DEC-1996 17:44 [SYSTEM] (RWED,RWED,RE,RE)" */
/* "CORE.DIR;1 1 8-SEP-1996 16:09 [SYSTEM] (RWE,RWE,RE,RE)" */
/* and non-MutliNet VMS: */
/* "CII-MANUAL.TEX;1 213/216 29-JAN-1996 03:33:12 [ANONYMOU,ANONYMOUS] (RWED,RWED,,)" */
for (i = 0;i < len;++i)
if (buf[i] == ';')
break;
if (i < len) {
fp->name = buf;
fp->namelen = i;
if (i > 4)
if (buf[i - 4] == '.')
if (buf[i - 3] == 'D')
if (buf[i - 2] == 'I')
if (buf[i - 1] == 'R') {
fp->namelen -= 4;
fp->flagtrycwd = 1;
}
if (!fp->flagtrycwd)
fp->flagtryretr = 1;
while (buf[i] != ' ') if (++i == len) return 0;
while (buf[i] == ' ') if (++i == len) return 0;
while (buf[i] != ' ') if (++i == len) return 0;
while (buf[i] == ' ') if (++i == len) return 0;
j = i;
while (buf[j] != '-') if (++j == len) return 0;
mday = getlong(buf + i,j - i);
while (buf[j] == '-') if (++j == len) return 0;
i = j;
while (buf[j] != '-') if (++j == len) return 0;
month = getmonth(buf + i,j - i);
if (month < 0) return 0;
while (buf[j] == '-') if (++j == len) return 0;
i = j;
while (buf[j] != ' ') if (++j == len) return 0;
year = getlong(buf + i,j - i);
while (buf[j] == ' ') if (++j == len) return 0;
i = j;
while (buf[j] != ':') if (++j == len) return 0;
hour = getlong(buf + i,j - i);
while (buf[j] == ':') if (++j == len) return 0;
i = j;
while ((buf[j] != ':') && (buf[j] != ' ')) if (++j == len) return 0;
minute = getlong(buf + i,j - i);
fp->mtimetype = FTPPARSE_MTIME_REMOTEMINUTE;
initbase();
fp->mtime = base + totai(year,month,mday) + hour * 3600 + minute * 60;
return 1;
}
/* Some useless lines, safely ignored: */
/* "Total of 11 Files, 10966 Blocks." (VMS) */
/* "total 14786" (UNIX) */
/* "DISK$ANONFTP:[ANONYMOUS]" (VMS) */
/* "Directory DISK$PCSA:[ANONYM]" (VMS) */
return 0;
}

51
src/ftpparse.h Normal file
View File

@ -0,0 +1,51 @@
#ifndef FTPPARSE_H
#define FTPPARSE_H
/*
ftpparse(&fp,buf,len) tries to parse one line of LIST output.
The line is an array of len characters stored in buf.
It should not include the terminating CR LF; so buf[len] is typically CR.
If ftpparse() can't find a filename, it returns 0.
If ftpparse() can find a filename, it fills in fp and returns 1.
fp is a struct ftpparse, defined below.
The name is an array of fp.namelen characters stored in fp.name;
fp.name points somewhere within buf.
*/
struct ftpparse {
char *name; /* not necessarily 0-terminated */
int namelen;
int flagtrycwd; /* 0 if cwd is definitely pointless, 1 otherwise */
int flagtryretr; /* 0 if retr is definitely pointless, 1 otherwise */
int sizetype;
long size; /* number of octets */
int mtimetype;
time_t mtime; /* modification time */
int idtype;
char *id; /* not necessarily 0-terminated */
int idlen;
} ;
#define FTPPARSE_SIZE_UNKNOWN 0
#define FTPPARSE_SIZE_BINARY 1 /* size is the number of octets in TYPE I */
#define FTPPARSE_SIZE_ASCII 2 /* size is the number of octets in TYPE A */
#define FTPPARSE_MTIME_UNKNOWN 0
#define FTPPARSE_MTIME_LOCAL 1 /* time is correct */
#define FTPPARSE_MTIME_REMOTEMINUTE 2 /* time zone and secs are unknown */
#define FTPPARSE_MTIME_REMOTEDAY 3 /* time zone and time of day are unknown */
/*
When a time zone is unknown, it is assumed to be GMT. You may want
to use localtime() for LOCAL times, along with an indication that the
time is correct in the local time zone, and gmtime() for REMOTE* times.
*/
#define FTPPARSE_ID_UNKNOWN 0
#define FTPPARSE_ID_FULL 1 /* unique identifier for files on this FTP server */
extern int ftpparse();
#endif