diff --git a/networking/ftpd.c b/networking/ftpd.c index f005d129c..b295ddf4f 100644 --- a/networking/ftpd.c +++ b/networking/ftpd.c @@ -7,7 +7,9 @@ * Licensed under GPLv2, see file LICENSE in this tarball for details. * * Only subset of FTP protocol is implemented but vast majority of clients - * should not have any problem. You have to run this daemon via inetd. + * should not have any problem. + * + * You have to run this daemon via inetd. */ #include "libbb.h" @@ -172,11 +174,11 @@ cmdio_write(uint32_t status_str, const char *str) char *response; int len; - /* FTP allegedly uses telnet protocol for command link. + /* FTP uses telnet protocol for command link. * In telnet, 0xff is an escape char, and needs to be escaped: */ response = escape_text((char *) &status_str, str, (0xff << 8) + '\r'); - /* ?! does FTP send embedded LFs as NULs? wow */ + /* FTP sends embedded LFs as NULs */ len = replace_char(response, '\n', '\0'); response[len++] = '\n'; /* tack on trailing '\n' */ @@ -315,6 +317,7 @@ handle_feat(unsigned status) cmdio_write_raw(" EPSV\r\n" " PASV\r\n" " REST STREAM\r\n" + " MDTM\r\n" " SIZE\r\n"); cmdio_write(status, " Ok"); } @@ -726,11 +729,43 @@ handle_stat_file(void) handle_dir_common(LONG_LISTING + USE_CTRL_CONN); } +/* This can be extended to handle MLST, as all info is available + * in struct stat for that: + * MLST file_name + * 250-Listing file_name + * type=file;size=4161;modify=19970214165800; /dir/dir/file_name + * 250 End + * Nano-doc: + * MLST [] + * Returned name should be either the same as requested, or fully qualified. + * If there was no parameter, return "" or (preferred) fully-qualified name. + * Returned "facts" (case is not important): + * size - size in octets + * modify - last modification time + * type - entry type (file,dir,OS.unix=block) + * (+ cdir and pdir types for MLSD) + * unique - unique id of file/directory (inode#) + * perm - + * a: can be appended to (APPE) + * d: can be deleted (RMD/DELE) + * f: can be renamed (RNFR) + * r: can be read (RETR) + * w: can be written (STOR) + * e: can CWD into this dir + * l: this dir can be listed (dir only!) + * c: can create files in this dir + * m: can create dirs in this dir (MKD) + * p: can delete files in this dir + * UNIX.mode - unix file mode + */ static void -handle_size(void) +handle_size_or_mdtm(int need_size) { struct stat statbuf; - char buf[sizeof(STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n") + sizeof(off_t)*3]; + struct tm broken_out; + char buf[(sizeof("NNN %"OFF_FMT"u\r\n") + sizeof(off_t) * 3) + | sizeof("NNN YYYYMMDDhhmmss\r\n") + ]; if (!G.ftp_arg || stat(G.ftp_arg, &statbuf) != 0 @@ -739,7 +774,18 @@ handle_size(void) WRITE_ERR(FTP_FILEFAIL); return; } - sprintf(buf, STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n", statbuf.st_size); + if (need_size) { + sprintf(buf, STR(FTP_STATFILE_OK)" %"OFF_FMT"u\r\n", statbuf.st_size); + } else { + gmtime_r(&statbuf.st_mtime, &broken_out); + sprintf(buf, STR(FTP_STATFILE_OK)" %04u%02u%02u%02u%02u%02u\r\n", + broken_out.tm_year + 1900, + broken_out.tm_mon, + broken_out.tm_mday, + broken_out.tm_hour, + broken_out.tm_min, + broken_out.tm_sec); + } cmdio_write_raw(buf); } @@ -936,6 +982,7 @@ enum { const_FEAT = mk_const4('F', 'E', 'A', 'T'), const_HELP = mk_const4('H', 'E', 'L', 'P'), const_LIST = mk_const4('L', 'I', 'S', 'T'), + const_MDTM = mk_const4('M', 'D', 'T', 'M'), const_MKD = mk_const3('M', 'K', 'D'), const_MODE = mk_const4('M', 'O', 'D', 'E'), const_NLST = mk_const4('N', 'L', 'S', 'T'), @@ -1112,7 +1159,12 @@ int ftpd_main(int argc, char **argv) return 0; } else if (cmdval == const_USER) - WRITE_OK(FTP_GIVEPWORD); + /* This would mean "ok, now give me PASS". */ + /*WRITE_OK(FTP_GIVEPWORD);*/ + /* vsftpd can be configured to not require that, + * and this also saves one roundtrip: + */ + WRITE_OK(FTP_LOGINOK); else if (cmdval == const_PASS) WRITE_OK(FTP_LOGINOK); else if (cmdval == const_NOOP) @@ -1134,14 +1186,20 @@ int ftpd_main(int argc, char **argv) else if (cmdval == const_CDUP) /* cd .. */ handle_cdup(); /* HELP is nearly useless, but we can reuse FEAT for it */ + /* lftp uses FEAT */ else if (cmdval == const_HELP || cmdval == const_FEAT) - handle_feat(cmdval == const_HELP ? STRNUM32(FTP_HELP) : STRNUM32(FTP_STATOK)); + handle_feat(cmdval == const_HELP + ? STRNUM32(FTP_HELP) + : STRNUM32(FTP_STATOK) + ); else if (cmdval == const_LIST) /* ls -l */ handle_list(); else if (cmdval == const_NLST) /* "name list", bare ls */ handle_nlst(); - else if (cmdval == const_SIZE) - handle_size(); + /* SIZE is crucial for wget's download indicator etc */ + /* Mozilla, lftp use MDTM (presumably for caching) */ + else if (cmdval == const_SIZE || cmdval == const_MDTM) + handle_size_or_mdtm(cmdval == const_SIZE); else if (cmdval == const_STAT) { if (G.ftp_arg == NULL) handle_stat(); @@ -1194,7 +1252,7 @@ int ftpd_main(int argc, char **argv) else { /* Which unsupported commands were seen in the wild? * (doesn't necessarily mean "we must support them") - * lftp 3.6.3: MDTM - works fine without it anyway + * foo 1.2.3: XXXX - comment */ cmdio_write_raw(STR(FTP_BADCMD)" Unknown command\r\n"); }