From c3d636db033551ec654fecd7116cf7ecac467fd9 Mon Sep 17 00:00:00 2001 From: hniksic Date: Tue, 21 Nov 2000 08:48:39 -0800 Subject: [PATCH] [svn] Committed Jan's ftpparse patch with Hrvoje's modifications. . --- src/ChangeLog | 28 ++++ src/Makefile.in | 8 +- src/ftp-basic.c | 96 +++++++++++ src/ftp-ls.c | 122 ++++++++++++-- src/ftp.c | 131 ++++++++++++++- src/ftp.h | 14 +- src/ftpparse.c | 411 ++++++++++++++++++++++++++++++++++++++++++++++++ src/ftpparse.h | 51 ++++++ 8 files changed, 837 insertions(+), 24 deletions(-) create mode 100644 src/ftpparse.c create mode 100644 src/ftpparse.h diff --git a/src/ChangeLog b/src/ChangeLog index 7ec215bd..237439ba 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,31 @@ +2000-11-21 Hrvoje Niksic + + * 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 + + * 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 * hash.c (hash_table_put): Don't overwrite deleted mappings. diff --git a/src/Makefile.in b/src/Makefile.in index bfe9868d..27cf75a7 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -57,10 +57,10 @@ MD5_OBJ = @MD5_OBJ@ OPIE_OBJ = @OPIE_OBJ@ 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 \ - html-parse$o html-url$o http$o init$o log$o main$o \ - $(MD5_OBJ) netrc$o rbuf$o recur$o retr$o snprintf$o \ - url$o utils$o version$o + ftp-ls$o $(OPIE_OBJ) ftpparse$o getopt$o hash$o \ + headers$o host$o html-parse$o html-url$o http$o init$o \ + log$o main$o $(MD5_OBJ) netrc$o rbuf$o recur$o retr$o \ + snprintf$o url$o utils$o version$o .SUFFIXES: .SUFFIXES: .c .o ._c ._o diff --git a/src/ftp-basic.c b/src/ftp-basic.c index 8d433a69..2de25969 100644 --- a/src/ftp-basic.c +++ b/src/ftp-basic.c @@ -41,6 +41,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "rbuf.h" #include "connect.h" #include "host.h" +#include "ftp.h" #ifndef errno extern int errno; @@ -537,3 +538,98 @@ ftp_list (struct rbuf *rbuf, const char *file) /* All OK. */ 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; +} diff --git a/src/ftp-ls.c b/src/ftp-ls.c index 884cf3d8..54e345ed 100644 --- a/src/ftp-ls.c +++ b/src/ftp-ls.c @@ -1,5 +1,5 @@ /* 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. @@ -38,6 +38,15 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ftp.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 rwxr-xr-x to 755. For now, it knows nothing of setuid/setgid/sticky. ACLs are ignored. */ @@ -376,18 +385,111 @@ ftp_parse_unix_ls (const char *file) return dir; } -/* This function is just a stub. It should actually accept some kind - 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. +#ifdef HAVE_FTPPARSE + +/* This is a "glue function" that connects the ftpparse interface to + the interface Wget expects. ftpparse is used to parse listings + from servers other than Unix, like those running VMS or NT. */ + +static struct fileinfo * +ftp_parse_nonunix_ls (const char *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 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. */ - Since we currently support only the Unix FTP servers, this function - simply returns the result of ftp_parse_unix_ls(). */ struct fileinfo * -ftp_parse_ls (const char *file) +ftp_parse_ls (const char *file, const enum stype system_type) { - return ftp_parse_unix_ls (file); + 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. */ diff --git a/src/ftp.c b/src/ftp.c index aa283cf8..d628ad1d 100644 --- a/src/ftp.c +++ b/src/ftp.c @@ -57,6 +57,10 @@ extern int h_errno; 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 the string S, and return the number converted to long, if found, 0 otherwise. */ @@ -240,7 +244,64 @@ Error in server response, closing control connection.\n")); exit (1); 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) logprintf (LOG_VERBOSE, "==> TYPE %c ... ", 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")); else { - /* Change working directory. */ - if (!opt.server_response) - logprintf (LOG_VERBOSE, "==> CWD %s ... ", u->dir); - err = ftp_cwd (&con->rbuf, u->dir); + /* Change working directory. If the FTP host runs VMS and + the path specified is absolute, we will have to convert + it to VMS style as VMS does not like leading slashes */ + 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 */ switch (err) { @@ -1080,7 +1186,7 @@ ftp_get_listing (struct urlinfo *u, ccon *con) err = ftp_loop_internal (u, NULL, con); u->local = olocal; if (err == RETROK) - f = ftp_parse_ls (list_filename); + f = ftp_parse_ls (list_filename, host_type); else f = NULL; 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) { 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; } 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); + } } } } /* opt.timestamping && f->type == FT_PLAINFILE */ diff --git a/src/ftp.h b/src/ftp.h index 064e6354..61c72400 100644 --- a/src/ftp.h +++ b/src/ftp.h @@ -1,5 +1,5 @@ /* 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 it under the terms of the GNU General Public License as published by @@ -21,6 +21,14 @@ /* Need it for struct rbuf. */ #include "rbuf.h" +/* System types. */ +enum stype +{ + ST_UNIX, + ST_VMS, + ST_OTHER +}; + uerr_t ftp_response PARAMS ((struct rbuf *, char **)); uerr_t ftp_login PARAMS ((struct rbuf *, const char *, const char *)); 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_rest PARAMS ((struct rbuf *, long)); 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; @@ -89,7 +99,7 @@ typedef struct long dltime; /* time of the download */ } 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_index (const char *, struct urlinfo *, struct fileinfo *); diff --git a/src/ftpparse.c b/src/ftpparse.c new file mode 100644 index 00000000..ead436c6 --- /dev/null +++ b/src/ftpparse.c @@ -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 +#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; +} diff --git a/src/ftpparse.h b/src/ftpparse.h new file mode 100644 index 00000000..9d626059 --- /dev/null +++ b/src/ftpparse.h @@ -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 -- 2.39.2