ftp-srv-esm
Version:
Modern, extensible FTP server (daemon) for Node.js with ESM support. Based on ftp-srv.
66 lines (57 loc) • 2.49 kB
JavaScript
import getFileStat from '../../helpers/file-stat.js';
// http://cr.yp.to/ftp/list.html
// http://cr.yp.to/ftp/list/eplf.html
export default {
directive: 'LIST',
handler: function ({log, command} = {}) {
if (!this.fs) return this.reply(550, 'File system not instantiated');
if (!this.fs.get) return this.reply(402, 'Not supported by file system');
if (!this.fs.list) return this.reply(402, 'Not supported by file system');
// Parse command arguments: extract options and path
let path = '.';
let showHidden = false;
if (command.arg) {
// Split by spaces, filter out empty strings
const args = command.arg.split(/\s+/).filter(Boolean);
// Check for options
const options = args.filter(arg => arg.startsWith('-'));
showHidden = options.some(opt => opt.includes('a')); // -a or -al
// Find the path (non-option argument)
const nonOption = args.find(arg => !arg.startsWith('-'));
if (nonOption) path = nonOption;
}
return this.connector.waitForConnection()
.then(() => { this.commandSocket.pause(); })
.then(() => this.fs.get(path))
.then((stat) => stat.isDirectory() ? this.fs.list(path, { showHidden }) : [stat])
.then((files) => {
this.reply(150, `Accepted data connection, returning ${files.length} file(s)`);
if (!files) {
return this.reply({ raw: true, socket: this.connector.socket, useEmptyMessage: true});
}
Promise.try(() => files.map((file) => {
// Use connection-specific format if set via OPTS LIST, otherwise use server default
const fileFormat = this.listFormat || this?.server?.options?.list_format || 'ls';
return getFileStat(file, fileFormat);
}))
.then((files) => {
this.reply({}, {raw: true, message: files.join('\r\n'), socket: this.connector.socket});
})
})
.then(() => this.reply(226))
.catch((err) => {
if (err && err.name === 'TimeoutError') {
log.error(err);
return this.reply(425, 'No connection established');
}
log.error(err);
return this.reply(451, err.message || 'No directory');
})
.then(() => {
this.connector.end();
this.commandSocket.resume();
});
},
syntax: '{{cmd}} [<path>]',
description: 'Returns information of a file or directory if specified, else information of the current working directory is returned'
};