root/src/vfs/ftpfs/ftpfs_parse_ls.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. ftpfs_get_uid
  2. ftpfs_get_gid
  3. ftpfs_init_time
  4. guess_year
  5. parse_year_or_time
  6. mktime_from_utc
  7. ftpfs_convert_date
  8. parse_ls_line
  9. ftpfs_parse_long_list_UNIX
  10. ftpfs_parse_long_list_NT
  11. ftpfs_parse_long_list_EPLF
  12. ftpfs_parse_long_list_MLSD
  13. ftpfs_parse_long_list_AS400
  14. ftpfs_parse_long_list_OS2
  15. ftpfs_parse_long_list_MacWebStar
  16. ftpfs_parse_long_list

   1 /*
   2    Virtual File System: FTP file system
   3 
   4    Copyright (C) 2015-2021
   5    The Free Software Foundation, Inc.
   6 
   7    Written by: Andrew Borodin <aborodin@vmail.ru>, 2013
   8 
   9    This file is part of the Midnight Commander.
  10 
  11    The Midnight Commander is free software: you can redistribute it
  12    and/or modify it under the terms of the GNU General Public License as
  13    published by the Free Software Foundation, either version 3 of the License,
  14    or (at your option) any later version.
  15 
  16    The Midnight Commander is distributed in the hope that it will be useful,
  17    but WITHOUT ANY WARRANTY; without even the implied warranty of
  18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19    GNU General Public License for more details.
  20 
  21    You should have received a copy of the GNU General Public License
  22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  23  */
  24 
  25 /** \file
  26  *  \brief Source: Virtual File System: FTP file system
  27  *  \author Andrew Borodin
  28  *  \date 2015
  29  *
  30  *  Parser of ftp long file list (reply to "LIST -la" command).
  31  *  Borrowed from lftp project (http://http://lftp.yar.ru/).
  32  *  Author of original lftp code: Alexander V. Lukyanov (lav@yars.free.net)
  33  */
  34 
  35 #include <config.h>
  36 
  37 #include <ctype.h>              /* isdigit() */
  38 #include <stdio.h>              /* sscanf() */
  39 #include <stdlib.h>
  40 #include <string.h>
  41 #include <sys/stat.h>           /* mode_t */
  42 #include <time.h>
  43 #include <unistd.h>
  44 #include <sys/types.h>
  45 
  46 #include "lib/global.h"
  47 
  48 #include "lib/vfs/vfs.h"
  49 #include "lib/vfs/utilvfs.h"
  50 
  51 #include "ftpfs.h"
  52 
  53 /*** global variables ****************************************************************************/
  54 
  55 /*** file scope macro definitions ****************************************************************/
  56 
  57 #define number_of_parsers 7
  58 
  59 #define MINUTE (60)
  60 #define HOUR   (60 * MINUTE)
  61 #define DAY    (24 * HOUR)
  62 
  63 #define NO_SIZE     ((off_t) (-1L))
  64 #define NO_SIZE_YET ((off_t) (-2L))
  65 #define NO_DATE     ((time_t) (-1L))
  66 #define NO_DATE_YET ((time_t) (-2L))
  67 
  68 #define FIRST_TOKEN strtok (line, " \t")
  69 #define NEXT_TOKEN  strtok (NULL, " \t")
  70 #define FIRST_TOKEN_R strtok_r (line, " \t", &next)
  71 #define NEXT_TOKEN_R  strtok_r (NULL, " \t", &next)
  72 
  73 #define ERR2 do { (*err)++; return FALSE; } while (FALSE)
  74 
  75 /*** file scope type declarations ****************************************************************/
  76 
  77 typedef enum
  78 {
  79     UNKNOWN = 0,
  80     DIRECTORY,
  81     SYMLINK,
  82     NORMAL
  83 } filetype;
  84 
  85 typedef gboolean (*ftpfs_line_parser) (char *line, struct stat * s, char **filename,
  86                                        char **linkname, int *err);
  87 
  88 /* formard declarations */
  89 static gboolean ftpfs_parse_long_list_UNIX (char *line, struct stat *s, char **filename,
  90                                             char **linkname, int *err);
  91 static gboolean ftpfs_parse_long_list_NT (char *line, struct stat *s, char **filename,
  92                                           char **linkname, int *err);
  93 static gboolean ftpfs_parse_long_list_EPLF (char *line, struct stat *s, char **filename,
  94                                             char **linkname, int *err);
  95 static gboolean ftpfs_parse_long_list_MLSD (char *line, struct stat *s, char **filename,
  96                                             char **linkname, int *err);
  97 static gboolean ftpfs_parse_long_list_AS400 (char *line, struct stat *s, char **filename,
  98                                              char **linkname, int *err);
  99 static gboolean ftpfs_parse_long_list_OS2 (char *line, struct stat *s, char **filename,
 100                                            char **linkname, int *err);
 101 static gboolean ftpfs_parse_long_list_MacWebStar (char *line, struct stat *s, char **filename,
 102                                                   char **linkname, int *err);
 103 
 104 /*** file scope variables ************************************************************************/
 105 
 106 static time_t rawnow;
 107 static struct tm now;
 108 
 109 static ftpfs_line_parser line_parsers[number_of_parsers] = {
 110     ftpfs_parse_long_list_UNIX,
 111     ftpfs_parse_long_list_NT,
 112     ftpfs_parse_long_list_EPLF,
 113     ftpfs_parse_long_list_MLSD,
 114     ftpfs_parse_long_list_AS400,
 115     ftpfs_parse_long_list_OS2,
 116     ftpfs_parse_long_list_MacWebStar
 117 };
 118 
 119 /* --------------------------------------------------------------------------------------------- */
 120 /*** file scope functions ************************************************************************/
 121 /* --------------------------------------------------------------------------------------------- */
 122 
 123 static inline uid_t
 124 ftpfs_get_uid (const char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 125 {
 126     uid_t u;
 127 
 128     if (*s < '0' || *s > '9')
 129         u = vfs_finduid (s);
 130     else
 131         u = (uid_t) atol (s);
 132 
 133     return u;
 134 }
 135 
 136 /* --------------------------------------------------------------------------------------------- */
 137 
 138 static inline gid_t
 139 ftpfs_get_gid (const char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 140 {
 141     gid_t g;
 142 
 143     if (*s < '0' || *s > '9')
 144         g = vfs_findgid (s);
 145     else
 146         g = (gid_t) atol (s);
 147 
 148     return g;
 149 }
 150 
 151 /* --------------------------------------------------------------------------------------------- */
 152 
 153 static void
 154 ftpfs_init_time (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 155 {
 156     time (&rawnow);
 157     now = *localtime (&rawnow);
 158 }
 159 
 160 /* --------------------------------------------------------------------------------------------- */
 161 
 162 static int
 163 guess_year (int month, int day, int hour, int minute)
     /* [previous][next][first][last][top][bottom][index][help]  */
 164 {
 165     int year;
 166 
 167     (void) hour;
 168     (void) minute;
 169 
 170     year = now.tm_year + 1900;
 171 
 172     if (month * 32 + day > now.tm_mon * 32 + now.tm_mday + 6)
 173         year--;
 174 
 175     return year;
 176 }
 177 
 178 /* --------------------------------------------------------------------------------------------- */
 179 
 180 static gboolean
 181 parse_year_or_time (const char *year_or_time, int *year, int *hour, int *minute)
     /* [previous][next][first][last][top][bottom][index][help]  */
 182 {
 183     if (year_or_time[2] == ':')
 184     {
 185         if (sscanf (year_or_time, "%2d:%2d", hour, minute) != 2)
 186             return FALSE;
 187 
 188         *year = -1;
 189     }
 190     else
 191     {
 192         if (sscanf (year_or_time, "%d", year) != 1)
 193             return FALSE;
 194 
 195         *hour = *minute = 0;
 196     }
 197 
 198     return TRUE;
 199 }
 200 
 201 /* --------------------------------------------------------------------------------------------- */
 202 
 203 /* Converts struct tm to time_t, assuming the data in tm is UTC rather
 204    than local timezone (mktime assumes the latter).
 205 
 206    Contributed by Roger Beeman <beeman@cisco.com>, with the help of
 207    Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO.  */
 208 static time_t
 209 mktime_from_utc (const struct tm *t)
     /* [previous][next][first][last][top][bottom][index][help]  */
 210 {
 211     struct tm tc;
 212     time_t tl, tb;
 213 
 214     memcpy (&tc, t, sizeof (struct tm));
 215 
 216     /* UTC times are never DST; if we say -1, we'll introduce odd localtime-
 217      * dependant errors. */
 218 
 219     tc.tm_isdst = 0;
 220 
 221     tl = mktime (&tc);
 222     if (tl == -1)
 223         return (-1);
 224 
 225     tb = mktime (gmtime (&tl));
 226 
 227     return (tl <= tb ? (tl + (tl - tb)) : (tl - (tb - tl)));
 228 }
 229 
 230 /* --------------------------------------------------------------------------------------------- */
 231 
 232 static time_t
 233 ftpfs_convert_date (const char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 234 {
 235     struct tm tm;
 236     int year, month, day, hour, minute, second;
 237     int skip = 0;
 238     int n;
 239 
 240     memset (&tm, 0, sizeof (tm));
 241 
 242     n = sscanf (s, "%4d%n", &year, &skip);
 243 
 244     /* try to workaround server's y2k bug *
 245      * I hope in the next 300 years the y2k bug will be finally fixed :) */
 246     if (n == 1 && year >= 1910 && year <= 1930)
 247     {
 248         n = sscanf (s, "%5d%n", &year, &skip);
 249         year = year - 19100 + 2000;
 250     }
 251 
 252     if (n != 1)
 253         return NO_DATE;
 254 
 255     n = sscanf (s + skip, "%2d%2d%2d%2d%2d", &month, &day, &hour, &minute, &second);
 256 
 257     if (n != 5)
 258         return NO_DATE;
 259 
 260     tm.tm_year = year - 1900;
 261     tm.tm_mon = month - 1;
 262     tm.tm_mday = day;
 263     tm.tm_hour = hour;
 264     tm.tm_min = minute;
 265     tm.tm_sec = second;
 266 
 267     return mktime_from_utc (&tm);
 268 }
 269 
 270 /* --------------------------------------------------------------------------------------------- */
 271 
 272 /*
 273    -rwxr-xr-x   1 lav      root         4771 Sep 12  1996 install-sh
 274    -rw-r--r--   1 lav      root         1349 Feb  2 14:10 lftp.lsm
 275    drwxr-xr-x   4 lav      root         1024 Feb 22 15:32 lib
 276    lrwxrwxrwx   1 lav      root           33 Feb 14 17:45 ltconfig -> /usr/share/libtool/ltconfig
 277 
 278    NOTE: group may be missing.
 279  */
 280 
 281 static gboolean
 282 parse_ls_line (char *line, struct stat *s, char **filename, char **linkname)
     /* [previous][next][first][last][top][bottom][index][help]  */
 283 {
 284     char *next = NULL;
 285     char *t;
 286     mode_t type, mode = 0;
 287     char *group_or_size;
 288     struct tm date;
 289     const char *day_of_month;
 290     gboolean year_anomaly = FALSE;
 291     char *name;
 292 
 293     /* parse perms */
 294     t = FIRST_TOKEN_R;
 295     if (t == NULL)
 296         return FALSE;
 297 
 298     if (!vfs_parse_filetype (t, NULL, &type))
 299         return FALSE;
 300 
 301     if (vfs_parse_fileperms (t + 1, NULL, &mode))
 302         mode |= type;
 303 
 304     s->st_mode = mode;
 305 
 306     /* link count */
 307     t = NEXT_TOKEN_R;
 308     if (t == NULL)
 309         return FALSE;
 310     s->st_nlink = atol (t);
 311 
 312     /* user */
 313     t = NEXT_TOKEN_R;
 314     if (t == NULL)
 315         return FALSE;
 316 
 317     s->st_uid = ftpfs_get_uid (t);
 318 
 319     /* group or size */
 320     group_or_size = NEXT_TOKEN_R;
 321 
 322     /* size or month */
 323     t = NEXT_TOKEN_R;
 324     if (t == NULL)
 325         return FALSE;
 326     if (isdigit ((unsigned char) *t))
 327     {
 328         /* it's size, so the previous was group: */
 329         long long size;
 330         int n;
 331 
 332         s->st_gid = ftpfs_get_gid (t);
 333 
 334         if (sscanf (t, "%lld%n", &size, &n) == 1 && t[n] == '\0')
 335             s->st_size = (off_t) size;
 336         t = NEXT_TOKEN_R;
 337         if (t == NULL)
 338             return FALSE;
 339     }
 340     else
 341     {
 342         /*  it was month, so the previous was size: */
 343         long long size;
 344         int n;
 345 
 346         if (sscanf (group_or_size, "%lld%n", &size, &n) == 1 && group_or_size[n] == '\0')
 347             s->st_size = (off_t) size;
 348     }
 349 
 350 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
 351     s->st_blksize = 512;
 352 #endif
 353 #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
 354     s->st_blocks = (s->st_size + 511) / 512;
 355 #endif
 356 
 357     memset (&date, 0, sizeof (date));
 358 
 359     if (vfs_parse_month (t, &date))
 360         date.tm_mon = 0;
 361 
 362     day_of_month = NEXT_TOKEN_R;
 363     if (day_of_month == NULL)
 364         return FALSE;
 365     date.tm_mday = atoi (day_of_month);
 366 
 367     /* time or year */
 368     t = NEXT_TOKEN_R;
 369     if (t == NULL)
 370         return FALSE;
 371     date.tm_isdst = -1;
 372     date.tm_hour = date.tm_min = 0;
 373     date.tm_sec = 30;
 374 
 375     if (sscanf (t, "%2d:%2d", &date.tm_hour, &date.tm_min) == 2)
 376         date.tm_year = guess_year (date.tm_mon, date.tm_mday, date.tm_hour, date.tm_min) - 1900;
 377     else
 378     {
 379         if (day_of_month + strlen (day_of_month) + 1 == t)
 380             year_anomaly = TRUE;
 381         date.tm_year = atoi (t) - 1900;
 382         /* We don't know the hour.  Set it to something other than 0, or
 383          * DST -1 will end up changing the date. */
 384         date.tm_hour = 12;
 385         date.tm_min = 0;
 386         date.tm_sec = 0;
 387     }
 388 
 389     s->st_mtime = mktime (&date);
 390     /* Use resulting time value */
 391     s->st_atime = s->st_ctime = s->st_mtime;
 392 
 393     name = strtok_r (NULL, "", &next);
 394     if (name == NULL)
 395         return FALSE;
 396 
 397     /* there are ls which output extra space after year. */
 398     if (year_anomaly && *name == ' ')
 399         name++;
 400 
 401     if (!S_ISLNK (s->st_mode))
 402         *linkname = NULL;
 403     else
 404     {
 405         char *arrow;
 406 
 407         for (arrow = name; (arrow = strstr (arrow, " -> ")) != NULL; arrow++)
 408             if (arrow != name && arrow[4] != '\0')
 409             {
 410                 *arrow = '\0';
 411                 *linkname = g_strdup (arrow + 4);
 412                 break;
 413             }
 414     }
 415 
 416     *filename = g_strdup (name);
 417 
 418     return TRUE;
 419 }
 420 
 421 /* --------------------------------------------------------------------------------------------- */
 422 
 423 static gboolean
 424 ftpfs_parse_long_list_UNIX (char *line, struct stat *s, char **filename, char **linkname, int *err)
     /* [previous][next][first][last][top][bottom][index][help]  */
 425 {
 426     int tmp;
 427     gboolean ret;
 428 
 429     if (sscanf (line, "total %d", &tmp) == 1)
 430         return FALSE;
 431 
 432     if (strncasecmp (line, "Status of ", 10) == 0)
 433         return FALSE;           /* STAT output. */
 434     if (strchr ("bcpsD", line[0]) != NULL)      /* block, char, pipe, socket, Door. */
 435         return FALSE;
 436 
 437     ret = parse_ls_line (line, s, filename, linkname);
 438     if (!ret)
 439         (*err)++;
 440 
 441     return ret;
 442 }
 443 
 444 /* --------------------------------------------------------------------------------------------- */
 445 
 446 /*
 447    07-13-98  09:06PM       <DIR>          aix
 448    07-13-98  09:06PM       <DIR>          hpux
 449    07-13-98  09:06PM       <DIR>          linux
 450    07-13-98  09:06PM       <DIR>          ncr
 451    07-13-98  09:06PM       <DIR>          solaris
 452    03-18-98  06:01AM              2109440 nlxb318e.tar
 453    07-02-98  11:17AM                13844 Whatsnew.txt
 454  */
 455 
 456 static gboolean
 457 ftpfs_parse_long_list_NT (char *line, struct stat *s, char **filename, char **linkname, int *err)
     /* [previous][next][first][last][top][bottom][index][help]  */
 458 {
 459     char *t;
 460     int month, day, year, hour, minute;
 461     char am;
 462     struct tm tms;
 463     long long size;
 464 
 465     t = FIRST_TOKEN;
 466     if (t == NULL)
 467         ERR2;
 468     if (sscanf (t, "%2d-%2d-%2d", &month, &day, &year) != 3)
 469         ERR2;
 470     if (year >= 70)
 471         year += 1900;
 472     else
 473         year += 2000;
 474 
 475     t = NEXT_TOKEN;
 476     if (t == NULL)
 477         ERR2;
 478     am = 'A';                   /* AM/PM is optional */
 479     if (sscanf (t, "%2d:%2d%c", &hour, &minute, &am) < 2)
 480         ERR2;
 481 
 482     t = NEXT_TOKEN;
 483     if (t == NULL)
 484         ERR2;
 485 
 486     if (am == 'P')              /* PM - after noon */
 487     {
 488         hour += 12;
 489         if (hour == 24)
 490             hour = 0;
 491     }
 492 
 493     tms.tm_sec = 30;            /* seconds after the minute [0, 61]  */
 494     tms.tm_min = minute;        /* minutes after the hour [0, 59] */
 495     tms.tm_hour = hour;         /* hour since midnight [0, 23] */
 496     tms.tm_mday = day;          /* day of the month [1, 31] */
 497     tms.tm_mon = month - 1;     /* months since January [0, 11] */
 498     tms.tm_year = year - 1900;  /* years since 1900 */
 499     tms.tm_isdst = -1;
 500 
 501 
 502     s->st_mtime = mktime (&tms);
 503     /* Use resulting time value */
 504     s->st_atime = s->st_ctime = s->st_mtime;
 505 
 506     if (strcmp (t, "<DIR>") == 0)
 507         s->st_mode = S_IFDIR;
 508     else
 509     {
 510         s->st_mode = S_IFREG;
 511         if (sscanf (t, "%lld", &size) != 1)
 512             ERR2;
 513         s->st_size = (off_t) size;
 514     }
 515 
 516     t = strtok (NULL, "");
 517     if (t == NULL)
 518         ERR2;
 519     while (*t == ' ')
 520         t++;
 521     if (*t == '\0')
 522         ERR2;
 523 
 524     *filename = g_strdup (t);
 525     *linkname = NULL;
 526 
 527     return TRUE;
 528 }
 529 
 530 /* --------------------------------------------------------------------------------------------- */
 531 
 532 /*
 533    +i774.71425,m951188401,/,       users
 534    +i774.49602,m917883130,r,s79126,        jgr_www2.exe
 535 
 536    starts with +
 537    comma separated
 538    first character of field is type:
 539    i - ?
 540    m - modification time
 541    / - means directory
 542    r - means plain file
 543    s - size
 544    up - permissions in octal
 545    \t - file name follows.
 546  */
 547 
 548 static gboolean
 549 ftpfs_parse_long_list_EPLF (char *line, struct stat *s, char **filename, char **linkname, int *err)
     /* [previous][next][first][last][top][bottom][index][help]  */
 550 {
 551     size_t len;
 552     const char *b;
 553     const char *name = NULL;
 554     size_t name_len = 0;
 555     off_t size = NO_SIZE;
 556     time_t date = NO_DATE;
 557     long date_l;
 558     long long size_ll;
 559     gboolean dir = FALSE;
 560     gboolean type_known = FALSE;
 561     int perms = -1;
 562     const char *scan;
 563     ssize_t scan_len;
 564 
 565     len = strlen (line);
 566     b = line;
 567 
 568     if (len < 2 || b[0] != '+')
 569         ERR2;
 570 
 571     scan = b + 1;
 572     scan_len = len - 1;
 573 
 574     while (scan != NULL && scan_len > 0)
 575     {
 576         const char *comma;
 577 
 578         switch (*scan)
 579         {
 580         case '\t':             /* the rest is file name. */
 581             name = scan + 1;
 582             name_len = scan_len - 1;
 583             scan = NULL;
 584             break;
 585         case 's':
 586             if (sscanf (scan + 1, "%lld", &size_ll) != 1)
 587                 break;
 588             size = size_ll;
 589             break;
 590         case 'm':
 591             if (sscanf (scan + 1, "%ld", &date_l) != 1)
 592                 break;
 593             date = date_l;
 594             break;
 595         case '/':
 596             dir = TRUE;
 597             type_known = TRUE;
 598             break;
 599         case 'r':
 600             dir = FALSE;
 601             type_known = TRUE;
 602             break;
 603         case 'i':
 604             break;
 605         case 'u':
 606             if (scan[1] == 'p') /* permissions. */
 607                 if (sscanf (scan + 2, "%o", (unsigned int *) &perms) != 1)
 608                     perms = -1;
 609             break;
 610         default:
 611             name = NULL;
 612             scan = NULL;
 613             break;
 614         }
 615         if (scan == NULL || scan_len == 0)
 616             break;
 617 
 618         comma = (const char *) memchr (scan, ',', scan_len);
 619         if (comma == NULL)
 620             break;
 621 
 622         scan_len -= comma + 1 - scan;
 623         scan = comma + 1;
 624     }
 625 
 626     if (name == NULL || !type_known)
 627         ERR2;
 628 
 629     *filename = g_strndup (name, name_len);
 630     *linkname = NULL;
 631 
 632     if (size != NO_SIZE)
 633         s->st_size = size;
 634     if (date != NO_DATE)
 635     {
 636         s->st_mtime = date;
 637         /* Use resulting time value */
 638         s->st_atime = s->st_ctime = s->st_mtime;
 639     }
 640     if (type_known)
 641         s->st_mode = dir ? S_IFDIR : S_IFREG;
 642     if (perms != -1)
 643         s->st_mode |= perms;    /* FIXME */
 644 
 645     return TRUE;
 646 }
 647 
 648 /* --------------------------------------------------------------------------------------------- */
 649 /*
 650    Type=cdir;Modify=20021029173810;Perm=el;Unique=BP8AAjJufAA; /
 651    Type=pdir;Modify=20021029173810;Perm=el;Unique=BP8AAjJufAA; ..
 652    Type=dir;Modify=20010118144705;Perm=e;Unique=BP8AAjNufAA; bin
 653    Type=dir;Modify=19981021003019;Perm=el;Unique=BP8AAlhufAA; pub
 654    Type=file;Size=12303;Modify=19970124132601;Perm=r;Unique=BP8AAo9ufAA; mailserv.FAQ
 655    modify=20161215062118;perm=flcdmpe;type=dir;UNIX.group=503;UNIX.mode=0700; directory-name
 656    modify=20161213121618;perm=adfrw;size=6369064;type=file;UNIX.group=503;UNIX.mode=0644; file-name
 657    modify=20120103123744;perm=adfrw;size=11;type=OS.unix=symlink;UNIX.group=0;UNIX.mode=0777; www
 658  */
 659 
 660 static gboolean
 661 ftpfs_parse_long_list_MLSD (char *line, struct stat *s, char **filename, char **linkname, int *err)
     /* [previous][next][first][last][top][bottom][index][help]  */
 662 {
 663     const char *name = NULL;
 664     off_t size = NO_SIZE;
 665     time_t date = NO_DATE;
 666     const char *owner = NULL;
 667     const char *group = NULL;
 668     filetype type = UNKNOWN;
 669     int perms = -1;
 670     char *space;
 671     char *tok;
 672 
 673     space = strstr (line, "; ");
 674     if (space != NULL)
 675     {
 676         name = space + 2;
 677         *space = '\0';
 678     }
 679     else
 680     {
 681         /* NcFTPd does not put a semicolon after last fact, workaround it. */
 682         space = strchr (line, ' ');
 683         if (space == NULL)
 684             ERR2;
 685         name = space + 1;
 686         *space = '\0';
 687     }
 688 
 689     for (tok = strtok (line, ";"); tok != NULL; tok = strtok (NULL, ";"))
 690     {
 691         if (strcasecmp (tok, "Type=cdir") == 0
 692             || strcasecmp (tok, "Type=pdir") == 0 || strcasecmp (tok, "Type=dir") == 0)
 693         {
 694             type = DIRECTORY;
 695             continue;
 696         }
 697         if (strcasecmp (tok, "Type=file") == 0)
 698         {
 699             type = NORMAL;
 700             continue;
 701         }
 702         if (strcasecmp (tok, "Type=OS.unix=symlink") == 0)
 703         {
 704             type = SYMLINK;
 705             continue;
 706         }
 707         if (strncasecmp (tok, "Modify=", 7) == 0)
 708         {
 709             date = ftpfs_convert_date (tok + 7);
 710             continue;
 711         }
 712         if (strncasecmp (tok, "Size=", 5) == 0)
 713         {
 714             long long size_ll;
 715 
 716             if (sscanf (tok + 5, "%lld", &size_ll) == 1)
 717                 size = size_ll;
 718             continue;
 719         }
 720         if (strncasecmp (tok, "Perm=", 5) == 0)
 721         {
 722             perms = 0;
 723             for (tok += 5; *tok != '\0'; tok++)
 724             {
 725                 switch (g_ascii_tolower (*tok))
 726                 {
 727                 case 'e':
 728                     perms |= 0111;
 729                     break;
 730                 case 'l':
 731                     perms |= 0444;
 732                     break;
 733                 case 'r':
 734                     perms |= 0444;
 735                     break;
 736                 case 'c':
 737                     perms |= 0200;
 738                     break;
 739                 case 'w':
 740                     perms |= 0200;
 741                     break;
 742                 default:
 743                     break;
 744                 }
 745             }
 746             continue;
 747         }
 748         if (strncasecmp (tok, "UNIX.mode=", 10) == 0)
 749         {
 750             if (sscanf (tok + 10, "%o", (unsigned int *) &perms) != 1)
 751                 perms = -1;
 752             continue;
 753         }
 754         if (strncasecmp (tok, "UNIX.owner=", 11) == 0)
 755         {
 756             owner = tok + 11;
 757             continue;
 758         }
 759         if (strncasecmp (tok, "UNIX.group=", 11) == 0)
 760         {
 761             group = tok + 11;
 762             continue;
 763         }
 764         if (strncasecmp (tok, "UNIX.uid=", 9) == 0)
 765         {
 766             if (owner == NULL)
 767                 owner = tok + 9;
 768             continue;
 769         }
 770         if (strncasecmp (tok, "UNIX.gid=", 9) == 0)
 771         {
 772             if (group == NULL)
 773                 group = tok + 9;
 774             continue;
 775         }
 776     }
 777     if (name == NULL || name[0] == '\0' || type == UNKNOWN)
 778         ERR2;
 779 
 780     *filename = g_strdup (name);
 781     *linkname = NULL;
 782 
 783     if (size != NO_SIZE)
 784         s->st_size = size;
 785     if (date != NO_DATE)
 786     {
 787         s->st_mtime = date;
 788         /* Use resulting time value */
 789         s->st_atime = s->st_ctime = s->st_mtime;
 790     }
 791     switch (type)
 792     {
 793     case DIRECTORY:
 794         s->st_mode = S_IFDIR;
 795         break;
 796     case SYMLINK:
 797         s->st_mode = S_IFLNK;
 798         break;
 799     case NORMAL:
 800         s->st_mode = S_IFREG;
 801         break;
 802     default:
 803         g_assert_not_reached ();
 804     }
 805     if (perms != -1)
 806         s->st_mode |= perms;    /* FIXME */
 807     if (owner != NULL)
 808         s->st_uid = ftpfs_get_uid (owner);
 809     if (group != NULL)
 810         s->st_uid = ftpfs_get_gid (group);
 811 
 812     return TRUE;
 813 }
 814 
 815 /* --------------------------------------------------------------------------------------------- */
 816 
 817 /*
 818    ASUSER          8192 04/26/05 13:54:16 *DIR       dir/
 819    ASUSER          8192 04/26/05 13:57:34 *DIR       dir1/
 820    ASUSER        365255 02/28/01 15:41:40 *STMF      readme.txt
 821    ASUSER       8489625 03/18/03 09:37:00 *STMF      saved.zip
 822    ASUSER        365255 02/28/01 15:41:40 *STMF      unist.old
 823  */
 824 
 825 static gboolean
 826 ftpfs_parse_long_list_AS400 (char *line, struct stat *s, char **filename, char **linkname, int *err)
     /* [previous][next][first][last][top][bottom][index][help]  */
 827 {
 828     char *t;
 829     char *user;
 830     long long size;
 831     int month, day, year, hour, minute, second;
 832     struct tm tms;
 833     time_t mtime;
 834     mode_t type;
 835     char *slash;
 836 
 837     t = FIRST_TOKEN;
 838     if (t == NULL)
 839         ERR2;
 840     user = t;
 841 
 842     t = NEXT_TOKEN;
 843     if (t == NULL)
 844         ERR2;
 845     if (sscanf (t, "%lld", &size) != 1)
 846         ERR2;
 847 
 848     t = NEXT_TOKEN;
 849     if (t == NULL)
 850         ERR2;
 851     if (sscanf (t, "%2d/%2d/%2d", &month, &day, &year) != 3)
 852         ERR2;
 853     if (year >= 70)
 854         year += 1900;
 855     else
 856         year += 2000;
 857 
 858     t = NEXT_TOKEN;
 859     if (t == NULL)
 860         ERR2;
 861     if (sscanf (t, "%2d:%2d:%2d", &hour, &minute, &second) != 3)
 862         ERR2;
 863 
 864     t = NEXT_TOKEN;
 865     if (t == NULL)
 866         ERR2;
 867 
 868     tms.tm_sec = second;        /* seconds after the minute [0, 61]  */
 869     tms.tm_min = minute;        /* minutes after the hour [0, 59] */
 870     tms.tm_hour = hour;         /* hour since midnight [0, 23] */
 871     tms.tm_mday = day;          /* day of the month [1, 31] */
 872     tms.tm_mon = month - 1;     /* months since January [0, 11] */
 873     tms.tm_year = year - 1900;  /* years since 1900 */
 874     tms.tm_isdst = -1;
 875     mtime = mktime (&tms);
 876 
 877     t = NEXT_TOKEN;
 878     if (t == NULL)
 879         ERR2;
 880     if (strcmp (t, "*DIR") == 0)
 881         type = S_IFDIR;
 882     else
 883         type = S_IFREG;
 884 
 885     t = strtok (NULL, "");
 886     if (t == NULL)
 887         ERR2;
 888     while (*t == ' ')
 889         t++;
 890     if (*t == '\0')
 891         ERR2;
 892 
 893     *linkname = NULL;
 894 
 895     slash = strchr (t, '/');
 896     if (slash != NULL)
 897     {
 898         if (slash == t)
 899             return FALSE;
 900 
 901         *slash = '\0';
 902         type = S_IFDIR;
 903         if (slash[1] != '\0')
 904         {
 905             *filename = g_strdup (t);
 906             s->st_mode = type;  /* FIXME */
 907             return TRUE;
 908         }
 909     }
 910 
 911     *filename = g_strdup (t);
 912     s->st_mode = type;
 913     s->st_size = (off_t) size;
 914     s->st_mtime = mtime;
 915     /* Use resulting time value */
 916     s->st_atime = s->st_ctime = s->st_mtime;
 917     s->st_uid = ftpfs_get_uid (user);
 918 
 919     return TRUE;
 920 }
 921 
 922 /* --------------------------------------------------------------------------------------------- */
 923 
 924 /*
 925    0          DIR  06-27-96  11:57  PROTOCOL
 926    169               11-29-94  09:20  SYSLEVEL.MPT
 927  */
 928 
 929 static gboolean
 930 ftpfs_parse_long_list_OS2 (char *line, struct stat *s, char **filename, char **linkname, int *err)
     /* [previous][next][first][last][top][bottom][index][help]  */
 931 {
 932     char *t;
 933     long long size;
 934     int month, day, year, hour, minute;
 935     struct tm tms;
 936 
 937     t = FIRST_TOKEN;
 938     if (t == NULL)
 939         ERR2;
 940 
 941     if (sscanf (t, "%lld", &size) != 1)
 942         ERR2;
 943     s->st_size = (off_t) size;
 944 
 945     t = NEXT_TOKEN;
 946     if (t == NULL)
 947         ERR2;
 948     s->st_mode = S_IFREG;
 949     if (strcmp (t, "DIR") == 0)
 950     {
 951         s->st_mode = S_IFDIR;
 952         t = NEXT_TOKEN;
 953 
 954         if (t == NULL)
 955             ERR2;
 956     }
 957 
 958     if (sscanf (t, "%2d-%2d-%2d", &month, &day, &year) != 3)
 959         ERR2;
 960     if (year >= 70)
 961         year += 1900;
 962     else
 963         year += 2000;
 964 
 965     t = NEXT_TOKEN;
 966     if (t == NULL)
 967         ERR2;
 968     if (sscanf (t, "%2d:%2d", &hour, &minute) != 3)
 969         ERR2;
 970 
 971     tms.tm_sec = 30;            /* seconds after the minute [0, 61]  */
 972     tms.tm_min = minute;        /* minutes after the hour [0, 59] */
 973     tms.tm_hour = hour;         /* hour since midnight [0, 23] */
 974     tms.tm_mday = day;          /* day of the month [1, 31] */
 975     tms.tm_mon = month - 1;     /* months since January [0, 11] */
 976     tms.tm_year = year - 1900;  /* years since 1900 */
 977     tms.tm_isdst = -1;
 978     s->st_mtime = mktime (&tms);
 979     /* Use resulting time value */
 980     s->st_atime = s->st_ctime = s->st_mtime;
 981 
 982     t = strtok (NULL, "");
 983     if (t == NULL)
 984         ERR2;
 985     while (*t == ' ')
 986         t++;
 987     if (*t == '\0')
 988         ERR2;
 989     *filename = g_strdup (t);
 990     *linkname = NULL;
 991 
 992     return TRUE;
 993 }
 994 
 995 /* --------------------------------------------------------------------------------------------- */
 996 
 997 static gboolean
 998 ftpfs_parse_long_list_MacWebStar (char *line, struct stat *s, char **filename,
     /* [previous][next][first][last][top][bottom][index][help]  */
 999                                   char **linkname, int *err)
1000 {
1001     char *t;
1002     mode_t type, mode;
1003     struct tm date;
1004     const char *day_of_month;
1005     char *name;
1006 
1007     t = FIRST_TOKEN;
1008     if (t == NULL)
1009         ERR2;
1010 
1011     if (!vfs_parse_filetype (t, NULL, &type))
1012         ERR2;
1013 
1014     s->st_mode = type;
1015 
1016     if (!vfs_parse_fileperms (t + 1, NULL, &mode))
1017         ERR2;
1018     /* permissions are meaningless here. */
1019 
1020     /* "folder" or 0 */
1021     t = NEXT_TOKEN;
1022     if (t == NULL)
1023         ERR2;
1024 
1025     if (strcmp (t, "folder") != 0)
1026     {
1027         long long size;
1028 
1029         /* size? */
1030         t = NEXT_TOKEN;
1031         if (t == NULL)
1032             ERR2;
1033         /* size */
1034         t = NEXT_TOKEN;
1035         if (t == NULL)
1036             ERR2;
1037         if (!isdigit ((unsigned char) *t))
1038             ERR2;
1039 
1040         if (sscanf (t, "%lld", &size) == 1)
1041             s->st_size = (off_t) size;
1042     }
1043     else
1044     {
1045         /* ?? */
1046         t = NEXT_TOKEN;
1047         if (t == NULL)
1048             ERR2;
1049     }
1050 
1051     /* month */
1052     t = NEXT_TOKEN;
1053     if (t == NULL)
1054         ERR2;
1055 
1056     memset (&date, 0, sizeof (date));
1057 
1058     if (!vfs_parse_month (t, &date))
1059         ERR2;
1060 
1061     day_of_month = NEXT_TOKEN;
1062     if (day_of_month == NULL)
1063         ERR2;
1064 
1065     date.tm_mday = atoi (day_of_month);
1066 
1067     /* time or year */
1068     t = NEXT_TOKEN;
1069     if (t == NULL)
1070         ERR2;
1071     if (!parse_year_or_time (t, &date.tm_year, &date.tm_hour, &date.tm_min))
1072         ERR2;
1073 
1074     date.tm_isdst = -1;
1075     date.tm_sec = 30;
1076     if (date.tm_year == -1)
1077         date.tm_year = guess_year (date.tm_mon, date.tm_mday, date.tm_hour, date.tm_min) - 1900;
1078     else
1079         date.tm_hour = 12;
1080 
1081     s->st_mtime = mktime (&date);
1082     /* Use resulting time value */
1083     s->st_atime = s->st_ctime = s->st_mtime;
1084 
1085     name = strtok (NULL, "");
1086     if (name == NULL)
1087         ERR2;
1088 
1089     /* no symlinks on Mac, but anyway. */
1090     if (!S_ISLNK (s->st_mode))
1091         *linkname = NULL;
1092     else
1093     {
1094         char *arrow;
1095 
1096         for (arrow = name; (arrow = strstr (arrow, " -> ")) != NULL; arrow++)
1097             if (arrow != name && arrow[4] != '\0')
1098             {
1099                 *arrow = '\0';
1100                 *linkname = g_strdup (arrow + 4);
1101                 break;
1102             }
1103     }
1104 
1105     *filename = g_strdup (name);
1106 
1107     return TRUE;
1108 }
1109 
1110 /* --------------------------------------------------------------------------------------------- */
1111 /*** public functions ****************************************************************************/
1112 /* --------------------------------------------------------------------------------------------- */
1113 
1114 GSList *
1115 ftpfs_parse_long_list (struct vfs_class * me, struct vfs_s_inode * dir, GSList * buf, int *err_ret)
     /* [previous][next][first][last][top][bottom][index][help]  */
1116 {
1117     int err[number_of_parsers];
1118     GSList *set[number_of_parsers];     /* arrays of struct vfs_s_entry */
1119     size_t i;
1120     GSList *bufp;
1121     ftpfs_line_parser guessed_parser = NULL;
1122     GSList **the_set = NULL;
1123     int *the_err = NULL;
1124     int *best_err1 = &err[0];
1125     int *best_err2 = &err[1];
1126 
1127     ftpfs_init_time ();
1128 
1129     if (err_ret != NULL)
1130         *err_ret = 0;
1131 
1132     memset (&err, 0, sizeof (err));
1133     memset (&set, 0, sizeof (set));
1134 
1135     for (bufp = buf; bufp != NULL; bufp = g_slist_next (bufp))
1136     {
1137         char *b = (char *) bufp->data;
1138         size_t blen;
1139 
1140         blen = strlen (b);
1141 
1142         if (b[blen - 1] == '\r')
1143         {
1144             b[blen - 1] = '\0';
1145             blen--;
1146         }
1147 
1148         if (blen == 0)
1149             continue;
1150 
1151         if (guessed_parser == NULL)
1152         {
1153             for (i = 0; i < number_of_parsers; i++)
1154             {
1155                 struct vfs_s_entry *info;
1156                 gboolean ok;
1157                 char *tmp_line;
1158                 int nlink;
1159 
1160                 /* parser can clobber the line - work on a copy */
1161                 tmp_line = g_strndup (b, blen);
1162 
1163                 info = vfs_s_generate_entry (me, NULL, dir, 0);
1164                 nlink = info->ino->st.st_nlink;
1165                 ok = (*line_parsers[i]) (tmp_line, &info->ino->st, &info->name,
1166                                          &info->ino->linkname, &err[i]);
1167                 if (ok && strchr (info->name, '/') == NULL)
1168                 {
1169                     info->ino->st.st_nlink = nlink;     /* Ouch, we need to preserve our counts :-( */
1170                     set[i] = g_slist_prepend (set[i], info);
1171                 }
1172                 else
1173                     vfs_s_free_entry (me, info);
1174 
1175                 g_free (tmp_line);
1176 
1177                 if (*best_err1 > err[i])
1178                     best_err1 = &err[i];
1179                 if (*best_err2 > err[i] && best_err1 != &err[i])
1180                     best_err2 = &err[i];
1181 
1182                 if (*best_err1 > 16)
1183                     goto leave; /* too many errors with best parser. */
1184             }
1185 
1186             if (*best_err2 > (*best_err1 + 1) * 16)
1187             {
1188                 i = (size_t) (best_err1 - err);
1189                 guessed_parser = line_parsers[i];
1190                 the_set = &set[i];
1191                 the_err = &err[i];
1192             }
1193         }
1194         else
1195         {
1196             struct vfs_s_entry *info;
1197             gboolean ok;
1198             char *tmp_line;
1199             int nlink;
1200 
1201             /* parser can clobber the line - work on a copy */
1202             tmp_line = g_strndup (b, blen);
1203 
1204             info = vfs_s_generate_entry (me, NULL, dir, 0);
1205             nlink = info->ino->st.st_nlink;
1206             ok = guessed_parser (tmp_line, &info->ino->st, &info->name, &info->ino->linkname,
1207                                  the_err);
1208             if (ok && strchr (info->name, '/') == NULL)
1209             {
1210                 info->ino->st.st_nlink = nlink; /* Ouch, we need to preserve our counts :-( */
1211                 *the_set = g_slist_prepend (*the_set, info);
1212             }
1213             else
1214                 vfs_s_free_entry (me, info);
1215 
1216             g_free (tmp_line);
1217         }
1218     }
1219 
1220     if (the_set == NULL)
1221     {
1222         i = best_err1 - err;
1223         the_set = &set[i];
1224         the_err = &err[i];
1225     }
1226 
1227   leave:
1228     for (i = 0; i < number_of_parsers; i++)
1229         if (&set[i] != the_set)
1230         {
1231             for (bufp = set[i]; bufp != NULL; bufp = g_slist_next (bufp))
1232                 vfs_s_free_entry (me, VFS_ENTRY (bufp->data));
1233 
1234             g_slist_free (set[i]);
1235         }
1236 
1237     if (err_ret != NULL && the_err != NULL)
1238         *err_ret = *the_err;
1239 
1240     return the_set != NULL ? g_slist_reverse (*the_set) : NULL;
1241 }
1242 
1243 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */