Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb24c48669 | ||
|
|
a83c391d58 |
@@ -114,7 +114,7 @@ __Allowable values:__
|
||||
- Only one argument is passed in: a node [file stat](https://nodejs.org/api/fs.html#fs_class_fs_stats) object with additional file `name` parameter
|
||||
|
||||
##### `log`
|
||||
A [bunyan logger](https://github.com/trentm/node-bunyan) instance. Created by default.
|
||||
A [signale logger](https://github.com/klauscfhq/signale) instance. Created by default.
|
||||
|
||||
## CLI
|
||||
|
||||
|
||||
@@ -53,10 +53,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"bluebird": "^3.5.1",
|
||||
"bunyan": "^1.8.12",
|
||||
"ip": "^1.1.5",
|
||||
"lodash": "^4.17.10",
|
||||
"moment": "^2.22.1",
|
||||
"signale": "^1.0.1",
|
||||
"uuid": "^3.2.1",
|
||||
"yargs": "^11.0.0"
|
||||
},
|
||||
|
||||
@@ -36,19 +36,16 @@ class FtpCommands {
|
||||
const logCommand = _.clone(command);
|
||||
if (logCommand.directive === 'PASS') logCommand.arg = '********';
|
||||
|
||||
const log = this.connection.log.child({directive: command.directive});
|
||||
log.trace({command: logCommand}, 'Handle command');
|
||||
|
||||
if (!REGISTRY.hasOwnProperty(command.directive)) {
|
||||
return this.connection.reply(402, 'Command not allowed');
|
||||
}
|
||||
|
||||
if (_.includes(this.blacklist, command.directive)) {
|
||||
return this.connection.reply(502, 'Command blacklisted');
|
||||
return this.connection.reply(502, 'Command on blacklist');
|
||||
}
|
||||
|
||||
if (this.whitelist.length > 0 && !_.includes(this.whitelist, command.directive)) {
|
||||
return this.connection.reply(502, 'Command not whitelisted');
|
||||
return this.connection.reply(502, 'Command not on whitelist');
|
||||
}
|
||||
|
||||
const commandRegister = REGISTRY[command.directive];
|
||||
@@ -61,8 +58,7 @@ class FtpCommands {
|
||||
return this.connection.reply(502, 'Handler not set on command');
|
||||
}
|
||||
|
||||
const handler = commandRegister.handler.bind(this.connection);
|
||||
return Promise.resolve(handler({log, command, previous_command: this.previousCommand}))
|
||||
return Promise.try(() => commandRegister.handler.call(this, this.connection, command, this.previousCommand))
|
||||
.finally(() => {
|
||||
this.previousCommand = _.clone(command);
|
||||
});
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
module.exports = {
|
||||
directive: 'ABOR',
|
||||
handler: function () {
|
||||
return this.connector.waitForConnection()
|
||||
handler: function (connection) {
|
||||
return connection.connector.waitForConnection()
|
||||
.then(socket => {
|
||||
return this.reply(426, {socket})
|
||||
.then(() => this.connector.end())
|
||||
.then(() => this.reply(226));
|
||||
return connection.reply(426, {socket})
|
||||
.then(() => connection.connector.end())
|
||||
.then(() => connection.reply(226));
|
||||
})
|
||||
.catch(() => this.reply(225));
|
||||
.catch(err => {
|
||||
connection.emit('error', err);
|
||||
return connection.reply(225);
|
||||
});
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Abort an active file transfer'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module.exports = {
|
||||
directive: 'ALLO',
|
||||
handler: function () {
|
||||
return this.reply(202);
|
||||
handler: function (connection) {
|
||||
return connection.reply(202);
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Allocate sufficient disk space to receive a file',
|
||||
|
||||
@@ -2,8 +2,8 @@ const stor = require('./stor').handler;
|
||||
|
||||
module.exports = {
|
||||
directive: 'APPE',
|
||||
handler: function (args) {
|
||||
return stor.call(this, args);
|
||||
handler: function () {
|
||||
return stor.call(this, ...arguments);
|
||||
},
|
||||
syntax: '{{cmd}} <path>',
|
||||
description: 'Append to a file'
|
||||
|
||||
@@ -3,12 +3,12 @@ const tls = require('tls');
|
||||
|
||||
module.exports = {
|
||||
directive: 'AUTH',
|
||||
handler: function ({command} = {}) {
|
||||
handler: function (connection, command) {
|
||||
const method = _.upperCase(command.arg);
|
||||
|
||||
switch (method) {
|
||||
case 'TLS': return handleTLS.call(this);
|
||||
default: return this.reply(504);
|
||||
case 'TLS': return handleTLS.call(this, connection);
|
||||
default: return connection.reply(504);
|
||||
}
|
||||
},
|
||||
syntax: '{{cmd}} <type>',
|
||||
@@ -19,24 +19,24 @@ module.exports = {
|
||||
}
|
||||
};
|
||||
|
||||
function handleTLS() {
|
||||
if (!this.server._tls) return this.reply(502);
|
||||
if (this.secure) return this.reply(202);
|
||||
function handleTLS(connection) {
|
||||
if (!connection.server._tls) return connection.reply(502);
|
||||
if (connection.secure) return connection.reply(202);
|
||||
|
||||
return this.reply(234)
|
||||
return connection.reply(234)
|
||||
.then(() => {
|
||||
const secureContext = tls.createSecureContext(this.server._tls);
|
||||
const secureSocket = new tls.TLSSocket(this.commandSocket, {
|
||||
const secureContext = tls.createSecureContext(connection.server._tls);
|
||||
const secureSocket = new tls.TLSSocket(connection.commandSocket, {
|
||||
isServer: true,
|
||||
secureContext
|
||||
});
|
||||
['data', 'timeout', 'end', 'close', 'drain', 'error'].forEach(event => {
|
||||
function forwardEvent() {
|
||||
this.emit.apply(this, arguments);
|
||||
connection.emit.apply(this, arguments);
|
||||
}
|
||||
secureSocket.on(event, forwardEvent.bind(this.commandSocket, event));
|
||||
secureSocket.on(event, forwardEvent.bind(connection.commandSocket, event));
|
||||
});
|
||||
this.commandSocket = secureSocket;
|
||||
this.secure = true;
|
||||
connection.commandSocket = secureSocket;
|
||||
connection.secure = true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ const cwd = require('./cwd').handler;
|
||||
|
||||
module.exports = {
|
||||
directive: ['CDUP', 'XCUP'],
|
||||
handler: function (args) {
|
||||
args.command.arg = '..';
|
||||
return cwd.call(this, args);
|
||||
handler: function (connection, command, ...args) {
|
||||
command.arg = '..';
|
||||
return cwd.call(this, connection, command, ...args);
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Change to Parent Directory'
|
||||
|
||||
@@ -3,18 +3,18 @@ const escapePath = require('../../helpers/escape-path');
|
||||
|
||||
module.exports = {
|
||||
directive: ['CWD', 'XCWD'],
|
||||
handler: function ({log, command} = {}) {
|
||||
if (!this.fs) return this.reply(550, 'File system not instantiated');
|
||||
if (!this.fs.chdir) return this.reply(402, 'Not supported by file system');
|
||||
handler: function (connection, command) {
|
||||
if (!connection.fs) return connection.reply(550, 'File system not instantiated');
|
||||
if (!connection.fs.chdir) return connection.reply(402, 'Not supported by file system');
|
||||
|
||||
return Promise.resolve(this.fs.chdir(command.arg))
|
||||
return Promise.resolve(connection.fs.chdir(command.arg))
|
||||
.then(cwd => {
|
||||
const path = cwd ? `"${escapePath(cwd)}"` : undefined;
|
||||
return this.reply(250, path);
|
||||
return connection.reply(250, path);
|
||||
})
|
||||
.catch(err => {
|
||||
log.error(err);
|
||||
return this.reply(550, err.message);
|
||||
connection.emit('error', err);
|
||||
return connection.reply(550, err.message);
|
||||
});
|
||||
},
|
||||
syntax: '{{cmd}} <path>',
|
||||
|
||||
@@ -2,17 +2,17 @@ const Promise = require('bluebird');
|
||||
|
||||
module.exports = {
|
||||
directive: 'DELE',
|
||||
handler: function ({log, command} = {}) {
|
||||
if (!this.fs) return this.reply(550, 'File system not instantiated');
|
||||
if (!this.fs.delete) return this.reply(402, 'Not supported by file system');
|
||||
handler: function (connection, command) {
|
||||
if (!connection.fs) return connection.reply(550, 'File system not instantiated');
|
||||
if (!connection.fs.delete) return connection.reply(402, 'Not supported by file system');
|
||||
|
||||
return Promise.resolve(this.fs.delete(command.arg))
|
||||
return Promise.resolve(connection.fs.delete(command.arg))
|
||||
.then(() => {
|
||||
return this.reply(250);
|
||||
return connection.reply(250);
|
||||
})
|
||||
.catch(err => {
|
||||
log.error(err);
|
||||
return this.reply(550, err.message);
|
||||
connection.emit('error', err);
|
||||
return connection.reply(550, err.message);
|
||||
});
|
||||
},
|
||||
syntax: '{{cmd}} <path>',
|
||||
|
||||
@@ -8,14 +8,14 @@ const FAMILY = {
|
||||
|
||||
module.exports = {
|
||||
directive: 'EPRT',
|
||||
handler: function ({command} = {}) {
|
||||
handler: function (connection, command) {
|
||||
const [, protocol, ip, port] = _.chain(command).get('arg', '').split('|').value();
|
||||
const family = FAMILY[protocol];
|
||||
if (!family) return this.reply(504, 'Unknown network protocol');
|
||||
if (!family) return connection.reply(504, 'Unknown network protocol');
|
||||
|
||||
this.connector = new ActiveConnector(this);
|
||||
return this.connector.setupConnection(ip, port, family)
|
||||
.then(() => this.reply(200));
|
||||
connection.connector = new ActiveConnector(connection);
|
||||
return connection.connector.setupConnection(ip, port, family)
|
||||
.then(() => connection.reply(200));
|
||||
},
|
||||
syntax: '{{cmd}} |<protocol>|<address>|<port>|',
|
||||
description: 'Specifies an address and port to which the server should connect'
|
||||
|
||||
@@ -2,13 +2,13 @@ const PassiveConnector = require('../../connector/passive');
|
||||
|
||||
module.exports = {
|
||||
directive: 'EPSV',
|
||||
handler: function () {
|
||||
this.connector = new PassiveConnector(this);
|
||||
return this.connector.setupServer()
|
||||
handler: function (connection) {
|
||||
connection.connector = new PassiveConnector(connection);
|
||||
return connection.connector.setupServer()
|
||||
.then(server => {
|
||||
const {port} = server.address();
|
||||
|
||||
return this.reply(229, `EPSV OK (|||${port}|)`);
|
||||
return connection.reply(229, `EPSV OK (|||${port}|)`);
|
||||
});
|
||||
},
|
||||
syntax: '{{cmd}} [<protocol>]',
|
||||
|
||||
@@ -2,7 +2,7 @@ const _ = require('lodash');
|
||||
|
||||
module.exports = {
|
||||
directive: 'FEAT',
|
||||
handler: function () {
|
||||
handler: function (connection) {
|
||||
const registry = require('../registry');
|
||||
const features = Object.keys(registry)
|
||||
.reduce((feats, cmd) => {
|
||||
@@ -16,8 +16,8 @@ module.exports = {
|
||||
raw: true
|
||||
}));
|
||||
return features.length
|
||||
? this.reply(211, 'Extensions supported', ...features, 'End')
|
||||
: this.reply(211, 'No features');
|
||||
? connection.reply(211, 'Extensions supported', ...features, 'End')
|
||||
: connection.reply(211, 'No features');
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Get the feature list implemented by the server',
|
||||
|
||||
@@ -2,18 +2,18 @@ const _ = require('lodash');
|
||||
|
||||
module.exports = {
|
||||
directive: 'HELP',
|
||||
handler: function ({command} = {}) {
|
||||
handler: function (connection, command) {
|
||||
const registry = require('../registry');
|
||||
const directive = _.upperCase(command.arg);
|
||||
if (directive) {
|
||||
if (!registry.hasOwnProperty(directive)) return this.reply(502, `Unknown command ${directive}.`);
|
||||
if (!registry.hasOwnProperty(directive)) return connection.reply(502, `Unknown command ${directive}.`);
|
||||
|
||||
const {syntax, description} = registry[directive];
|
||||
const reply = _.concat([syntax.replace('{{cmd}}', directive), description]);
|
||||
return this.reply(214, ...reply);
|
||||
return connection.reply(214, ...reply);
|
||||
} else {
|
||||
const supportedCommands = _.chunk(Object.keys(registry), 5).map(chunk => chunk.join('\t'));
|
||||
return this.reply(211, 'Supported commands:', ...supportedCommands, 'Use "HELP [command]" for syntax help.');
|
||||
return connection.reply(211, 'Supported commands:', ...supportedCommands, 'Use "HELP [command]" for syntax help.');
|
||||
}
|
||||
},
|
||||
syntax: '{{cmd}} [<command>]',
|
||||
|
||||
@@ -6,22 +6,22 @@ const getFileStat = require('../../helpers/file-stat');
|
||||
// http://cr.yp.to/ftp/list/eplf.html
|
||||
module.exports = {
|
||||
directive: 'LIST',
|
||||
handler: function ({log, command} = {}) {
|
||||
if (!this.fs) return this.reply(550, 'File system not instantiated');
|
||||
handler: function (connection, command) {
|
||||
if (!connection.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');
|
||||
|
||||
const simple = command.directive === 'NLST';
|
||||
|
||||
const path = command.arg || '.';
|
||||
return this.connector.waitForConnection()
|
||||
.tap(() => this.commandSocket.pause())
|
||||
.then(() => Promise.resolve(this.fs.get(path)))
|
||||
.then(stat => stat.isDirectory() ? Promise.resolve(this.fs.list(path)) : [stat])
|
||||
return connection.connector.waitForConnection()
|
||||
.tap(() => connection.commandSocket.pause())
|
||||
.then(() => Promise.resolve(connection.fs.get(path)))
|
||||
.then(stat => stat.isDirectory() ? Promise.resolve(connection.fs.list(path)) : [stat])
|
||||
.then(files => {
|
||||
const getFileMessage = file => {
|
||||
if (simple) return file.name;
|
||||
return getFileStat(file, _.get(this, 'server.options.file_format', 'ls'));
|
||||
return getFileStat(file, _.get(connection, 'server.options.file_format', 'ls'));
|
||||
};
|
||||
|
||||
const fileList = files.map(file => {
|
||||
@@ -29,26 +29,26 @@ module.exports = {
|
||||
return {
|
||||
raw: true,
|
||||
message,
|
||||
socket: this.connector.socket
|
||||
socket: connection.connector.socket
|
||||
};
|
||||
});
|
||||
return this.reply(150)
|
||||
return connection.reply(150)
|
||||
.then(() => {
|
||||
if (fileList.length) return this.reply({}, ...fileList);
|
||||
if (fileList.length) return connection.reply({}, ...fileList);
|
||||
});
|
||||
})
|
||||
.then(() => this.reply(226))
|
||||
.then(() => connection.reply(226))
|
||||
.catch(Promise.TimeoutError, err => {
|
||||
log.error(err);
|
||||
return this.reply(425, 'No connection established');
|
||||
connection.emit('error', err);
|
||||
return connection.reply(425, 'No connection established');
|
||||
})
|
||||
.catch(err => {
|
||||
log.error(err);
|
||||
return this.reply(451, err.message || 'No directory');
|
||||
connection.emit('error', err);
|
||||
return connection.reply(451, err.message || 'No directory');
|
||||
})
|
||||
.finally(() => {
|
||||
this.connector.end();
|
||||
this.commandSocket.resume();
|
||||
connection.connector.end();
|
||||
connection.commandSocket.resume();
|
||||
});
|
||||
},
|
||||
syntax: '{{cmd}} [<path>]',
|
||||
|
||||
@@ -3,18 +3,18 @@ const moment = require('moment');
|
||||
|
||||
module.exports = {
|
||||
directive: 'MDTM',
|
||||
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');
|
||||
handler: function (connection, command) {
|
||||
if (!connection.fs) return connection.reply(550, 'File system not instantiated');
|
||||
if (!connection.fs.get) return connection.reply(402, 'Not supported by file system');
|
||||
|
||||
return Promise.resolve(this.fs.get(command.arg))
|
||||
return Promise.resolve(connection.fs.get(command.arg))
|
||||
.then(fileStat => {
|
||||
const modificationTime = moment.utc(fileStat.mtime).format('YYYYMMDDHHmmss.SSS');
|
||||
return this.reply(213, modificationTime);
|
||||
return connection.reply(213, modificationTime);
|
||||
})
|
||||
.catch(err => {
|
||||
log.error(err);
|
||||
return this.reply(550, err.message);
|
||||
connection.emit('error', err);
|
||||
return connection.reply(550, err.message);
|
||||
});
|
||||
},
|
||||
syntax: '{{cmd}} <path>',
|
||||
|
||||
@@ -3,18 +3,18 @@ const escapePath = require('../../helpers/escape-path');
|
||||
|
||||
module.exports = {
|
||||
directive: ['MKD', 'XMKD'],
|
||||
handler: function ({log, command} = {}) {
|
||||
if (!this.fs) return this.reply(550, 'File system not instantiated');
|
||||
if (!this.fs.mkdir) return this.reply(402, 'Not supported by file system');
|
||||
handler: function (connection, command) {
|
||||
if (!connection.fs) return connection.reply(550, 'File system not instantiated');
|
||||
if (!connection.fs.mkdir) return connection.reply(402, 'Not supported by file system');
|
||||
|
||||
return Promise.resolve(this.fs.mkdir(command.arg))
|
||||
return Promise.resolve(connection.fs.mkdir(command.arg))
|
||||
.then(dir => {
|
||||
const path = dir ? `"${escapePath(dir)}"` : undefined;
|
||||
return this.reply(257, path);
|
||||
return connection.reply(257, path);
|
||||
})
|
||||
.catch(err => {
|
||||
log.error(err);
|
||||
return this.reply(550, err.message);
|
||||
connection.emit('error', err);
|
||||
return connection.reply(550, err.message);
|
||||
});
|
||||
},
|
||||
syntax: '{{cmd}} <path>',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module.exports = {
|
||||
directive: 'MODE',
|
||||
handler: function ({command} = {}) {
|
||||
return this.reply(/^S$/i.test(command.arg) ? 200 : 504);
|
||||
handler: function (connection, command) {
|
||||
return connection.reply(/^S$/i.test(command.arg) ? 200 : 504);
|
||||
},
|
||||
syntax: '{{cmd}} <mode>',
|
||||
description: 'Sets the transfer mode (Stream, Block, or Compressed)',
|
||||
|
||||
@@ -2,8 +2,8 @@ const list = require('./list').handler;
|
||||
|
||||
module.exports = {
|
||||
directive: 'NLST',
|
||||
handler: function (args) {
|
||||
return list.call(this, args);
|
||||
handler: function () {
|
||||
return list.call(this, ...arguments);
|
||||
},
|
||||
syntax: '{{cmd}} [<path>]',
|
||||
description: 'Returns a list of file names in a specified directory'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module.exports = {
|
||||
directive: 'NOOP',
|
||||
handler: function () {
|
||||
return this.reply(200);
|
||||
handler: function (connection) {
|
||||
return connection.reply(200);
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'No operation',
|
||||
|
||||
@@ -7,14 +7,14 @@ const OPTIONS = {
|
||||
|
||||
module.exports = {
|
||||
directive: 'OPTS',
|
||||
handler: function ({command} = {}) {
|
||||
if (!_.has(command, 'arg')) return this.reply(501);
|
||||
handler: function (connection, command) {
|
||||
if (!_.has(command, 'arg')) return connection.reply(501);
|
||||
|
||||
const [_option, ...args] = command.arg.split(' ');
|
||||
const option = _.toUpper(_option);
|
||||
|
||||
if (!OPTIONS.hasOwnProperty(option)) return this.reply(500);
|
||||
return OPTIONS[option].call(this, args);
|
||||
if (!OPTIONS.hasOwnProperty(option)) return connection.reply(500);
|
||||
return OPTIONS[option].call(this, ...args);
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Select options for a feature'
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
module.exports = {
|
||||
directive: 'PASS',
|
||||
handler: function ({log, command} = {}) {
|
||||
if (!this.username) return this.reply(503);
|
||||
handler: function (connection, command) {
|
||||
if (!connection.username) return this.reply(503);
|
||||
if (this.authenticated) return this.reply(202);
|
||||
|
||||
// 332 : require account name (ACCT)
|
||||
|
||||
const password = command.arg;
|
||||
if (!password) return this.reply(501, 'Must provide password');
|
||||
return this.login(this.username, password)
|
||||
return connection.login(connection.username, password)
|
||||
.then(() => {
|
||||
return this.reply(230);
|
||||
return connection.reply(230);
|
||||
})
|
||||
.catch(err => {
|
||||
log.error(err);
|
||||
return this.reply(530, err.message || 'Authentication failed');
|
||||
connection.emit('error', err);
|
||||
return connection.reply(530, err.message || 'Authentication failed');
|
||||
});
|
||||
},
|
||||
syntax: '{{cmd}} <password>',
|
||||
|
||||
@@ -2,17 +2,17 @@ const PassiveConnector = require('../../connector/passive');
|
||||
|
||||
module.exports = {
|
||||
directive: 'PASV',
|
||||
handler: function () {
|
||||
this.connector = new PassiveConnector(this);
|
||||
return this.connector.setupServer()
|
||||
handler: function (connection) {
|
||||
connection.connector = new PassiveConnector(connection);
|
||||
return connection.connector.setupServer()
|
||||
.then(server => {
|
||||
const address = this.server.url.hostname;
|
||||
const address = connection.server.url.hostname;
|
||||
const {port} = server.address();
|
||||
const host = address.replace(/\./g, ',');
|
||||
const portByte1 = port / 256 | 0;
|
||||
const portByte2 = port % 256;
|
||||
|
||||
return this.reply(227, `PASV OK (${host},${portByte1},${portByte2})`);
|
||||
return connection.reply(227, `PASV OK (${host},${portByte1},${portByte2})`);
|
||||
});
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
module.exports = {
|
||||
directive: 'PBSZ',
|
||||
handler: function ({command} = {}) {
|
||||
if (!this.secure) return this.reply(202, 'Not suppored');
|
||||
this.bufferSize = parseInt(command.arg, 10);
|
||||
return this.reply(200, this.bufferSize === 0 ? 'OK' : 'Buffer too large: PBSZ=0');
|
||||
handler: function (connection, command) {
|
||||
if (!connection.secure) return connection.reply(202, 'Not suppored');
|
||||
connection.bufferSize = parseInt(command.arg, 10);
|
||||
return connection.reply(200, connection.bufferSize === 0 ? 'OK' : 'Buffer too large: PBSZ=0');
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Protection Buffer Size',
|
||||
|
||||
@@ -3,18 +3,18 @@ const ActiveConnector = require('../../connector/active');
|
||||
|
||||
module.exports = {
|
||||
directive: 'PORT',
|
||||
handler: function ({command} = {}) {
|
||||
this.connector = new ActiveConnector(this);
|
||||
handler: function (connection, command) {
|
||||
connection.connector = new ActiveConnector(connection);
|
||||
|
||||
const rawConnection = _.get(command, 'arg', '').split(',');
|
||||
if (rawConnection.length !== 6) return this.reply(425);
|
||||
if (rawConnection.length !== 6) return connection.reply(425);
|
||||
|
||||
const ip = rawConnection.slice(0, 4).join('.');
|
||||
const portBytes = rawConnection.slice(4).map(p => parseInt(p));
|
||||
const port = portBytes[0] * 256 + portBytes[1];
|
||||
|
||||
return this.connector.setupConnection(ip, port)
|
||||
.then(() => this.reply(200));
|
||||
return connection.connector.setupConnection(ip, port)
|
||||
.then(() => connection.reply(200));
|
||||
},
|
||||
syntax: '{{cmd}} <x>,<x>,<x>,<x>,<y>,<y>',
|
||||
description: 'Specifies an address and port to which the server should connect'
|
||||
|
||||
@@ -2,16 +2,16 @@ const _ = require('lodash');
|
||||
|
||||
module.exports = {
|
||||
directive: 'PROT',
|
||||
handler: function ({command} = {}) {
|
||||
if (!this.secure) return this.reply(202, 'Not suppored');
|
||||
if (!this.bufferSize && typeof this.bufferSize !== 'number') return this.reply(503);
|
||||
handler: function (connection, command) {
|
||||
if (!connection.secure) return connection.reply(202, 'Not suppored');
|
||||
if (!connection.bufferSize && typeof connection.bufferSize !== 'number') return connection.reply(503);
|
||||
|
||||
switch (_.toUpper(command.arg)) {
|
||||
case 'P': return this.reply(200, 'OK');
|
||||
case 'P': return connection.reply(200, 'OK');
|
||||
case 'C':
|
||||
case 'S':
|
||||
case 'E': return this.reply(536, 'Not supported');
|
||||
default: return this.reply(504);
|
||||
case 'E': return connection.reply(536, 'Not supported');
|
||||
default: return connection.reply(504);
|
||||
}
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
|
||||
@@ -3,18 +3,18 @@ const escapePath = require('../../helpers/escape-path');
|
||||
|
||||
module.exports = {
|
||||
directive: ['PWD', 'XPWD'],
|
||||
handler: function ({log} = {}) {
|
||||
if (!this.fs) return this.reply(550, 'File system not instantiated');
|
||||
if (!this.fs.currentDirectory) return this.reply(402, 'Not supported by file system');
|
||||
handler: function (connection) {
|
||||
if (!connection.fs) return connection.reply(550, 'File system not instantiated');
|
||||
if (!connection.fs.currentDirectory) return connection.reply(402, 'Not supported by file system');
|
||||
|
||||
return Promise.resolve(this.fs.currentDirectory())
|
||||
return Promise.resolve(connection.fs.currentDirectory())
|
||||
.then(cwd => {
|
||||
const path = cwd ? `"${escapePath(cwd)}"` : undefined;
|
||||
return this.reply(257, path);
|
||||
return connection.reply(257, path);
|
||||
})
|
||||
.catch(err => {
|
||||
log.error(err);
|
||||
return this.reply(550, err.message);
|
||||
connection.emit('error', err);
|
||||
return connection.reply(550, err.message);
|
||||
});
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module.exports = {
|
||||
directive: 'QUIT',
|
||||
handler: function () {
|
||||
return this.close(221, 'Client called QUIT');
|
||||
handler: function (connection) {
|
||||
return connection.close(221, 'Client called QUIT');
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Disconnect',
|
||||
|
||||
@@ -2,14 +2,14 @@ const _ = require('lodash');
|
||||
|
||||
module.exports = {
|
||||
directive: 'REST',
|
||||
handler: function ({command} = {}) {
|
||||
handler: function (connection, command) {
|
||||
const arg = _.get(command, 'arg');
|
||||
const byteCount = parseInt(arg, 10);
|
||||
|
||||
if (isNaN(byteCount) || byteCount < 0) return this.reply(501, 'Byte count must be 0 or greater');
|
||||
if (isNaN(byteCount) || byteCount < 0) return connection.reply(501, 'Byte count must be 0 or greater');
|
||||
|
||||
this.restByteCount = byteCount;
|
||||
return this.reply(350, `Restarting next transfer at ${byteCount}`);
|
||||
connection.restByteCount = byteCount;
|
||||
return connection.reply(350, `Restarting next transfer at ${byteCount}`);
|
||||
},
|
||||
syntax: '{{cmd}} <byte-count>',
|
||||
description: 'Restart transfer from the specified point. Resets after any STORE or RETRIEVE'
|
||||
|
||||
@@ -2,54 +2,54 @@ const Promise = require('bluebird');
|
||||
|
||||
module.exports = {
|
||||
directive: 'RETR',
|
||||
handler: function ({log, command} = {}) {
|
||||
if (!this.fs) return this.reply(550, 'File system not instantiated');
|
||||
if (!this.fs.read) return this.reply(402, 'Not supported by file system');
|
||||
handler: function (connection, command) {
|
||||
if (!connection.fs) return connection.reply(550, 'File system not instantiated');
|
||||
if (!connection.fs.read) return connection.reply(402, 'Not supported by file system');
|
||||
|
||||
const filePath = command.arg;
|
||||
|
||||
return this.connector.waitForConnection()
|
||||
.tap(() => this.commandSocket.pause())
|
||||
.then(() => Promise.resolve(this.fs.read(filePath, {start: this.restByteCount})))
|
||||
return connection.connector.waitForConnection()
|
||||
.tap(() => connection.commandSocket.pause())
|
||||
.then(() => Promise.resolve(connection.fs.read(filePath, {start: connection.restByteCount})))
|
||||
.then(stream => {
|
||||
const destroyConnection = (connection, reject) => err => {
|
||||
if (connection) connection.destroy(err);
|
||||
const destroyConnection = (conn, reject) => err => {
|
||||
if (conn) conn.destroy(err);
|
||||
reject(err);
|
||||
};
|
||||
|
||||
const eventsPromise = new Promise((resolve, reject) => {
|
||||
stream.on('data', data => {
|
||||
if (stream) stream.pause();
|
||||
if (this.connector.socket) {
|
||||
this.connector.socket.write(data, this.transferType, () => stream && stream.resume());
|
||||
if (connection.connector.socket) {
|
||||
connection.connector.socket.write(data, connection.transferType, () => stream && stream.resume());
|
||||
}
|
||||
});
|
||||
stream.once('end', () => resolve());
|
||||
stream.once('error', destroyConnection(this.connector.socket, reject));
|
||||
stream.once('error', destroyConnection(connection.connector.socket, reject));
|
||||
|
||||
this.connector.socket.once('error', destroyConnection(stream, reject));
|
||||
connection.connector.socket.once('error', destroyConnection(stream, reject));
|
||||
});
|
||||
|
||||
this.restByteCount = 0;
|
||||
connection.restByteCount = 0;
|
||||
|
||||
return this.reply(150).then(() => stream.resume() && this.connector.socket.resume())
|
||||
return connection.reply(150).then(() => stream.resume() && connection.connector.socket.resume())
|
||||
.then(() => eventsPromise)
|
||||
.tap(() => this.emit('RETR', null, filePath))
|
||||
.tap(() => connection.emit('RETR', null, filePath))
|
||||
.finally(() => stream.destroy && stream.destroy());
|
||||
})
|
||||
.then(() => this.reply(226))
|
||||
.then(() => connection.reply(226))
|
||||
.catch(Promise.TimeoutError, err => {
|
||||
log.error(err);
|
||||
return this.reply(425, 'No connection established');
|
||||
connection.emit('error', err);
|
||||
return connection.reply(425, 'No connection established');
|
||||
})
|
||||
.catch(err => {
|
||||
log.error(err);
|
||||
this.emit('RETR', err);
|
||||
return this.reply(551, err.message);
|
||||
connection.emit('error', err);
|
||||
connection.emit('RETR', err);
|
||||
return connection.reply(551, err.message);
|
||||
})
|
||||
.finally(() => {
|
||||
this.connector.end();
|
||||
this.commandSocket.resume();
|
||||
connection.connector.end();
|
||||
connection.commandSocket.resume();
|
||||
});
|
||||
},
|
||||
syntax: '{{cmd}} <path>',
|
||||
|
||||
@@ -2,8 +2,8 @@ const {handler: dele} = require('./dele');
|
||||
|
||||
module.exports = {
|
||||
directive: ['RMD', 'XRMD'],
|
||||
handler: function (args) {
|
||||
return dele.call(this, args);
|
||||
handler: function (...args) {
|
||||
return dele.call(this, ...args);
|
||||
},
|
||||
syntax: '{{cmd}} <path>',
|
||||
description: 'Remove a directory'
|
||||
|
||||
@@ -2,19 +2,19 @@ const Promise = require('bluebird');
|
||||
|
||||
module.exports = {
|
||||
directive: 'RNFR',
|
||||
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');
|
||||
handler: function (connection, command) {
|
||||
if (!connection.fs) return connection.reply(550, 'File system not instantiated');
|
||||
if (!connection.fs.get) return connection.reply(402, 'Not supported by file system');
|
||||
|
||||
const fileName = command.arg;
|
||||
return Promise.resolve(this.fs.get(fileName))
|
||||
return Promise.resolve(connection.fs.get(fileName))
|
||||
.then(() => {
|
||||
this.renameFrom = fileName;
|
||||
return this.reply(350);
|
||||
connection.renameFrom = fileName;
|
||||
return connection.reply(350);
|
||||
})
|
||||
.catch(err => {
|
||||
log.error(err);
|
||||
return this.reply(550, err.message);
|
||||
connection.emit('error', err);
|
||||
return connection.reply(550, err.message);
|
||||
});
|
||||
},
|
||||
syntax: '{{cmd}} <name>',
|
||||
|
||||
@@ -2,25 +2,25 @@ const Promise = require('bluebird');
|
||||
|
||||
module.exports = {
|
||||
directive: 'RNTO',
|
||||
handler: function ({log, command} = {}) {
|
||||
if (!this.renameFrom) return this.reply(503);
|
||||
handler: function (connection, command) {
|
||||
if (!connection.renameFrom) return connection.reply(503);
|
||||
|
||||
if (!this.fs) return this.reply(550, 'File system not instantiated');
|
||||
if (!this.fs.rename) return this.reply(402, 'Not supported by file system');
|
||||
if (!connection.fs) return connection.reply(550, 'File system not instantiated');
|
||||
if (!connection.fs.rename) return connection.reply(402, 'Not supported by file system');
|
||||
|
||||
const from = this.renameFrom;
|
||||
const from = connection.renameFrom;
|
||||
const to = command.arg;
|
||||
|
||||
return Promise.resolve(this.fs.rename(from, to))
|
||||
return Promise.resolve(connection.fs.rename(from, to))
|
||||
.then(() => {
|
||||
return this.reply(250);
|
||||
return connection.reply(250);
|
||||
})
|
||||
.catch(err => {
|
||||
log.error(err);
|
||||
return this.reply(550, err.message);
|
||||
connection.emit('error', err);
|
||||
return connection.reply(550, err.message);
|
||||
})
|
||||
.finally(() => {
|
||||
delete this.renameFrom;
|
||||
delete connection.renameFrom;
|
||||
});
|
||||
},
|
||||
syntax: '{{cmd}} <name>',
|
||||
|
||||
@@ -8,7 +8,7 @@ module.exports = {
|
||||
handler: function ({log, command} = {}) {
|
||||
const rawSubCommand = _.get(command, 'arg', '');
|
||||
const subCommand = this.commands.parse(rawSubCommand);
|
||||
const subLog = log.child({subverb: subCommand.directive});
|
||||
const subLog = log.scope(subCommand.directive);
|
||||
|
||||
if (!registry.hasOwnProperty(subCommand.directive)) return this.reply(502);
|
||||
|
||||
|
||||
@@ -2,17 +2,17 @@ const Promise = require('bluebird');
|
||||
|
||||
module.exports = {
|
||||
directive: 'SIZE',
|
||||
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');
|
||||
handler: function (connection, command) {
|
||||
if (!connection.fs) return connection.reply(550, 'File system not instantiated');
|
||||
if (!connection.fs.get) return connection.reply(402, 'Not supported by file system');
|
||||
|
||||
return Promise.resolve(this.fs.get(command.arg))
|
||||
return Promise.resolve(connection.fs.get(command.arg))
|
||||
.then(fileStat => {
|
||||
return this.reply(213, {message: fileStat.size});
|
||||
return connection.reply(213, {message: fileStat.size});
|
||||
})
|
||||
.catch(err => {
|
||||
log.error(err);
|
||||
return this.reply(550, err.message);
|
||||
connection.emit('error', err);
|
||||
return connection.reply(550, err.message);
|
||||
});
|
||||
},
|
||||
syntax: '{{cmd}} <path>',
|
||||
|
||||
@@ -4,26 +4,25 @@ const getFileStat = require('../../helpers/file-stat');
|
||||
|
||||
module.exports = {
|
||||
directive: 'STAT',
|
||||
handler: function (args = {}) {
|
||||
const {log, command} = args;
|
||||
handler: function (connection, command) {
|
||||
const path = _.get(command, 'arg');
|
||||
if (path) {
|
||||
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 (!connection.fs) return connection.reply(550, 'File system not instantiated');
|
||||
if (!connection.fs.get) return connection.reply(402, 'Not supported by file system');
|
||||
|
||||
return Promise.resolve(this.fs.get(path))
|
||||
return Promise.resolve(connection.fs.get(path))
|
||||
.then(stat => {
|
||||
if (stat.isDirectory()) {
|
||||
if (!this.fs.list) return this.reply(402, 'Not supported by file system');
|
||||
if (!connection.fs.list) return connection.reply(402, 'Not supported by file system');
|
||||
|
||||
return Promise.resolve(this.fs.list(path))
|
||||
return Promise.resolve(connection.fs.list(path))
|
||||
.then(stats => [213, stats]);
|
||||
}
|
||||
return [212, [stat]];
|
||||
})
|
||||
.then(([code, fileStats]) => {
|
||||
return Promise.map(fileStats, file => {
|
||||
const message = getFileStat(file, _.get(this, 'server.options.file_format', 'ls'));
|
||||
const message = getFileStat(file, _.get(connection, 'server.options.file_format', 'ls'));
|
||||
return {
|
||||
raw: true,
|
||||
message
|
||||
@@ -31,13 +30,13 @@ module.exports = {
|
||||
})
|
||||
.then(messages => [code, messages]);
|
||||
})
|
||||
.then(([code, messages]) => this.reply(code, 'Status begin', ...messages, 'Status end'))
|
||||
.then(([code, messages]) => connection.reply(code, 'Status begin', ...messages, 'Status end'))
|
||||
.catch(err => {
|
||||
log.error(err);
|
||||
return this.reply(450, err.message);
|
||||
connection.emit('error', err);
|
||||
return connection.reply(450, err.message);
|
||||
});
|
||||
} else {
|
||||
return this.reply(211, 'Status OK');
|
||||
return connection.reply(211, 'Status OK');
|
||||
}
|
||||
},
|
||||
syntax: '{{cmd}} [<path>]',
|
||||
|
||||
@@ -2,62 +2,62 @@ const Promise = require('bluebird');
|
||||
|
||||
module.exports = {
|
||||
directive: 'STOR',
|
||||
handler: function ({log, command} = {}) {
|
||||
if (!this.fs) return this.reply(550, 'File system not instantiated');
|
||||
if (!this.fs.write) return this.reply(402, 'Not supported by file system');
|
||||
handler: function (connection, command) {
|
||||
if (!connection.fs) return connection.reply(550, 'File system not instantiated');
|
||||
if (!connection.fs.write) return connection.reply(402, 'Not supported by file system');
|
||||
|
||||
const append = command.directive === 'APPE';
|
||||
const fileName = command.arg;
|
||||
|
||||
return this.connector.waitForConnection()
|
||||
.tap(() => this.commandSocket.pause())
|
||||
.then(() => Promise.resolve(this.fs.write(fileName, {append, start: this.restByteCount})))
|
||||
return connection.connector.waitForConnection()
|
||||
.tap(() => connection.commandSocket.pause())
|
||||
.then(() => Promise.resolve(connection.fs.write(fileName, {append, start: connection.restByteCount})))
|
||||
.then(stream => {
|
||||
const destroyConnection = (connection, reject) => err => {
|
||||
if (connection) connection.destroy(err);
|
||||
const destroyConnection = (conn, reject) => err => {
|
||||
if (conn) conn.destroy(err);
|
||||
reject(err);
|
||||
};
|
||||
|
||||
const streamPromise = new Promise((resolve, reject) => {
|
||||
stream.once('error', destroyConnection(this.connector.socket, reject));
|
||||
stream.once('error', destroyConnection(connection.connector.socket, reject));
|
||||
stream.once('finish', () => resolve());
|
||||
});
|
||||
|
||||
const socketPromise = new Promise((resolve, reject) => {
|
||||
this.connector.socket.on('data', data => {
|
||||
if (this.connector.socket) this.connector.socket.pause();
|
||||
connection.connector.socket.on('data', data => {
|
||||
if (connection.connector.socket) connection.connector.socket.pause();
|
||||
if (stream) {
|
||||
stream.write(data, this.transferType, () => this.connector.socket && this.connector.socket.resume());
|
||||
stream.write(data, connection.transferType, () => connection.connector.socket && connection.connector.socket.resume());
|
||||
}
|
||||
});
|
||||
this.connector.socket.once('end', () => {
|
||||
connection.connector.socket.once('end', () => {
|
||||
if (stream.listenerCount('close')) stream.emit('close');
|
||||
else stream.end();
|
||||
resolve();
|
||||
});
|
||||
this.connector.socket.once('error', destroyConnection(stream, reject));
|
||||
connection.connector.socket.once('error', destroyConnection(stream, reject));
|
||||
});
|
||||
|
||||
this.restByteCount = 0;
|
||||
connection.restByteCount = 0;
|
||||
|
||||
return this.reply(150).then(() => this.connector.socket.resume())
|
||||
return connection.reply(150).then(() => connection.connector.socket.resume())
|
||||
.then(() => Promise.join(streamPromise, socketPromise))
|
||||
.tap(() => this.emit('STOR', null, fileName))
|
||||
.tap(() => connection.emit('STOR', null, fileName))
|
||||
.finally(() => stream.destroy && stream.destroy());
|
||||
})
|
||||
.then(() => this.reply(226, fileName))
|
||||
.then(() => connection.reply(226, fileName))
|
||||
.catch(Promise.TimeoutError, err => {
|
||||
log.error(err);
|
||||
return this.reply(425, 'No connection established');
|
||||
connection.emit('error', err);
|
||||
return connection.reply(425, 'No connection established');
|
||||
})
|
||||
.catch(err => {
|
||||
log.error(err);
|
||||
this.emit('STOR', err);
|
||||
return this.reply(550, err.message);
|
||||
connection.emit('error', err);
|
||||
connection.emit('STOR', err);
|
||||
return connection.reply(550, err.message);
|
||||
})
|
||||
.finally(() => {
|
||||
this.connector.end();
|
||||
this.commandSocket.resume();
|
||||
connection.connector.end();
|
||||
connection.commandSocket.resume();
|
||||
});
|
||||
},
|
||||
syntax: '{{cmd}} <path>',
|
||||
|
||||
@@ -3,19 +3,19 @@ const {handler: stor} = require('./stor');
|
||||
|
||||
module.exports = {
|
||||
directive: 'STOU',
|
||||
handler: function (args) {
|
||||
handler: function (connection, command, ...args) {
|
||||
if (!this.fs) return this.reply(550, 'File system not instantiated');
|
||||
if (!this.fs.get || !this.fs.getUniqueName) return this.reply(402, 'Not supported by file system');
|
||||
|
||||
const fileName = args.command.arg;
|
||||
const fileName = command.arg;
|
||||
return Promise.try(() => {
|
||||
return Promise.resolve(this.fs.get(fileName))
|
||||
.then(() => Promise.resolve(this.fs.getUniqueName()))
|
||||
.catch(() => Promise.resolve(fileName));
|
||||
})
|
||||
.then(name => {
|
||||
args.command.arg = name;
|
||||
return stor.call(this, args);
|
||||
command.arg = name;
|
||||
return stor.call(this, connection, command, ...args);
|
||||
});
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module.exports = {
|
||||
directive: 'STRU',
|
||||
handler: function ({command} = {}) {
|
||||
return this.reply(/^F$/i.test(command.arg) ? 200 : 504);
|
||||
handler: function (connection, command) {
|
||||
return connection.reply(/^F$/i.test(command.arg) ? 200 : 504);
|
||||
},
|
||||
syntax: '{{cmd}} <structure>',
|
||||
description: 'Set file transfer structure',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module.exports = {
|
||||
directive: 'SYST',
|
||||
handler: function () {
|
||||
return this.reply(215);
|
||||
handler: function (connection) {
|
||||
return connection.reply(215);
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Return system type',
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
module.exports = {
|
||||
directive: 'TYPE',
|
||||
handler: function ({command} = {}) {
|
||||
handler: function (connection, command) {
|
||||
if (/^A[0-9]?$/i.test(command.arg)) {
|
||||
this.transferType = 'ascii';
|
||||
connection.transferType = 'ascii';
|
||||
} else if (/^L[0-9]?$/i.test(command.arg) || /^I$/i.test(command.arg)) {
|
||||
this.transferType = 'binary';
|
||||
connection.transferType = 'binary';
|
||||
} else {
|
||||
return this.reply(501);
|
||||
return connection.reply(501);
|
||||
}
|
||||
return this.reply(200, `Switch to "${this.transferType}" transfer mode.`);
|
||||
return connection.reply(200, `Switch to "${connection.transferType}" transfer mode.`);
|
||||
},
|
||||
syntax: '{{cmd}} <mode>',
|
||||
description: 'Set the transfer mode, binary (I) or ascii (A)',
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
module.exports = {
|
||||
directive: 'USER',
|
||||
handler: function ({log, command} = {}) {
|
||||
if (this.username) return this.reply(530, 'Username already set');
|
||||
if (this.authenticated) return this.reply(230);
|
||||
handler: function (connection, command) {
|
||||
if (connection.username) return connection.reply(530, 'Username already set');
|
||||
if (connection.authenticated) return connection.reply(230);
|
||||
|
||||
this.username = command.arg;
|
||||
if (!this.username) return this.reply(501, 'Must provide username');
|
||||
connection.username = command.arg;
|
||||
if (!connection.username) return connection.reply(501, 'Must provide username');
|
||||
|
||||
if (this.server.options.anonymous === true && this.username === 'anonymous' ||
|
||||
this.username === this.server.options.anonymous) {
|
||||
return this.login(this.username, '@anonymous')
|
||||
if (connection.server.options.anonymous === true && connection.username === 'anonymous' ||
|
||||
connection.username === connection.server.options.anonymous) {
|
||||
return connection.login(connection.username, '@anonymous')
|
||||
.then(() => {
|
||||
return this.reply(230);
|
||||
return connection.reply(230);
|
||||
})
|
||||
.catch(err => {
|
||||
log.error(err);
|
||||
return this.reply(530, err.message || 'Authentication failed');
|
||||
connection.emit('error', err);
|
||||
return connection.reply(530, err.message || 'Authentication failed');
|
||||
});
|
||||
}
|
||||
return this.reply(331);
|
||||
return connection.reply(331);
|
||||
},
|
||||
syntax: '{{cmd}} <username>',
|
||||
description: 'Authentication username',
|
||||
|
||||
@@ -10,11 +10,13 @@ const errors = require('./errors');
|
||||
const DEFAULT_MESSAGE = require('./messages');
|
||||
|
||||
class FtpConnection extends EventEmitter {
|
||||
constructor(server, options) {
|
||||
constructor(server, socket) {
|
||||
super();
|
||||
this.server = server;
|
||||
this.id = uuid.v4();
|
||||
this.log = options.log.child({id: this.id, ip: this.ip});
|
||||
this.commandSocket = socket;
|
||||
this.log = server.log.scope(`client: ${this.ip}`);
|
||||
// this.log = options.log.child({id: this.id, ip: this.ip});
|
||||
this.commands = new Commands(this);
|
||||
this.transferType = 'binary';
|
||||
this.encoding = 'utf8';
|
||||
@@ -24,10 +26,8 @@ class FtpConnection extends EventEmitter {
|
||||
|
||||
this.connector = new BaseConnector(this);
|
||||
|
||||
this.commandSocket = options.socket;
|
||||
this.commandSocket.on('error', err => {
|
||||
this.log.error(err, 'Client error');
|
||||
this.server.emit('client-error', {connection: this, context: 'commandSocket', error: err});
|
||||
this.log.scope('error event').error(err);
|
||||
});
|
||||
this.commandSocket.on('data', this._handleData.bind(this));
|
||||
this.commandSocket.on('timeout', () => {});
|
||||
@@ -40,7 +40,6 @@ class FtpConnection extends EventEmitter {
|
||||
|
||||
_handleData(data) {
|
||||
const messages = _.compact(data.toString(this.encoding).split('\r\n'));
|
||||
this.log.trace(messages);
|
||||
return Promise.mapSeries(messages, message => this.commands.handle(message));
|
||||
}
|
||||
|
||||
@@ -117,12 +116,13 @@ class FtpConnection extends EventEmitter {
|
||||
};
|
||||
|
||||
const processLetter = letter => {
|
||||
const log = this.log.scope('reply');
|
||||
return new Promise((resolve, reject) => {
|
||||
if (letter.socket && letter.socket.writable) {
|
||||
this.log.trace({port: letter.socket.address().port, encoding: letter.encoding, message: letter.message}, 'Reply');
|
||||
log.debug(letter.message, {port: letter.socket.address().port});
|
||||
letter.socket.write(letter.message + '\r\n', letter.encoding, err => {
|
||||
if (err) {
|
||||
this.log.error(err);
|
||||
log.error(err);
|
||||
return reject(err);
|
||||
}
|
||||
resolve();
|
||||
|
||||
@@ -7,6 +7,7 @@ class Active extends Connector {
|
||||
constructor(connection) {
|
||||
super(connection);
|
||||
this.type = 'active';
|
||||
this.log = connection.log.scope('active');
|
||||
}
|
||||
|
||||
waitForConnection({timeout = 5000, delay = 250} = {}) {
|
||||
@@ -29,10 +30,16 @@ class Active extends Connector {
|
||||
.then(() => {
|
||||
this.dataSocket = new Socket();
|
||||
this.dataSocket.setEncoding(this.connection.transferType);
|
||||
this.dataSocket.on('error', err => this.server && this.server.emit('client-error', {connection: this.connection, context: 'dataSocket', error: err}));
|
||||
this.dataSocket.on('error', err => this.connection.emit('error', err));
|
||||
this.dataSocket.on('close', () => {
|
||||
this.log.debug('socket closed');
|
||||
this.end();
|
||||
});
|
||||
this.dataSocket.connect({host, port, family}, () => {
|
||||
this.dataSocket.pause();
|
||||
|
||||
this.log.debug('connection', {port, remoteAddress: this.dataSocket.remoteAddress});
|
||||
|
||||
if (this.connection.secure) {
|
||||
const secureContext = tls.createSecureContext(this.server._tls);
|
||||
const secureSocket = new tls.TLSSocket(this.dataSocket, {
|
||||
|
||||
@@ -8,10 +8,7 @@ class Connector {
|
||||
this.dataSocket = null;
|
||||
this.dataServer = null;
|
||||
this.type = false;
|
||||
}
|
||||
|
||||
get log() {
|
||||
return this.connection.log;
|
||||
this.log = connection.log.scope('connector');
|
||||
}
|
||||
|
||||
get socket() {
|
||||
|
||||
@@ -11,6 +11,7 @@ class Passive extends Connector {
|
||||
constructor(connection) {
|
||||
super(connection);
|
||||
this.type = 'passive';
|
||||
this.log = connection.log.scope('passive');
|
||||
}
|
||||
|
||||
waitForConnection({timeout = 5000, delay = 250} = {}) {
|
||||
@@ -37,16 +38,16 @@ class Passive extends Connector {
|
||||
.then(port => {
|
||||
const connectionHandler = socket => {
|
||||
if (!ip.isEqual(this.connection.commandSocket.remoteAddress, socket.remoteAddress)) {
|
||||
this.log.error({
|
||||
this.log.error('ip address mismatch', {
|
||||
pasv_connection: socket.remoteAddress,
|
||||
cmd_connection: this.connection.commandSocket.remoteAddress
|
||||
}, 'Connecting addresses do not match');
|
||||
});
|
||||
|
||||
socket.destroy();
|
||||
return this.connection.reply(550, 'Remote addresses do not match')
|
||||
return this.connection.reply(550, 'IP address mismatch')
|
||||
.finally(() => this.connection.close());
|
||||
}
|
||||
this.log.trace({port, remoteAddress: socket.remoteAddress}, 'Passive connection fulfilled.');
|
||||
this.log.debug('connection', {port, remoteAddress: socket.remoteAddress});
|
||||
|
||||
if (this.connection.secure) {
|
||||
const secureContext = tls.createSecureContext(this.server._tls);
|
||||
@@ -60,9 +61,9 @@ class Passive extends Connector {
|
||||
}
|
||||
this.dataSocket.connected = true;
|
||||
this.dataSocket.setEncoding(this.connection.transferType);
|
||||
this.dataSocket.on('error', err => this.server && this.server.emit('client-error', {connection: this.connection, context: 'dataSocket', error: err}));
|
||||
this.dataSocket.on('error', err => this.connection.emit('error', err));
|
||||
this.dataSocket.on('close', () => {
|
||||
this.log.trace('Passive connection closed');
|
||||
this.log.debug('socket closed');
|
||||
this.end();
|
||||
});
|
||||
};
|
||||
@@ -70,9 +71,9 @@ class Passive extends Connector {
|
||||
this.dataSocket = null;
|
||||
this.dataServer = net.createServer({pauseOnConnect: true}, connectionHandler);
|
||||
this.dataServer.maxConnections = 1;
|
||||
this.dataServer.on('error', err => this.server && this.server.emit('client-error', {connection: this.connection, context: 'dataServer', error: err}));
|
||||
this.dataServer.on('error', err => this.connection.emit('error', err));
|
||||
this.dataServer.on('close', () => {
|
||||
this.log.trace('Passive server closed');
|
||||
this.log.debug('server closed');
|
||||
this.dataServer = null;
|
||||
});
|
||||
|
||||
@@ -80,7 +81,7 @@ class Passive extends Connector {
|
||||
this.dataServer.listen(port, err => {
|
||||
if (err) reject(err);
|
||||
else {
|
||||
this.log.debug({port}, 'Passive connection listening');
|
||||
this.log.debug('listening', {port});
|
||||
resolve(this.dataServer);
|
||||
}
|
||||
});
|
||||
|
||||
17
src/index.js
17
src/index.js
@@ -1,7 +1,7 @@
|
||||
const _ = require('lodash');
|
||||
const Promise = require('bluebird');
|
||||
const nodeUrl = require('url');
|
||||
const buyan = require('bunyan');
|
||||
const {Signale} = require('signale');
|
||||
const net = require('net');
|
||||
const tls = require('tls');
|
||||
const fs = require('fs');
|
||||
@@ -14,7 +14,9 @@ class FtpServer extends EventEmitter {
|
||||
constructor(url, options = {}) {
|
||||
super();
|
||||
this.options = _.merge({
|
||||
log: buyan.createLogger({name: 'ftp-srv'}),
|
||||
log: new Signale({
|
||||
scope: 'ftp-srv'
|
||||
}),
|
||||
anonymous: false,
|
||||
pasv_range: 22,
|
||||
file_format: 'ls',
|
||||
@@ -35,7 +37,7 @@ class FtpServer extends EventEmitter {
|
||||
this.url = nodeUrl.parse(url || 'ftp://127.0.0.1:21');
|
||||
|
||||
const serverConnectionHandler = socket => {
|
||||
let connection = new Connection(this, {log: this.log, socket});
|
||||
let connection = new Connection(this, socket);
|
||||
this.connections[connection.id] = connection;
|
||||
|
||||
socket.on('close', () => this.disconnectClient(connection.id));
|
||||
@@ -48,7 +50,7 @@ class FtpServer extends EventEmitter {
|
||||
const serverOptions = _.assign(this.isTLS ? this._tls : {}, {pauseOnConnect: true});
|
||||
|
||||
this.server = (this.isTLS ? tls : net).createServer(serverOptions, serverConnectionHandler);
|
||||
this.server.on('error', err => this.log.error(err, '[Event] error'));
|
||||
this.server.on('error', err => this.log.scope('error event').error(err));
|
||||
|
||||
const quit = _.debounce(this.quit.bind(this), 100);
|
||||
|
||||
@@ -122,7 +124,8 @@ class FtpServer extends EventEmitter {
|
||||
try {
|
||||
client.close(0);
|
||||
} catch (err) {
|
||||
this.log.error(err, 'Error closing connection', {id});
|
||||
this.log.error('Error disconnecting client', err);
|
||||
this.log.debug('User ID', {id});
|
||||
} finally {
|
||||
resolve('Disconnected');
|
||||
}
|
||||
@@ -135,12 +138,12 @@ class FtpServer extends EventEmitter {
|
||||
}
|
||||
|
||||
close() {
|
||||
this.log.info('Server closing...');
|
||||
this.log.await('Closing server...');
|
||||
this.server.maxConnections = 0;
|
||||
return Promise.map(Object.keys(this.connections), id => Promise.try(this.disconnectClient.bind(this, id)))
|
||||
.then(() => new Promise(resolve => {
|
||||
this.server.close(err => {
|
||||
if (err) this.log.error(err, 'Error closing server');
|
||||
if (err) this.log.error('Error closing server', err);
|
||||
resolve('Closed');
|
||||
});
|
||||
}))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const {expect} = require('chai');
|
||||
const Promise = require('bluebird');
|
||||
const bunyan = require('bunyan');
|
||||
const {Signale} = require('signale');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const FtpCommands = require('../../src/commands');
|
||||
@@ -10,7 +10,7 @@ describe('FtpCommands', function () {
|
||||
let commands;
|
||||
let mockConnection = {
|
||||
authenticated: false,
|
||||
log: bunyan.createLogger({name: 'FtpCommands'}),
|
||||
log: new Signale('commands'),
|
||||
reply: () => Promise.resolve({}),
|
||||
server: {
|
||||
options: {
|
||||
@@ -92,7 +92,7 @@ describe('FtpCommands', function () {
|
||||
.then(() => {
|
||||
expect(mockConnection.reply.callCount).to.equal(1);
|
||||
expect(mockConnection.reply.args[0][0]).to.equal(502);
|
||||
expect(mockConnection.reply.args[0][1]).to.match(/blacklisted/);
|
||||
expect(mockConnection.reply.args[0][1]).to.match(/blacklist/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -102,7 +102,7 @@ describe('FtpCommands', function () {
|
||||
.then(() => {
|
||||
expect(mockConnection.reply.callCount).to.equal(1);
|
||||
expect(mockConnection.reply.args[0][0]).to.equal(502);
|
||||
expect(mockConnection.reply.args[0][1]).to.match(/whitelisted/);
|
||||
expect(mockConnection.reply.args[0][1]).to.match(/whitelist/);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -10,9 +10,10 @@ describe(CMD, function () {
|
||||
connector: {
|
||||
waitForConnection: () => Promise.resolve(),
|
||||
end: () => Promise.resolve()
|
||||
}
|
||||
},
|
||||
emit: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -29,7 +30,7 @@ describe(CMD, function () {
|
||||
mockClient.connector.waitForConnection.restore();
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').rejects();
|
||||
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.connector.waitForConnection.callCount).to.equal(1);
|
||||
expect(mockClient.connector.end.callCount).to.equal(0);
|
||||
@@ -38,7 +39,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// successful | active connection', () => {
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.connector.waitForConnection.callCount).to.equal(1);
|
||||
expect(mockClient.connector.end.callCount).to.equal(1);
|
||||
|
||||
@@ -8,7 +8,7 @@ describe(CMD, function () {
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -20,7 +20,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(202);
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ describe(CMD, function () {
|
||||
_tls: {}
|
||||
}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -23,7 +23,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('TLS // supported', () => {
|
||||
return cmdFn({command: {arg: 'TLS', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'TLS', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(234);
|
||||
expect(mockClient.secure).to.equal(true);
|
||||
@@ -31,14 +31,14 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('SSL // not supported', () => {
|
||||
return cmdFn({command: {arg: 'SSL', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'SSL', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504);
|
||||
});
|
||||
});
|
||||
|
||||
it('bad // bad', () => {
|
||||
return cmdFn({command: {arg: 'bad', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'bad', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504);
|
||||
});
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
const Promise = require('bluebird');
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const CMD = 'CDUP';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve(),
|
||||
fs: {
|
||||
chdir: () => Promise.resolve()
|
||||
}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -26,7 +24,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('.. // successful', () => {
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
return cmdFn(mockClient, {directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(250);
|
||||
expect(mockClient.fs.chdir.args[0][0]).to.equal('..');
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const CMD = 'CWD';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
fs: {chdir: () => {}}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -25,10 +23,10 @@ describe(CMD, function () {
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = {reply: () => {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
return badCmdFn()
|
||||
return badCmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
@@ -36,10 +34,10 @@ describe(CMD, function () {
|
||||
|
||||
it('fails on no fs chdir command', () => {
|
||||
const badMockClient = {reply: () => {}, fs: {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
return badCmdFn()
|
||||
return badCmdFn(badMockClient)
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
@@ -47,7 +45,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('test // successful', () => {
|
||||
return cmdFn({log, command: {arg: 'test', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'test', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(250);
|
||||
expect(mockClient.fs.chdir.args[0][0]).to.equal('test');
|
||||
@@ -58,7 +56,7 @@ describe(CMD, function () {
|
||||
mockClient.fs.chdir.restore();
|
||||
sandbox.stub(mockClient.fs, 'chdir').resolves('/test');
|
||||
|
||||
return cmdFn({log, command: {arg: 'test', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'test', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(250);
|
||||
expect(mockClient.fs.chdir.args[0][0]).to.equal('test');
|
||||
@@ -69,7 +67,7 @@ describe(CMD, function () {
|
||||
mockClient.fs.chdir.restore();
|
||||
sandbox.stub(mockClient.fs, 'chdir').rejects(new Error('Bad'));
|
||||
|
||||
return cmdFn({log, command: {arg: 'bad', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'bad', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
expect(mockClient.fs.chdir.args[0][0]).to.equal('bad');
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const CMD = 'DELE';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
fs: {delete: () => {}}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -25,10 +23,10 @@ describe(CMD, function () {
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = {reply: () => {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
return badCmdFn()
|
||||
return badCmdFn(badMockClient)
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
@@ -39,7 +37,7 @@ describe(CMD, function () {
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
return badCmdFn()
|
||||
return badCmdFn(badMockClient)
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
@@ -47,7 +45,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('test // successful', () => {
|
||||
return cmdFn({log, command: {arg: 'test', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'test', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(250);
|
||||
expect(mockClient.fs.delete.args[0][0]).to.equal('test');
|
||||
@@ -58,7 +56,7 @@ describe(CMD, function () {
|
||||
mockClient.fs.delete.restore();
|
||||
sandbox.stub(mockClient.fs, 'delete').rejects(new Error('Bad'));
|
||||
|
||||
return cmdFn({log, command: {arg: 'bad', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'bad', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
expect(mockClient.fs.delete.args[0][0]).to.equal('bad');
|
||||
|
||||
@@ -10,7 +10,7 @@ describe(CMD, function () {
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -23,21 +23,21 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// unsuccessful | no argument', () => {
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful | invalid argument', () => {
|
||||
return cmdFn({command: {arg: 'blah'}})
|
||||
return cmdFn(mockClient, {arg: 'blah'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504);
|
||||
});
|
||||
});
|
||||
|
||||
it('// successful IPv4', () => {
|
||||
return cmdFn({command: {arg: '|1|192.168.0.100|35286|'}})
|
||||
return cmdFn(mockClient, {arg: '|1|192.168.0.100|35286|'})
|
||||
.then(() => {
|
||||
const [ip, port, family] = ActiveConnector.prototype.setupConnection.args[0];
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
@@ -48,7 +48,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// successful IPv6', () => {
|
||||
return cmdFn({command: {arg: '|2|8536:933f:e7f3:3e91:6dc1:e8c6:8482:7b23|35286|'}})
|
||||
return cmdFn(mockClient, {arg: '|2|8536:933f:e7f3:3e91:6dc1:e8c6:8482:7b23|35286|'})
|
||||
.then(() => {
|
||||
const [ip, port, family] = ActiveConnector.prototype.setupConnection.args[0];
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
|
||||
@@ -10,7 +10,7 @@ describe(CMD, function () {
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -25,7 +25,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// successful IPv4', () => {
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
const [code, message] = mockClient.reply.args[0];
|
||||
expect(code).to.equal(229);
|
||||
|
||||
@@ -8,7 +8,7 @@ describe(CMD, function () {
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -20,28 +20,28 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn({command: {directive: CMD}})
|
||||
return cmdFn(mockClient, {directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(211);
|
||||
});
|
||||
});
|
||||
|
||||
it('help // successful', () => {
|
||||
return cmdFn({command: {arg: 'help', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'help', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(214);
|
||||
});
|
||||
});
|
||||
|
||||
it('allo // successful', () => {
|
||||
return cmdFn({command: {arg: 'allo', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'allo', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(214);
|
||||
});
|
||||
});
|
||||
|
||||
it('bad // unsuccessful', () => {
|
||||
return cmdFn({command: {arg: 'bad', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'bad', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(502);
|
||||
});
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
const Promise = require('bluebird');
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const CMD = 'LIST';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
fs: {
|
||||
@@ -22,7 +20,7 @@ describe(CMD, function () {
|
||||
pause: () => {}
|
||||
}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -89,10 +87,10 @@ describe(CMD, function () {
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = {reply: () => {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
return badCmdFn()
|
||||
return badCmdFn(badMockClient)
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
@@ -100,10 +98,10 @@ describe(CMD, function () {
|
||||
|
||||
it('fails on no fs list command', () => {
|
||||
const badMockClient = {reply: () => {}, fs: {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
return badCmdFn()
|
||||
return badCmdFn(badMockClient)
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
@@ -111,7 +109,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('. // successful', () => {
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
return cmdFn(mockClient, {directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(150);
|
||||
expect(mockClient.reply.args[1].length).to.equal(3);
|
||||
@@ -143,7 +141,7 @@ describe(CMD, function () {
|
||||
isDirectory: () => false
|
||||
});
|
||||
|
||||
return cmdFn({log, command: {directive: CMD, arg: 'testfile.txt'}})
|
||||
return cmdFn(mockClient, {directive: CMD, arg: 'testfile.txt'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(150);
|
||||
expect(mockClient.reply.args[1].length).to.equal(2);
|
||||
@@ -158,7 +156,7 @@ describe(CMD, function () {
|
||||
mockClient.fs.list.restore();
|
||||
sandbox.stub(mockClient.fs, 'list').rejects(new Error());
|
||||
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
return cmdFn(mockClient, {directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(451);
|
||||
});
|
||||
@@ -167,7 +165,7 @@ describe(CMD, function () {
|
||||
it('. // unsuccessful (timeout)', () => {
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').returns(Promise.reject(new Promise.TimeoutError()));
|
||||
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
return cmdFn(mockClient, {directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(425);
|
||||
});
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const CMD = 'MDTM';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
fs: {get: () => {}}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -25,10 +23,10 @@ describe(CMD, function () {
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = {reply: () => {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
return badCmdFn()
|
||||
return badCmdFn(badMockClient)
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
@@ -36,10 +34,10 @@ describe(CMD, function () {
|
||||
|
||||
it('fails on no fs get command', () => {
|
||||
const badMockClient = {reply: () => {}, fs: {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
return badCmdFn()
|
||||
return badCmdFn(badMockClient)
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
@@ -47,7 +45,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('. // successful', () => {
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
return cmdFn(mockClient, {directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(213);
|
||||
//expect(mockClient.reply.args[0][1]).to.equal('20111010172411.000');
|
||||
@@ -58,7 +56,7 @@ describe(CMD, function () {
|
||||
mockClient.fs.get.restore();
|
||||
sandbox.stub(mockClient.fs, 'get').rejects(new Error());
|
||||
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
return cmdFn(mockClient, {directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const CMD = 'MKD';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
fs: {mkdir: () => {}}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -25,10 +23,10 @@ describe(CMD, function () {
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = {reply: () => {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
return badCmdFn()
|
||||
return badCmdFn(badMockClient)
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
@@ -36,10 +34,10 @@ describe(CMD, function () {
|
||||
|
||||
it('fails on no fs mkdir command', () => {
|
||||
const badMockClient = {reply: () => {}, fs: {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
return badCmdFn()
|
||||
return badCmdFn(badMockClient)
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
@@ -47,7 +45,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('test // successful', () => {
|
||||
return cmdFn({log, command: {arg: 'test', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'test', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(257);
|
||||
expect(mockClient.fs.mkdir.args[0][0]).to.equal('test');
|
||||
@@ -58,7 +56,7 @@ describe(CMD, function () {
|
||||
mockClient.fs.mkdir.restore();
|
||||
sandbox.stub(mockClient.fs, 'mkdir').resolves('test');
|
||||
|
||||
return cmdFn({log, command: {arg: 'test', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'test', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(257);
|
||||
expect(mockClient.fs.mkdir.args[0][0]).to.equal('test');
|
||||
@@ -69,7 +67,7 @@ describe(CMD, function () {
|
||||
mockClient.fs.mkdir.restore();
|
||||
sandbox.stub(mockClient.fs, 'mkdir').rejects(new Error('Bad'));
|
||||
|
||||
return cmdFn({log, command: {arg: 'bad', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'bad', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
expect(mockClient.fs.mkdir.args[0][0]).to.equal('bad');
|
||||
|
||||
@@ -8,7 +8,7 @@ describe(CMD, function () {
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -20,14 +20,14 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('S // successful', () => {
|
||||
return cmdFn({command: {arg: 'S'}})
|
||||
return cmdFn(mockClient, {arg: 'S'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
});
|
||||
});
|
||||
|
||||
it('Q // unsuccessful', () => {
|
||||
return cmdFn({command: {arg: 'Q'}})
|
||||
return cmdFn(mockClient, {arg: 'Q'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504);
|
||||
});
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
const Promise = require('bluebird');
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const CMD = 'NLST';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
fs: {
|
||||
@@ -22,7 +20,7 @@ describe(CMD, function () {
|
||||
pause: () => {}
|
||||
}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -87,7 +85,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('. // successful', () => {
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
return cmdFn(mockClient, {directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(150);
|
||||
expect(mockClient.reply.args[1].length).to.equal(3);
|
||||
@@ -119,7 +117,7 @@ describe(CMD, function () {
|
||||
isDirectory: () => false
|
||||
});
|
||||
|
||||
return cmdFn({log, command: {directive: CMD, arg: 'testfile.txt'}})
|
||||
return cmdFn(mockClient, {directive: CMD, arg: 'testfile.txt'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(150);
|
||||
expect(mockClient.reply.args[1].length).to.equal(2);
|
||||
|
||||
@@ -8,7 +8,7 @@ describe(CMD, function () {
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -20,7 +20,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ describe(CMD, function () {
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -20,28 +20,28 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// unsuccessful', () => {
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501);
|
||||
});
|
||||
});
|
||||
|
||||
it('BAD // unsuccessful', () => {
|
||||
return cmdFn({command: {arg: 'BAD', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'BAD', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(500);
|
||||
});
|
||||
});
|
||||
|
||||
it('UTF8 BAD // unsuccessful', () => {
|
||||
return cmdFn({command: {arg: 'UTF8 BAD', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'UTF8 BAD', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501);
|
||||
});
|
||||
});
|
||||
|
||||
it('UTF8 OFF // successful', () => {
|
||||
return cmdFn({command: {arg: 'UTF8 OFF', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'UTF8 OFF', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.encoding).to.equal('ascii');
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
@@ -49,7 +49,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('UTF8 ON // successful', () => {
|
||||
return cmdFn({command: {arg: 'UTF8 ON', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'UTF8 ON', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.encoding).to.equal('utf8');
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const CMD = 'PASS';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
login: () => {},
|
||||
server: {options: {anonymous: false}},
|
||||
username: 'anonymous'
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -25,7 +23,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('pass // successful', () => {
|
||||
return cmdFn({log, command: {arg: 'pass', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'pass', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(230);
|
||||
expect(mockClient.login.args[0]).to.eql(['anonymous', 'pass']);
|
||||
@@ -35,7 +33,7 @@ describe(CMD, function () {
|
||||
it('// successful (already authenticated)', () => {
|
||||
mockClient.server.options.anonymous = true;
|
||||
mockClient.authenticated = true;
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
return cmdFn(mockClient, {directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(202);
|
||||
expect(mockClient.login.callCount).to.equal(0);
|
||||
@@ -48,7 +46,7 @@ describe(CMD, function () {
|
||||
mockClient.login.restore();
|
||||
sandbox.stub(mockClient, 'login').rejects('bad');
|
||||
|
||||
return cmdFn({log, command: {arg: 'bad', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'bad', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(530);
|
||||
});
|
||||
@@ -58,7 +56,7 @@ describe(CMD, function () {
|
||||
mockClient.login.restore();
|
||||
sandbox.stub(mockClient, 'login').rejects({});
|
||||
|
||||
return cmdFn({log, command: {arg: 'bad', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'bad', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(530);
|
||||
});
|
||||
@@ -66,7 +64,7 @@ describe(CMD, function () {
|
||||
|
||||
it('bad // unsuccessful', () => {
|
||||
delete mockClient.username;
|
||||
return cmdFn({log, command: {arg: 'bad', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'bad', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(503);
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ describe(CMD, function () {
|
||||
reply: () => Promise.resolve(),
|
||||
server: {}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -21,7 +21,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// unsuccessful', () => {
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(202);
|
||||
});
|
||||
@@ -31,7 +31,7 @@ describe(CMD, function () {
|
||||
mockClient.secure = true;
|
||||
mockClient.server._tls = {};
|
||||
|
||||
return cmdFn({command: {arg: '0'}})
|
||||
return cmdFn(mockClient, {arg: '0'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
expect(mockClient.bufferSize).to.equal(0);
|
||||
@@ -42,7 +42,7 @@ describe(CMD, function () {
|
||||
mockClient.secure = true;
|
||||
mockClient.server._tls = {};
|
||||
|
||||
return cmdFn({command: {arg: '10'}})
|
||||
return cmdFn(mockClient, {arg: '10'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
expect(mockClient.bufferSize).to.equal(10);
|
||||
|
||||
@@ -10,7 +10,7 @@ describe(CMD, function () {
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -23,21 +23,21 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// unsuccessful | no argument', () => {
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(425);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful | invalid argument', () => {
|
||||
return cmdFn({command: {arg: '1,2,3,4,5'}})
|
||||
return cmdFn(mockClient, {arg: '1,2,3,4,5'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(425);
|
||||
});
|
||||
});
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn({command: {arg: '192,168,0,100,137,214'}})
|
||||
return cmdFn(mockClient, {arg: '192,168,0,100,137,214'})
|
||||
.then(() => {
|
||||
const [ip, port] = ActiveConnector.prototype.setupConnection.args[0];
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
|
||||
@@ -9,7 +9,7 @@ describe(CMD, function () {
|
||||
reply: () => Promise.resolve(),
|
||||
server: {}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -21,7 +21,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// unsuccessful', () => {
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(202);
|
||||
});
|
||||
@@ -31,7 +31,7 @@ describe(CMD, function () {
|
||||
mockClient.server._tls = {};
|
||||
mockClient.secure = true;
|
||||
|
||||
return cmdFn({command: {arg: 'P'}})
|
||||
return cmdFn(mockClient, {arg: 'P'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(503);
|
||||
});
|
||||
@@ -41,7 +41,7 @@ describe(CMD, function () {
|
||||
mockClient.bufferSize = 0;
|
||||
mockClient.secure = true;
|
||||
|
||||
return cmdFn({command: {arg: 'p'}})
|
||||
return cmdFn(mockClient, {arg: 'p'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
});
|
||||
@@ -49,7 +49,7 @@ describe(CMD, function () {
|
||||
|
||||
it('// unsuccessful - unsupported', () => {
|
||||
mockClient.secure = true;
|
||||
return cmdFn({command: {arg: 'C'}})
|
||||
return cmdFn(mockClient, {arg: 'C'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(536);
|
||||
});
|
||||
@@ -57,7 +57,7 @@ describe(CMD, function () {
|
||||
|
||||
it('// unsuccessful - unknown', () => {
|
||||
mockClient.secure = true;
|
||||
return cmdFn({command: {arg: 'QQ'}})
|
||||
return cmdFn(mockClient, {arg: 'QQ'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504);
|
||||
});
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const CMD = 'PWD';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
fs: {currentDirectory: () => {}}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -28,7 +26,7 @@ describe(CMD, function () {
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
return badCmdFn()
|
||||
return badCmdFn(badMockClient)
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
@@ -39,7 +37,7 @@ describe(CMD, function () {
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
return badCmdFn()
|
||||
return badCmdFn(badMockClient)
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
@@ -47,7 +45,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn({log, command: {arg: 'test', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'test', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(257);
|
||||
});
|
||||
@@ -57,7 +55,7 @@ describe(CMD, function () {
|
||||
mockClient.fs.currentDirectory.restore();
|
||||
sandbox.stub(mockClient.fs, 'currentDirectory').resolves('/test');
|
||||
|
||||
return cmdFn({log, command: {arg: 'test', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'test', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(257);
|
||||
});
|
||||
@@ -67,7 +65,7 @@ describe(CMD, function () {
|
||||
mockClient.fs.currentDirectory.restore();
|
||||
sandbox.stub(mockClient.fs, 'currentDirectory').rejects(new Error('Bad'));
|
||||
|
||||
return cmdFn({log, command: {arg: 'bad', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'bad', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@ describe(CMD, function () {
|
||||
const mockClient = {
|
||||
close: () => {}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -19,7 +19,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.close.callCount).to.equal(1);
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ describe(CMD, function () {
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -20,28 +20,28 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// unsuccessful', () => {
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501);
|
||||
});
|
||||
});
|
||||
|
||||
it('-1 // unsuccessful', () => {
|
||||
return cmdFn({command: {arg: '-1', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: '-1', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501);
|
||||
});
|
||||
});
|
||||
|
||||
it('bad // unsuccessful', () => {
|
||||
return cmdFn({command: {arg: 'bad', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: 'bad', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501);
|
||||
});
|
||||
});
|
||||
|
||||
it('1 // successful', () => {
|
||||
return cmdFn({command: {arg: '1', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: '1', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.restByteCount).to.equal(1);
|
||||
expect(mockClient.reply.args[0][0]).to.equal(350);
|
||||
@@ -49,7 +49,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('0 // successful', () => {
|
||||
return cmdFn({command: {arg: '0', directive: CMD}})
|
||||
return cmdFn(mockClient, {arg: '0', directive: CMD})
|
||||
.then(() => {
|
||||
expect(mockClient.restByteCount).to.equal(0);
|
||||
expect(mockClient.reply.args[0][0]).to.equal(350);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const Promise = require('bluebird');
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const EventEmitter = require('events');
|
||||
@@ -7,7 +6,6 @@ const EventEmitter = require('events');
|
||||
const CMD = 'RETR';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
let emitter;
|
||||
const mockClient = {
|
||||
commandSocket: {
|
||||
@@ -22,7 +20,7 @@ describe(CMD, function () {
|
||||
end: () => {}
|
||||
}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -43,7 +41,7 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
@@ -52,7 +50,7 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
@@ -64,7 +62,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'}})
|
||||
return cmdFn(mockClient, {arg: 'test.txt'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(425);
|
||||
});
|
||||
@@ -75,7 +73,7 @@ describe(CMD, function () {
|
||||
return Promise.reject(new Error('test'));
|
||||
});
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'}})
|
||||
return cmdFn(mockClient, {arg: 'test.txt'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(551);
|
||||
});
|
||||
@@ -91,7 +89,7 @@ describe(CMD, function () {
|
||||
errorEmitted = !!err;
|
||||
});
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'}})
|
||||
return cmdFn(mockClient, {arg: 'test.txt'})
|
||||
.then(() => {
|
||||
expect(errorEmitted).to.equal(true);
|
||||
});
|
||||
|
||||
@@ -5,9 +5,8 @@ const sinon = require('sinon');
|
||||
const CMD = 'RNFR';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockLog = {error: () => {}};
|
||||
const mockClient = {reply: () => Promise.resolve()};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -27,7 +26,7 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
@@ -36,7 +35,7 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
@@ -46,14 +45,14 @@ describe(CMD, function () {
|
||||
mockClient.fs.get.restore();
|
||||
sandbox.stub(mockClient.fs, 'get').rejects(new Error('test'));
|
||||
|
||||
return cmdFn({log: mockLog, command: {arg: 'test'}})
|
||||
return cmdFn(mockClient, {arg: 'test'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
|
||||
it('test // successful', () => {
|
||||
return cmdFn({log: mockLog, command: {arg: 'test'}})
|
||||
return cmdFn(mockClient, {arg: 'test'})
|
||||
.then(() => {
|
||||
expect(mockClient.fs.get.args[0][0]).to.equal('test');
|
||||
expect(mockClient.reply.args[0][0]).to.equal(350);
|
||||
|
||||
@@ -5,9 +5,8 @@ const sinon = require('sinon');
|
||||
const CMD = 'RNTO';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockLog = {error: () => {}};
|
||||
const mockClient = {reply: () => Promise.resolve()};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -28,7 +27,7 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | no renameFrom set', () => {
|
||||
delete mockClient.renameFrom;
|
||||
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(503);
|
||||
});
|
||||
@@ -37,7 +36,7 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
@@ -46,7 +45,7 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
@@ -56,14 +55,14 @@ describe(CMD, function () {
|
||||
mockClient.fs.rename.restore();
|
||||
sandbox.stub(mockClient.fs, 'rename').rejects(new Error('test'));
|
||||
|
||||
return cmdFn({log: mockLog, command: {arg: 'new'}})
|
||||
return cmdFn(mockClient, {arg: 'new'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
|
||||
it('new // successful', () => {
|
||||
return cmdFn({command: {arg: 'new'}})
|
||||
return cmdFn(mockClient, {arg: 'new'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(250);
|
||||
expect(mockClient.fs.rename.args[0]).to.eql(['test', 'new']);
|
||||
|
||||
@@ -5,9 +5,8 @@ const sinon = require('sinon');
|
||||
const CMD = 'CHMOD';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockLog = {error: () => {}};
|
||||
const mockClient = {reply: () => Promise.resolve()};
|
||||
const cmdFn = require(`../../../../src/commands/registration/site/${CMD.toLowerCase()}`).bind(mockClient);
|
||||
const cmdFn = require(`../../../../src/commands/registration/site/${CMD.toLowerCase()}`);
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -26,7 +25,7 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | no file system', done => {
|
||||
delete mockClient.fs;
|
||||
|
||||
cmdFn()
|
||||
cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
done();
|
||||
@@ -37,7 +36,7 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | file system does not have functions', done => {
|
||||
mockClient.fs = {};
|
||||
|
||||
cmdFn()
|
||||
cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
done();
|
||||
@@ -49,7 +48,7 @@ describe(CMD, function () {
|
||||
mockClient.fs.chmod.restore();
|
||||
sandbox.stub(mockClient.fs, 'chmod').rejects(new Error('test'));
|
||||
|
||||
cmdFn({log: mockLog, command: {arg: '777 test'}})
|
||||
cmdFn(mockClient, {arg: '777 test'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(500);
|
||||
done();
|
||||
@@ -58,7 +57,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('777 test // successful', done => {
|
||||
cmdFn({log: mockLog, command: {arg: '777 test'}})
|
||||
cmdFn(mockClient, {arg: '777 test'})
|
||||
.then(() => {
|
||||
expect(mockClient.fs.chmod.args[0]).to.eql(['test', 511]);
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const bunyan = require('bunyan');
|
||||
|
||||
const siteRegistry = require('../../../../src/commands/registration/site/registry');
|
||||
const FtpCommands = require('../../../../src/commands');
|
||||
@@ -9,12 +8,11 @@ const FtpCommands = require('../../../../src/commands');
|
||||
const CMD = 'SITE';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const log = bunyan.createLogger({name: 'site-test'});
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve(),
|
||||
commands: new FtpCommands()
|
||||
};
|
||||
const cmdFn = require(`../../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -26,14 +24,14 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// unsuccessful', () => {
|
||||
return cmdFn({log})
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(502);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful', () => {
|
||||
return cmdFn({log, command: {arg: 'BAD'}})
|
||||
return cmdFn(mockClient, {arg: 'BAD'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(502);
|
||||
});
|
||||
@@ -42,7 +40,7 @@ describe(CMD, function () {
|
||||
it('// successful', () => {
|
||||
sandbox.stub(siteRegistry.CHMOD, 'handler').resolves();
|
||||
|
||||
return cmdFn({log, command: {arg: 'CHMOD test'}})
|
||||
return cmdFn(mockClient, {arg: 'CHMOD test'})
|
||||
.then(() => {
|
||||
const {command} = siteRegistry.CHMOD.handler.args[0][0];
|
||||
expect(command.directive).to.equal('CHMOD');
|
||||
|
||||
@@ -5,9 +5,8 @@ const sinon = require('sinon');
|
||||
const CMD = 'SIZE';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockLog = {error: () => {}};
|
||||
const mockClient = {reply: () => Promise.resolve()};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -25,7 +24,7 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
@@ -34,7 +33,7 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
@@ -43,14 +42,14 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | file get fails', () => {
|
||||
sandbox.stub(mockClient.fs, 'get').rejects(new Error('test'));
|
||||
|
||||
return cmdFn({log: mockLog, command: {arg: 'test'}})
|
||||
return cmdFn(mockClient, {arg: 'test'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn({command: {arg: 'test'}})
|
||||
return cmdFn(mockClient, {arg: 'test'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(213);
|
||||
});
|
||||
|
||||
@@ -5,9 +5,8 @@ const sinon = require('sinon');
|
||||
const CMD = 'STAT';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockLog = {error: () => {}};
|
||||
const mockClient = {reply: () => Promise.resolve()};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -24,7 +23,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(211);
|
||||
});
|
||||
@@ -33,7 +32,7 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
|
||||
return cmdFn({command: {arg: 'test'}})
|
||||
return cmdFn(mockClient, {arg: 'test'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
@@ -42,7 +41,7 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
|
||||
return cmdFn({command: {arg: 'test'}})
|
||||
return cmdFn(mockClient, {arg: 'test'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
@@ -51,7 +50,7 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | file get fails', () => {
|
||||
sandbox.stub(mockClient.fs, 'get').rejects(new Error('test'));
|
||||
|
||||
return cmdFn({log: mockLog, command: {arg: 'test'}})
|
||||
return cmdFn(mockClient, {arg: 'test'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(450);
|
||||
});
|
||||
@@ -77,7 +76,7 @@ describe(CMD, function () {
|
||||
isDirectory: () => false
|
||||
});
|
||||
|
||||
return cmdFn({command: {arg: 'test'}})
|
||||
return cmdFn(mockClient, {arg: 'test'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(212);
|
||||
});
|
||||
@@ -122,7 +121,7 @@ describe(CMD, function () {
|
||||
isDirectory: () => true
|
||||
});
|
||||
|
||||
return cmdFn({command: {arg: 'test'}})
|
||||
return cmdFn(mockClient, {arg: 'test'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(213);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const Promise = require('bluebird');
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const EventEmitter = require('events');
|
||||
@@ -7,7 +6,6 @@ const EventEmitter = require('events');
|
||||
const CMD = 'STOR';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
let emitter;
|
||||
const mockClient = {
|
||||
commandSocket: {
|
||||
@@ -22,7 +20,7 @@ describe(CMD, function () {
|
||||
end: () => {}
|
||||
}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -43,7 +41,7 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
@@ -52,7 +50,7 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
@@ -63,7 +61,7 @@ describe(CMD, function () {
|
||||
return Promise.reject(new Promise.TimeoutError());
|
||||
});
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'}})
|
||||
return cmdFn(mockClient, {arg: 'test.txt'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(425);
|
||||
});
|
||||
@@ -74,7 +72,7 @@ describe(CMD, function () {
|
||||
return Promise.reject(new Error('test'));
|
||||
});
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'}})
|
||||
return cmdFn(mockClient, {arg: 'test.txt'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
@@ -90,7 +88,7 @@ describe(CMD, function () {
|
||||
errorEmitted = !!err;
|
||||
});
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'}})
|
||||
return cmdFn(mockClient, {arg: 'test.txt'})
|
||||
.then(() => {
|
||||
expect(errorEmitted).to.equal(true);
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ describe(CMD, function () {
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -33,7 +33,7 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
@@ -42,7 +42,7 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
@@ -52,7 +52,7 @@ describe(CMD, function () {
|
||||
mockClient.fs.get.restore();
|
||||
sandbox.stub(mockClient.fs, 'get').rejects({});
|
||||
|
||||
return cmdFn({command: {arg: 'good'}})
|
||||
return cmdFn(mockClient, {arg: 'good'})
|
||||
.then(() => {
|
||||
const call = stor.handler.call.args[0][1];
|
||||
expect(call).to.have.property('command');
|
||||
@@ -63,7 +63,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// successful | generates unique name', () => {
|
||||
return cmdFn({command: {arg: 'bad'}})
|
||||
return cmdFn(mockClient, {arg: 'bad'})
|
||||
.then(() => {
|
||||
const call = stor.handler.call.args[0][1];
|
||||
expect(call).to.have.property('command');
|
||||
|
||||
@@ -8,7 +8,7 @@ describe(CMD, function () {
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -20,14 +20,14 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn({command: {arg: 'F'}})
|
||||
return cmdFn(mockClient, {arg: 'F'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful', () => {
|
||||
return cmdFn({command: {arg: 'X'}})
|
||||
return cmdFn(mockClient, {arg: 'X'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504);
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ describe(CMD, function () {
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -20,7 +20,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn()
|
||||
return cmdFn(mockClient)
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(215);
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ describe(CMD, function () {
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handle;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -21,7 +21,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('A // successful', () => {
|
||||
return cmdFn({command: {arg: 'A'}})
|
||||
return cmdFn(mockClient, {arg: 'A'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
expect(mockClient.transferType).to.equal('ascii');
|
||||
|
||||
@@ -5,15 +5,12 @@ const sinon = require('sinon');
|
||||
const CMD = 'USER';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockLog = {
|
||||
error: () => {}
|
||||
};
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve(),
|
||||
server: {options: {}},
|
||||
login: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler;
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@@ -29,7 +26,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('test // successful | prompt for password', () => {
|
||||
return cmdFn({command: {arg: 'test'}})
|
||||
return cmdFn(mockClient, {arg: 'test'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(331);
|
||||
});
|
||||
@@ -38,7 +35,7 @@ describe(CMD, function () {
|
||||
it('test // successful | anonymous login', () => {
|
||||
mockClient.server.options = {anonymous: true};
|
||||
|
||||
return cmdFn({command: {arg: 'anonymous'}})
|
||||
return cmdFn(mockClient, {arg: 'anonymous'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(230);
|
||||
expect(mockClient.login.callCount).to.equal(1);
|
||||
@@ -46,7 +43,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('test // unsuccessful | no username provided', () => {
|
||||
return cmdFn({command: { }})
|
||||
return cmdFn(mockClient, { })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501);
|
||||
expect(mockClient.login.callCount).to.equal(0);
|
||||
@@ -56,7 +53,7 @@ describe(CMD, function () {
|
||||
it('test // unsuccessful | already set username', () => {
|
||||
mockClient.username = 'test';
|
||||
|
||||
return cmdFn({command: {arg: 'test'}})
|
||||
return cmdFn(mockClient, {arg: 'test'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(530);
|
||||
expect(mockClient.login.callCount).to.equal(0);
|
||||
@@ -66,7 +63,7 @@ describe(CMD, function () {
|
||||
it('test // successful | regular login if anonymous is true', () => {
|
||||
mockClient.server.options = {anonymous: true};
|
||||
|
||||
return cmdFn({log: mockLog, command: {arg: 'test'}})
|
||||
return cmdFn(mockClient, {arg: 'test'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(331);
|
||||
expect(mockClient.login.callCount).to.equal(0);
|
||||
@@ -76,7 +73,7 @@ describe(CMD, function () {
|
||||
it('test // successful | anonymous login with set username', () => {
|
||||
mockClient.server.options = {anonymous: 'sillyrabbit'};
|
||||
|
||||
return cmdFn({log: mockLog, command: {arg: 'sillyrabbit'}})
|
||||
return cmdFn(mockClient, {arg: 'sillyrabbit'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(230);
|
||||
expect(mockClient.login.callCount).to.equal(1);
|
||||
@@ -88,7 +85,7 @@ describe(CMD, function () {
|
||||
mockClient.login.restore();
|
||||
sandbox.stub(mockClient, 'login').rejects(new Error('test'));
|
||||
|
||||
return cmdFn({log: mockLog, command: {arg: 'anonymous'}})
|
||||
return cmdFn(mockClient, {arg: 'anonymous'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(530);
|
||||
expect(mockClient.login.callCount).to.equal(1);
|
||||
@@ -98,7 +95,7 @@ describe(CMD, function () {
|
||||
it('test // successful | does not login if already authenticated', () => {
|
||||
mockClient.authenticated = true;
|
||||
|
||||
return cmdFn({log: mockLog, command: {arg: 'sillyrabbit'}})
|
||||
return cmdFn(mockClient, {arg: 'sillyrabbit'})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(230);
|
||||
expect(mockClient.login.callCount).to.equal(0);
|
||||
|
||||
@@ -4,7 +4,6 @@ const sinon = require('sinon');
|
||||
|
||||
const Promise = require('bluebird');
|
||||
const net = require('net');
|
||||
const bunyan = require('bunyan');
|
||||
|
||||
const PassiveConnector = require('../../src/connector/passive');
|
||||
|
||||
@@ -14,7 +13,6 @@ describe('Connector - Passive //', function () {
|
||||
reply: () => Promise.resolve({}),
|
||||
close: () => Promise.resolve({}),
|
||||
encoding: 'utf8',
|
||||
log: bunyan.createLogger({name: 'passive-test'}),
|
||||
commandSocket: {},
|
||||
server: {options: {}}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* eslint no-unused-expressions: 0 */
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const bunyan = require('bunyan');
|
||||
const Promise = require('bluebird');
|
||||
const _ = require('lodash');
|
||||
const fs = require('fs');
|
||||
@@ -15,7 +14,6 @@ describe('Integration', function () {
|
||||
|
||||
let client;
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: 'test-runner'});
|
||||
let server;
|
||||
|
||||
let connection;
|
||||
@@ -38,7 +36,6 @@ describe('Integration', function () {
|
||||
|
||||
function startServer(url, options = {}) {
|
||||
server = new FtpServer(url, _.assign({
|
||||
log,
|
||||
pasv_range: 8881,
|
||||
greeting: ['hello', 'world'],
|
||||
anonymous: true
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
require('dotenv').load();
|
||||
const bunyan = require('bunyan');
|
||||
|
||||
const FtpServer = require('../src');
|
||||
|
||||
const log = bunyan.createLogger({name: 'test'});
|
||||
log.level('trace');
|
||||
const server = new FtpServer('ftp://127.0.0.1:8880', {
|
||||
log,
|
||||
pasv_range: 8881,
|
||||
greeting: ['Welcome', 'to', 'the', 'jungle!'],
|
||||
tls: {
|
||||
|
||||
Reference in New Issue
Block a user