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
ftpfs_get_uid(const char * s)124 ftpfs_get_uid (const char *s)
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
ftpfs_get_gid(const char * s)139 ftpfs_get_gid (const char *s)
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
ftpfs_init_time(void)154 ftpfs_init_time (void)
155 {
156 time (&rawnow);
157 now = *localtime (&rawnow);
158 }
159
160 /* --------------------------------------------------------------------------------------------- */
161
162 static int
guess_year(int month,int day,int hour,int minute)163 guess_year (int month, int day, int hour, int minute)
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
parse_year_or_time(const char * year_or_time,int * year,int * hour,int * minute)181 parse_year_or_time (const char *year_or_time, int *year, int *hour, int *minute)
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
mktime_from_utc(const struct tm * t)209 mktime_from_utc (const struct tm *t)
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
ftpfs_convert_date(const char * s)233 ftpfs_convert_date (const char *s)
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
parse_ls_line(char * line,struct stat * s,char ** filename,char ** linkname)282 parse_ls_line (char *line, struct stat *s, char **filename, char **linkname)
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
ftpfs_parse_long_list_UNIX(char * line,struct stat * s,char ** filename,char ** linkname,int * err)424 ftpfs_parse_long_list_UNIX (char *line, struct stat *s, char **filename, char **linkname, int *err)
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
ftpfs_parse_long_list_NT(char * line,struct stat * s,char ** filename,char ** linkname,int * err)457 ftpfs_parse_long_list_NT (char *line, struct stat *s, char **filename, char **linkname, int *err)
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
ftpfs_parse_long_list_EPLF(char * line,struct stat * s,char ** filename,char ** linkname,int * err)549 ftpfs_parse_long_list_EPLF (char *line, struct stat *s, char **filename, char **linkname, int *err)
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
ftpfs_parse_long_list_MLSD(char * line,struct stat * s,char ** filename,char ** linkname,int * err)661 ftpfs_parse_long_list_MLSD (char *line, struct stat *s, char **filename, char **linkname, int *err)
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
ftpfs_parse_long_list_AS400(char * line,struct stat * s,char ** filename,char ** linkname,int * err)826 ftpfs_parse_long_list_AS400 (char *line, struct stat *s, char **filename, char **linkname, int *err)
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
ftpfs_parse_long_list_OS2(char * line,struct stat * s,char ** filename,char ** linkname,int * err)930 ftpfs_parse_long_list_OS2 (char *line, struct stat *s, char **filename, char **linkname, int *err)
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
ftpfs_parse_long_list_MacWebStar(char * line,struct stat * s,char ** filename,char ** linkname,int * err)998 ftpfs_parse_long_list_MacWebStar (char *line, struct stat *s, char **filename,
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 *
ftpfs_parse_long_list(struct vfs_class * me,struct vfs_s_inode * dir,GSList * buf,int * err_ret)1115 ftpfs_parse_long_list (struct vfs_class * me, struct vfs_s_inode * dir, GSList * buf, int *err_ret)
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 /* --------------------------------------------------------------------------------------------- */
1244