ftpd: add support for MDTM, I see clients often use it,

it may allow client-side caching.

function                                             old     new   delta
ftpd_main                                           2232    2306     +74
gmtime_r                                               -      19     +19
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 1/0 up/down: 93/0)               Total: 93 bytes
This commit is contained in:
Denis Vlasenko 2009-03-18 00:45:00 +00:00
parent 3a7a1eba2e
commit 1432cb4bd9

View File

@ -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 [<file or dir name, "." assumed if not given>]
* 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;
}
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");
}