* 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"
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' */
cmdio_write_raw(" EPSV\r\n"
" PASV\r\n"
" REST STREAM\r\n"
+ " MDTM\r\n"
" SIZE\r\n");
cmdio_write(status, " Ok");
}
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
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);
}
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'),
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)
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();
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");
}