/* ** Copyright 2000-2004 University of Illinois Board of Trustees ** Copyright 2000-2004 Mark D. Roth ** All rights reserved. ** ** list_parse_mlsd.c - MSLD-style FTP directory parsing code ** ** Mark D. Roth */ #include #include #include #include #include #include #ifdef STDC_HEADERS # include # include #endif #ifdef MAJOR_IN_MKDEV # include #else # ifdef MAJOR_IN_SYSMACROS # include # endif #endif /* needed for strcasecmp() on some platforms */ #include /* ** keep this list in sync with facts supported by _ftp_list_parse_mlsd() */ int _ftp_opts_mlsd(FTP *ftp) { int i; i = _ftp_opts(ftp, "MLST", "size;modify;type;perm;" "UNIX.mode;UNIX.owner;UNIX.group;UNIX.uid;UNIX.gid;"); /* ** if the server is broken enough to support FEAT but ** not OPTS, then we'll punt by ignoring the error ** and doing the best we can with whatever facts it provides */ if (i == -1 && errno == ENOSYS) i = 0; return i; } /* parse MLSD directories */ int _ftp_list_parse_mlsd(FTP *ftp, char *buf, file_info_t *fip) { char *factp, *valp, *nextp = buf; unsigned long ul; unsigned int ui; struct tm tt; #ifdef DEBUG printf("==> _ftp_list_parse_mlsd(ftp=0x%lx (%s), buf=0x%lx, " "fip=0x%lx)\n", ftp, ftp->ftp_host, buf, fip); #endif factp = strchr(buf, ' '); if (factp == NULL) { #ifdef DEBUG printf("<== _ftp_list_parse_mlsd(): no space character " "in entry - returning FLP_IGNORE\n"); #endif return FLP_IGNORE; } *factp++ = '\0'; strlcpy(fip->fi_filename, factp, sizeof(fip->fi_filename)); while ((factp = strsep(&nextp, ";")) != NULL) { if (*factp == '\0') continue; valp = factp; factp = strsep(&valp, "="); if (strcasecmp(factp, "size") == 0) { sscanf(valp, "%lu", &ul); fip->fi_stat.fs_size = (off_t)ul; continue; } if (strcasecmp(factp, "modify") == 0) { memset(&tt, 0, sizeof(tt)); sscanf(valp, "%4d%2d%2d%2d%2d%2d", &tt.tm_year, &tt.tm_mon, &tt.tm_mday, &tt.tm_hour, &tt.tm_min, &tt.tm_sec); tt.tm_year -= 1900; tt.tm_mon--; fip->fi_stat.fs_mtime = mktime(&tt); continue; } if (strcasecmp(factp, "type") == 0) { /* ** clear S_IFMT bits, in case the type was set before */ fip->fi_stat.fs_mode &= ~S_IFMT; if (strcasecmp(valp, "cdir") == 0) { strlcpy(fip->fi_filename, ".", sizeof(fip->fi_filename)); fip->fi_stat.fs_mode |= S_IFDIR; continue; } if (strcasecmp(valp, "pdir") == 0) { strlcpy(fip->fi_filename, "..", sizeof(fip->fi_filename)); fip->fi_stat.fs_mode |= S_IFDIR; continue; } if (strcasecmp(valp, "dir") == 0) { fip->fi_stat.fs_mode |= S_IFDIR; continue; } if (strcasecmp(valp, "file") == 0) { fip->fi_stat.fs_mode |= S_IFREG; continue; } if (strncasecmp(valp, "os.unix=", 8) == 0) { valp += 8; if (strncasecmp(valp, "slink:", 6) == 0) { fip->fi_stat.fs_mode |= S_IFLNK; valp += 6; strlcpy(fip->fi_linkto, valp, sizeof(fip->fi_linkto)); continue; } if (strncasecmp(valp, "chr-", 4) == 0) { fip->fi_stat.fs_mode |= S_IFCHR; /* FIXME: add fs_rdev field? */ continue; } if (strncasecmp(valp, "blk-", 4) == 0) { fip->fi_stat.fs_mode |= S_IFCHR; /* FIXME: add fs_rdev field? */ continue; } continue; } /* for unknown types, ignore the entire entry */ #ifdef DEBUG printf("<== _ftp_list_parse_mlsd(): " "unknown type \"%s\" - returning FLP_IGNORE\n", valp); #endif return FLP_IGNORE; } if (strcasecmp(factp, "perm") == 0) { /* ** if the mode bits are already set, don't ** overwrite them ** (this allows the UNIX.mode fact to take precedence) */ if (fip->fi_stat.fs_mode & ~S_IFMT != 0) continue; for (; *valp != '\0'; valp++) { switch (*valp) { case 'a': /* file: APPE */ case 'w': /* file: STOR */ case 'c': /* [cp]?dir: STOU, STOR */ fip->fi_stat.fs_mode |= 0222; break; case 'e': /* [cp]?dir: CWD */ fip->fi_stat.fs_mode |= 0111; break; case 'l': /* [cp]?dir: MLSD, LIST, NLST */ case 'r': /* file: RETR */ fip->fi_stat.fs_mode |= 0444; break; default: case 'd': /* any: DELE, RMD */ case 'f': /* any: RNFR */ case 'm': /* [cp]?dir: MKD (subdirs) */ case 'p': /* [cp]?dir: RMD (subdirs) */ break; } } continue; } if (strncasecmp(factp, "UNIX.", 5) == 0) { factp += 5; if (strcasecmp(factp, "mode") == 0) { /* clear any bits that may already be set */ fip->fi_stat.fs_mode &= S_IFMT; sscanf(valp, "%lo", &ui); fip->fi_stat.fs_mode |= (mode_t)ui; continue; } if (strcasecmp(factp, "owner") == 0) { strlcpy(fip->fi_stat.fs_username, valp, sizeof(fip->fi_stat.fs_username)); continue; } if (strcasecmp(factp, "group") == 0) { strlcpy(fip->fi_stat.fs_groupname, valp, sizeof(fip->fi_stat.fs_groupname)); continue; } if (strcasecmp(factp, "uid") == 0) { /* ** skip if already set ** (this allows the UNIX.owner fact ** to take precedence) */ if (fip->fi_stat.fs_username[0] != '\0') continue; strlcpy(fip->fi_stat.fs_username, valp, sizeof(fip->fi_stat.fs_username)); continue; } if (strcasecmp(factp, "gid") == 0) { /* ** skip if already set ** (this allows the UNIX.group fact ** to take precedence) */ if (fip->fi_stat.fs_groupname[0] != '\0') continue; strlcpy(fip->fi_stat.fs_groupname, valp, sizeof(fip->fi_stat.fs_groupname)); continue; } continue; } /* skip any other facts */ continue; } if (fip->fi_stat.fs_username[0] == '\0') strlcpy(fip->fi_stat.fs_username, "-1", sizeof(fip->fi_stat.fs_username)); if (fip->fi_stat.fs_groupname[0] == '\0') strlcpy(fip->fi_stat.fs_groupname, "-1", sizeof(fip->fi_stat.fs_groupname)); if (fip->fi_stat.fs_nlink == 0) fip->fi_stat.fs_nlink = 1; #ifdef DEBUG printf("<== _ftp_list_parse_mlsd(): success\n"); #endif return FLP_VALID; }