. */
static int
vmsperms (const char *s)
{
int perms = 0;
do
{
switch (*s) {
case ',': perms <<= 3; break;
case 'R': perms |= 4; break;
case 'W': perms |= 2; break;
case 'D': perms |= 2; break;
case 'E': perms |= 1; break;
default: DEBUGP(("wrong VMS permissons!\n"));
}
}
while (*++s);
return perms;
}
static struct fileinfo *
ftp_parse_vms_ls (const char *file)
{
FILE *fp;
/* #### A third copy of more-or-less the same array ? */
static const char *months[] = {
"JAN", "FEB", "MAR", "APR", "MAY", "JUN",
"JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
};
int i;
int year, month, day; /* for time analysis */
int hour, min, sec;
struct tm timestruct;
char *line, *tok; /* 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;
/* Skip empty line. */
line = read_whole_line (fp);
xfree_null (line);
/* Skip "Directory PUB$DEVICE[PUB]" */
line = read_whole_line (fp);
xfree_null (line);
/* Skip empty line. */
line = read_whole_line (fp);
xfree_null (line);
/* Line loop to end of file: */
while ((line = read_whole_line (fp)) != NULL)
{
char *p;
i = clean_line (line);
if (!i)
{
xfree (line);
break;
}
/* First column: Name. A bit of black magic again. The name my be
either ABCD.EXT or ABCD.EXT;NUM and it might be on a separate
line. Therefore we will first try to get the complete name
until the first space character; if it fails, we assume that the name
occupies the whole line. After that we search for the version
separator ";", we remove it and check the extension of the file;
extension .DIR denotes directory. */
tok = strtok(line, " ");
if (tok == NULL) tok = line;
DEBUGP(("file name: '%s'\n", tok));
for (p = tok ; *p && *p != ';' ; p++)
;
if (*p == ';') *p = '\0';
p = tok + strlen(tok) - 4;
if (!strcmp(p, ".DIR")) *p = '\0';
cur.name = xstrdup(tok);
DEBUGP(("Name: '%s'\n", cur.name));
/* If the name ends on .DIR or .DIR;#, it's a directory. We also set
the file size to zero as the listing does tell us only the size in
filesystem blocks - for an integrity check (when mirroring, for
example) we would need the size in bytes. */
if (! *p)
{
cur.type = FT_DIRECTORY;
cur.size = 0;
DEBUGP(("Directory\n"));
}
else
{
cur.type = FT_PLAINFILE;
DEBUGP(("File\n"));
}
cur.size = 0;
/* Second column, if exists, or the first column of the next line
contain file size in blocks. We will skip it. */
tok = strtok(NULL, " ");
if (tok == NULL)
{
DEBUGP(("Getting additional line\n"));
xfree (line);
line = read_whole_line (fp);
if (!line)
{
DEBUGP(("empty line read, leaving listing parser\n"));
break;
}
i = clean_line (line);
if (!i)
{
DEBUGP(("confusing VMS listing item, leaving listing parser\n"));
xfree (line);
break;
}
tok = strtok(line, " ");
}
DEBUGP(("second token: '%s'\n", tok));
/* Third/Second column: Date DD-MMM-YYYY. */
tok = strtok(NULL, "-");
if (tok == NULL) continue;
DEBUGP(("day: '%s'\n",tok));
day = atoi(tok);
tok = strtok(NULL, "-");
if (!tok)
{
/* If the server produces garbage like
'EA95_0PS.GZ;1 No privilege for attempted operation'
the first strtok(NULL, "-") will return everything until the end
of the line and only the next strtok() call will return NULL. */
DEBUGP(("nonsense in VMS listing, skipping this line\n"));
xfree (line);
break;
}
for (i=0; i<12; i++) if (!strcmp(tok,months[i])) break;
/* Uknown months are mapped to January */
month = i % 12 ;
tok = strtok (NULL, " ");
if (tok == NULL) continue;
year = atoi (tok) - 1900;
DEBUGP(("date parsed\n"));
/* Fourth/Third column: Time hh:mm[:ss] */
tok = strtok (NULL, " ");
if (tok == NULL) continue;
min = sec = 0;
p = tok;
hour = atoi (p);
for (; *p && *p != ':'; ++p)
;
if (*p)
min = atoi (++p);
for (; *p && *p != ':'; ++p)
;
if (*p)
sec = atoi (++p);
DEBUGP(("YYYY/MM/DD HH:MM:SS - %d/%02d/%02d %02d:%02d:%02d\n",
year+1900, month, day, hour, min, sec));
/* Build the time-stamp (copy & paste from above) */
timestruct.tm_sec = sec;
timestruct.tm_min = min;
timestruct.tm_hour = hour;
timestruct.tm_mday = day;
timestruct.tm_mon = month;
timestruct.tm_year = year;
timestruct.tm_wday = 0;
timestruct.tm_yday = 0;
timestruct.tm_isdst = -1;
cur.tstamp = mktime (×truct); /* store the time-stamp */
DEBUGP(("Timestamp: %ld\n", cur.tstamp));
/* Skip the fifth column */
tok = strtok(NULL, " ");
if (tok == NULL) continue;
/* Sixth column: Permissions */
tok = strtok(NULL, ","); /* Skip the VMS-specific SYSTEM permissons */
if (tok == NULL) continue;
tok = strtok(NULL, ")");
if (tok == NULL)
{
DEBUGP(("confusing VMS permissions, skipping line\n"));
xfree (line);
continue;
}
/* Permissons have the format "RWED,RWED,RE" */
cur.perms = vmsperms(tok);
DEBUGP(("permissions: %s -> 0%o\n", tok, cur.perms));
cur.linkto = NULL;
/* And put everything into the linked list */
if (!dir)
{
l = dir = xnew (struct fileinfo);
memcpy (l, &cur, sizeof (cur));
l->prev = l->next = NULL;
}
else
{
cur.prev = l;
l->next = xnew (struct fileinfo);
l = l->next;
memcpy (l, &cur, sizeof (cur));
l->next = NULL;
}
xfree (line);
}
fclose (fp);
return dir;
}
/* This function switches between the correct parsing routine depending on
the SYSTEM_TYPE. The system type should be based on the result of the
"SYST" response of the FTP server. According to this repsonse we will
use on of the three different listing parsers that cover the most of FTP
servers used nowadays. */
struct fileinfo *
ftp_parse_ls (const char *file, const enum stype system_type)
{
switch (system_type)
{
case ST_UNIX:
return ftp_parse_unix_ls (file, 0);
case ST_WINNT:
{
/* Detect whether the listing is simulating the UNIX format */
FILE *fp;
int c;
fp = fopen (file, "rb");
if (!fp)
{
logprintf (LOG_NOTQUIET, "%s: %s\n", file, strerror (errno));
return NULL;
}
c = fgetc(fp);
fclose(fp);
/* If the first character of the file is '0'-'9', it's WINNT
format. */
if (c >= '0' && c <='9')
return ftp_parse_winnt_ls (file);
else
return ftp_parse_unix_ls (file, 1);
}
case ST_VMS:
return ftp_parse_vms_ls (file);
case ST_MACOS:
return ftp_parse_unix_ls (file, 1);
default:
logprintf (LOG_NOTQUIET, _("\
Unsupported listing type, trying Unix listing parser.\n"));
return ftp_parse_unix_ls (file, 0);
}
}
/* Stuff for creating FTP index. */
/* The function creates an HTML index containing references to given
directories and files on the appropriate host. The references are
FTP. */
uerr_t
ftp_index (const char *file, struct url *u, struct fileinfo *f)
{
FILE *fp;
char *upwd;
char *htcldir; /* HTML-clean dir name */
char *htclfile; /* HTML-clean file name */
char *urlclfile; /* URL-clean file name */
if (!output_stream)
{
fp = fopen (file, "wb");
if (!fp)
{
logprintf (LOG_NOTQUIET, "%s: %s\n", file, strerror (errno));
return FOPENERR;
}
}
else
fp = output_stream;
if (u->user)
{
char *tmpu, *tmpp; /* temporary, clean user and passwd */
tmpu = url_escape (u->user);
tmpp = u->passwd ? url_escape (u->passwd) : NULL;
if (tmpp)
upwd = concat_strings (tmpu, ":", tmpp, "@", (char *) 0);
else
upwd = concat_strings (tmpu, "@", (char *) 0);
xfree (tmpu);
xfree_null (tmpp);
}
else
upwd = xstrdup ("");
htcldir = html_quote_string (u->dir);
fprintf (fp, "\n");
fprintf (fp, "\n\n");
fprintf (fp, _("Index of /%s on %s:%d"), htcldir, u->host, u->port);
fprintf (fp, "\n\n\n");
fprintf (fp, _("Index of /%s on %s:%d"), htcldir, u->host, u->port);
fprintf (fp, "
\n
\n\n");
xfree (htcldir);
while (f)
{
fprintf (fp, " ");
if (f->tstamp != -1)
{
/* #### Should we translate the months? Or, even better, use
ISO 8601 dates? */
static const char *months[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
time_t tstamp = f->tstamp;
struct tm *ptm = localtime (&tstamp);
fprintf (fp, "%d %s %02d ", ptm->tm_year + 1900, months[ptm->tm_mon],
ptm->tm_mday);
if (ptm->tm_hour)
fprintf (fp, "%02d:%02d ", ptm->tm_hour, ptm->tm_min);
else
fprintf (fp, " ");
}
else
fprintf (fp, _("time unknown "));
switch (f->type)
{
case FT_PLAINFILE:
fprintf (fp, _("File "));
break;
case FT_DIRECTORY:
fprintf (fp, _("Directory "));
break;
case FT_SYMLINK:
fprintf (fp, _("Link "));
break;
default:
fprintf (fp, _("Not sure "));
break;
}
htclfile = html_quote_string (f->name);
urlclfile = url_escape_unsafe_and_reserved (f->name);
fprintf (fp, "host, u->port);
if (*u->dir != '/')
putc ('/', fp);
fprintf (fp, "%s", u->dir);
if (*u->dir)
putc ('/', fp);
fprintf (fp, "%s", urlclfile);
if (f->type == FT_DIRECTORY)
putc ('/', fp);
fprintf (fp, "\">%s", htclfile);
if (f->type == FT_DIRECTORY)
putc ('/', fp);
fprintf (fp, " ");
if (f->type == FT_PLAINFILE)
fprintf (fp, _(" (%s bytes)"), number_to_static_string (f->size));
else if (f->type == FT_SYMLINK)
fprintf (fp, "-> %s", f->linkto ? f->linkto : "(nil)");
putc ('\n', fp);
xfree (htclfile);
xfree (urlclfile);
f = f->next;
}
fprintf (fp, "
\n\n\n");
xfree (upwd);
if (!output_stream)
fclose (fp);
else
fflush (fp);
return FTPOK;
}