Compare commits

..

11 Commits

Author SHA1 Message Date
Tyler Stewart
a308a33491 chore: update readme 2018-12-21 16:10:34 -07:00
Tyler Stewart
191ad5507c test: update sinon sandbox creation 2018-12-21 16:07:34 -07:00
Tyler Stewart
281d147b96 chore(package): update scripts 2018-12-21 16:03:13 -07:00
Tyler Stewart
52958ffd9f chore: update packages 2018-12-21 16:00:12 -07:00
Tyler Stewart
3f5d548634 chore(circle): update config 2018-12-21 15:57:04 -07:00
Tyler Stewart
ab085a1bca chore: remove npm-run-all
Late to the party, but removes dev dependency `npm-run-all` to avoid malicious package
2018-12-21 15:49:38 -07:00
Robert Kieffer
a5f26480e5 fix(cli): correct --root flag logic (#135)
Fixes #134
2018-12-21 22:44:13 +00:00
Mike Estes
e41b04be46 fix: add pasv_url to typescript definitions (#131) 2018-11-19 09:49:43 -07:00
Tyler Stewart
7acf861a4d fix(cli): correctly setup server, add pasv options (#130) 2018-11-19 09:48:24 -07:00
Tyler Stewart
4801ecc0cc fix: correct timeouts around TLS data connection (#128)
* fix: timeouts when using tls

* fix: correct tls connection

* fix(connector): dont prematurely destroy socket

* fix(passive): set connected if not tls

* refactor: dont return promises on connector end

Since we're not waiting, we don't need to return promises
2018-11-12 03:19:57 +00:00
Qian.Sicheng
8e34e4c71a fix: correct type definitions (#125)
* fix types of server options

* fix usage of server options
2018-11-07 08:29:37 -07:00
85 changed files with 763 additions and 979 deletions

View File

@@ -23,7 +23,7 @@ base-build: &base-build
- node_modules
- run:
name: Lint
command: npm run verify:js
command: npm run verify -- --silent
- run:
name: Test
command: npm run test:once

View File

@@ -36,6 +36,8 @@
## Overview
`ftp-srv` is a modern and extensible FTP server designed to be simple yet configurable.
You can use `ftp-srv` to traverse the file system on the server, but it's biggest strength comes from it's customizable file system. This allows you to serve a custom, dynamic, or unique file system to users. You can even server a different system depending on the user connecting.
## Features
- Extensible [file systems](#file-system) per connection
- Passive and active transfers
@@ -51,7 +53,7 @@
// Quick start
const FtpSrv = require('ftp-srv');
const ftpServer = new FtpSrv('ftp://0.0.0.0:9876', { options ... });
const ftpServer = new FtpSrv({ options ... });
ftpServer.on('login', (data, resolve, reject) => { ... });
...
@@ -124,6 +126,11 @@ __Allowable values:__
#### `log`
A [bunyan logger](https://github.com/trentm/node-bunyan) instance. Created by default.
Piping the output into bunyan will format logs nicely, eg:
```
$ node ./test/start.js | npx bunyan
```
## CLI
`ftp-srv` also comes with a builtin CLI.

View File

@@ -19,7 +19,8 @@ function setupYargs() {
})
.option('username', {
describe: 'Blank for anonymous',
type: 'string'
type: 'string',
default: ''
})
.option('password', {
describe: 'Password for given username',
@@ -36,6 +37,20 @@ function setupYargs() {
boolean: true,
default: false
})
.option('pasv_url', {
describe: 'URL to provide for passive connections',
type: 'string'
})
.option('pasv_min', {
describe: 'Starting point to use when creating passive connections',
type: 'number',
default: 1024
})
.option('pasv_max', {
describe: 'Ending port to use when creating passive connections',
type: 'number',
default: 65535
})
.parse();
}
@@ -46,15 +61,18 @@ function setupState(_args) {
if (_args._ && _args._.length > 0) {
_state.url = _args._[0];
}
_state.pasv_url = _args.pasv_url;
_state.pasv_min = _args.pasv_min;
_state.pasv_max = _args.pasv_max;
_state.anonymous = _args.username === '';
}
function setupRoot() {
const dirPath = _args.root;
if (dirPath) {
_state.root = process.cwd();
} else {
_state.root = dirPath;
} else {
_state.root = process.cwd();
}
}
@@ -105,7 +123,11 @@ function startFtpServer(_state) {
return reject(new errors.GeneralError('Invalid username or password', 401));
}
const ftpServer = new FtpSrv(_state.url, {
const ftpServer = new FtpSrv({
url: _state.url,
pasv_url: _state.pasv_url,
pasv_min: _state.pasv_min,
pasv_max: _state.pasv_max,
anonymous: _state.anonymous,
blacklist: _state.blacklist
});

View File

@@ -136,7 +136,7 @@
"comma-dangle": 1,
"new-cap": 2,
"new-parens": 2,
"arrow-parens": [2, "as-needed"],
"arrow-parens": [2, "always"],
"no-array-constructor": 2,
"array-callback-return": 1,
"no-extra-parens": 2,

9
ftp-srv.d.ts vendored
View File

@@ -59,18 +59,21 @@ export class FtpConnection extends EventEmitter {
}
export interface FtpServerOptions {
pasv_range?: number | string,
url?: string,
pasv_min?: number,
pasv_max?: number,
pasv_url?: string,
greeting?: string | string[],
tls?: tls.SecureContext | false,
anonymous?: boolean,
blacklist?: Array<string>,
whitelist?: Array<string>,
file_format?: (stat: Stats) => string | Promise<string> | "ls" | "ep",
log?: any
log?: any,
}
export class FtpServer extends EventEmitter {
constructor(url: string, options?: FtpServerOptions);
constructor(options?: FtpServerOptions);
readonly isTLS: boolean;

1169
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -28,14 +28,12 @@
"pre-release": "npm run verify",
"commitmsg": "cz-customizable-ghooks",
"dev": "cross-env NODE_ENV=development npm run verify:watch",
"prepush": "npm-run-all verify test:once --silent",
"prepush": "npm run verify && npm run test:once --silent",
"semantic-release": "semantic-release",
"start": "npm run dev",
"test": "cross-env NODE_ENV=test mocha --opts config/testUnit/mocha.opts -w",
"test:once": "cross-env NODE_ENV=test mocha --opts config/testUnit/mocha.opts",
"verify": "npm run verify:js --silent",
"verify:js": "eslint -c config/verify/.eslintrc \"src/**/*.js\" \"test/**/*.js\" \"config/**/*.js\" && echo ✅ verify:js success",
"verify:js:fix": "eslint --fix -c config/verify/.eslintrc \"src/**/*.js\" \"test/**/*.js\" \"config/**/*.js\" && echo ✅ verify:js:fix success"
"verify": "eslint -c config/verify/.eslintrc \"src/**/*.js\" \"test/**/*.js\""
},
"release": {
"verifyConditions": "condition-circle"
@@ -53,32 +51,30 @@
"bunyan": "^1.8.12",
"ip": "^1.1.5",
"lodash": "^4.17.10",
"moment": "^2.22.1",
"uuid": "^3.2.1",
"yargs": "^11.0.0"
"moment": "^2.22.2",
"uuid": "^3.3.2",
"yargs": "^12.0.1"
},
"devDependencies": {
"@icetee/ftp": "^1.0.2",
"chai": "^4.0.2",
"condition-circle": "^1.6.0",
"cross-env": "3.1.4",
"@icetee/ftp": "^1.0.3",
"chai": "^4.1.2",
"condition-circle": "^2.0.1",
"cross-env": "5.2.0",
"cz-customizable": "5.2.0",
"cz-customizable-ghooks": "1.5.0",
"dotenv": "^4.0.0",
"eslint": "4.5.0",
"eslint-config-google": "0.8.0",
"eslint-friendly-formatter": "3.0.0",
"eslint-plugin-mocha": "^4.11.0",
"eslint-plugin-node": "5.1.1",
"husky": "0.13.3",
"eslint": "5.3.0",
"eslint-config-google": "0.9.1",
"eslint-friendly-formatter": "4.0.1",
"eslint-plugin-mocha": "^5.1.0",
"eslint-plugin-node": "7.0.1",
"husky": "0.14.3",
"istanbul": "0.4.5",
"mocha": "^5.2.0",
"mocha-junit-reporter": "1.13.0",
"mocha-multi-reporters": "1.1.5",
"npm-run-all": "^4.1.3",
"rimraf": "2.6.1",
"semantic-release": "^15.10.6",
"sinon": "^2.3.5"
"mocha-junit-reporter": "1.18.0",
"mocha-multi-reporters": "1.1.7",
"rimraf": "2.6.2",
"semantic-release": "^15.9.8",
"sinon": "^6.1.5"
},
"engines": {
"node": ">=6.x",

View File

@@ -9,8 +9,8 @@ class FtpCommands {
constructor(connection) {
this.connection = connection;
this.previousCommand = {};
this.blacklist = _.get(this.connection, 'server.options.blacklist', []).map(cmd => _.upperCase(cmd));
this.whitelist = _.get(this.connection, 'server.options.whitelist', []).map(cmd => _.upperCase(cmd));
this.blacklist = _.get(this.connection, 'server.options.blacklist', []).map((cmd) => _.upperCase(cmd));
this.whitelist = _.get(this.connection, 'server.options.whitelist', []).map((cmd) => _.upperCase(cmd));
}
parse(message) {

View File

@@ -2,7 +2,7 @@ module.exports = {
directive: 'ABOR',
handler: function () {
return this.connector.waitForConnection()
.then(socket => {
.then((socket) => {
return this.reply(426, {socket})
.then(() => this.reply(226));
})

View File

@@ -30,7 +30,7 @@ function handleTLS() {
isServer: true,
secureContext
});
['data', 'timeout', 'end', 'close', 'drain', 'error'].forEach(event => {
['data', 'timeout', 'end', 'close', 'drain', 'error'].forEach((event) => {
function forwardEvent() {
this.emit.apply(this, arguments);
}

View File

@@ -8,11 +8,11 @@ module.exports = {
if (!this.fs.chdir) return this.reply(402, 'Not supported by file system');
return Promise.try(() => this.fs.chdir(command.arg))
.then(cwd => {
.then((cwd) => {
const path = cwd ? `"${escapePath(cwd)}"` : undefined;
return this.reply(250, path);
})
.catch(err => {
.catch((err) => {
log.error(err);
return this.reply(550, err.message);
});

View File

@@ -10,7 +10,7 @@ module.exports = {
.then(() => {
return this.reply(250);
})
.catch(err => {
.catch((err) => {
log.error(err);
return this.reply(550, err.message);
});

View File

@@ -5,7 +5,7 @@ module.exports = {
handler: function () {
this.connector = new PassiveConnector(this);
return this.connector.setupServer()
.then(server => {
.then((server) => {
const {port} = server.address();
return this.reply(229, `EPSV OK (|||${port}|)`);

View File

@@ -11,7 +11,7 @@ module.exports = {
return feats;
}, ['UTF8'])
.sort()
.map(feat => ({
.map((feat) => ({
message: ` ${feat}`,
raw: true
}));

View File

@@ -12,7 +12,7 @@ module.exports = {
const reply = _.concat([syntax.replace('{{cmd}}', directive), description]);
return this.reply(214, ...reply);
} else {
const supportedCommands = _.chunk(Object.keys(registry), 5).map(chunk => chunk.join('\t'));
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.');
}
},

View File

@@ -17,14 +17,14 @@ module.exports = {
return this.connector.waitForConnection()
.tap(() => this.commandSocket.pause())
.then(() => Promise.try(() => this.fs.get(path)))
.then(stat => stat.isDirectory() ? Promise.try(() => this.fs.list(path)) : [stat])
.then(files => {
const getFileMessage = file => {
.then((stat) => stat.isDirectory() ? Promise.try(() => this.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 Promise.try(() => files.map(file => {
return Promise.try(() => files.map((file) => {
const message = getFileMessage(file);
return {
raw: true,
@@ -34,19 +34,22 @@ module.exports = {
}));
})
.tap(() => this.reply(150))
.then(fileList => {
.then((fileList) => {
if (fileList.length) return this.reply({}, ...fileList);
})
.tap(() => this.reply(226))
.catch(Promise.TimeoutError, err => {
.catch(Promise.TimeoutError, (err) => {
log.error(err);
return this.reply(425, 'No connection established');
})
.catch(err => {
.catch((err) => {
log.error(err);
return this.reply(451, err.message || 'No directory');
})
.finally(() => this.connector.end().then(() => this.commandSocket.resume()));
.finally(() => {
this.connector.end();
this.commandSocket.resume();
});
},
syntax: '{{cmd}} [<path>]',
description: 'Returns information of a file or directory if specified, else information of the current working directory is returned'

View File

@@ -8,11 +8,11 @@ module.exports = {
if (!this.fs.get) return this.reply(402, 'Not supported by file system');
return Promise.try(() => this.fs.get(command.arg))
.then(fileStat => {
.then((fileStat) => {
const modificationTime = moment.utc(fileStat.mtime).format('YYYYMMDDHHmmss.SSS');
return this.reply(213, modificationTime);
})
.catch(err => {
.catch((err) => {
log.error(err);
return this.reply(550, err.message);
});

View File

@@ -8,11 +8,11 @@ module.exports = {
if (!this.fs.mkdir) return this.reply(402, 'Not supported by file system');
return Promise.try(() => this.fs.mkdir(command.arg))
.then(dir => {
.then((dir) => {
const path = dir ? `"${escapePath(dir)}"` : undefined;
return this.reply(257, path);
})
.catch(err => {
.catch((err) => {
log.error(err);
return this.reply(550, err.message);
});

View File

@@ -12,7 +12,7 @@ module.exports = {
.then(() => {
return this.reply(230);
})
.catch(err => {
.catch((err) => {
log.error(err);
return this.reply(530, err.message || 'Authentication failed');
});

View File

@@ -5,7 +5,7 @@ module.exports = {
handler: function () {
this.connector = new PassiveConnector(this);
return this.connector.setupServer()
.then(server => {
.then((server) => {
const address = this.server.options.pasv_url;
const {port} = server.address();
const host = address.replace(/\./g, ',');

View File

@@ -10,7 +10,7 @@ module.exports = {
if (rawConnection.length !== 6) return this.reply(425);
const ip = rawConnection.slice(0, 4).join('.');
const portBytes = rawConnection.slice(4).map(p => parseInt(p));
const portBytes = rawConnection.slice(4).map((p) => parseInt(p));
const port = portBytes[0] * 256 + portBytes[1];
return this.connector.setupConnection(ip, port)

View File

@@ -8,11 +8,11 @@ module.exports = {
if (!this.fs.currentDirectory) return this.reply(402, 'Not supported by file system');
return Promise.try(() => this.fs.currentDirectory())
.then(cwd => {
.then((cwd) => {
const path = cwd ? `"${escapePath(cwd)}"` : undefined;
return this.reply(257, path);
})
.catch(err => {
.catch((err) => {
log.error(err);
return this.reply(550, err.message);
});

View File

@@ -11,7 +11,7 @@ module.exports = {
return this.connector.waitForConnection()
.tap(() => this.commandSocket.pause())
.then(() => Promise.try(() => this.fs.read(filePath, {start: this.restByteCount})))
.then(fsResponse => {
.then((fsResponse) => {
let {stream, clientPath} = fsResponse;
if (!stream && !clientPath) {
stream = fsResponse;
@@ -19,13 +19,13 @@ module.exports = {
}
const serverPath = stream.path || filePath;
const destroyConnection = (connection, reject) => err => {
const destroyConnection = (connection, reject) => (err) => {
if (connection) connection.destroy(err);
reject(err);
};
const eventsPromise = new Promise((resolve, reject) => {
stream.on('data', data => {
stream.on('data', (data) => {
if (stream) stream.pause();
if (this.connector.socket) {
this.connector.socket.write(data, this.transferType, () => stream && stream.resume());
@@ -45,16 +45,19 @@ module.exports = {
.then(() => this.reply(226, clientPath))
.finally(() => stream.destroy && stream.destroy());
})
.catch(Promise.TimeoutError, err => {
.catch(Promise.TimeoutError, (err) => {
log.error(err);
return this.reply(425, 'No connection established');
})
.catch(err => {
.catch((err) => {
log.error(err);
this.emit('RETR', err);
return this.reply(551, err.message);
})
.finally(() => this.connector.end().then(() => this.commandSocket.resume()));
.finally(() => {
this.connector.end();
this.commandSocket.resume();
});
},
syntax: '{{cmd}} <path>',
description: 'Retrieve a copy of the file'

View File

@@ -12,7 +12,7 @@ module.exports = {
this.renameFrom = fileName;
return this.reply(350);
})
.catch(err => {
.catch((err) => {
log.error(err);
return this.reply(550, err.message);
});

View File

@@ -15,7 +15,7 @@ module.exports = {
.then(() => {
return this.reply(250);
})
.catch(err => {
.catch((err) => {
log.error(err);
return this.reply(550, err.message);
})

View File

@@ -10,7 +10,7 @@ module.exports = function ({log, command} = {}) {
.then(() => {
return this.reply(200);
})
.catch(err => {
.catch((err) => {
log.error(err);
return this.reply(500);
});

View File

@@ -7,10 +7,10 @@ module.exports = {
if (!this.fs.get) return this.reply(402, 'Not supported by file system');
return Promise.try(() => this.fs.get(command.arg))
.then(fileStat => {
.then((fileStat) => {
return this.reply(213, {message: fileStat.size});
})
.catch(err => {
.catch((err) => {
log.error(err);
return this.reply(550, err.message);
});

View File

@@ -12,27 +12,27 @@ module.exports = {
if (!this.fs.get) return this.reply(402, 'Not supported by file system');
return Promise.try(() => this.fs.get(path))
.then(stat => {
.then((stat) => {
if (stat.isDirectory()) {
if (!this.fs.list) return this.reply(402, 'Not supported by file system');
return Promise.try(() => this.fs.list(path))
.then(stats => [213, stats]);
.then((stats) => [213, stats]);
}
return [212, [stat]];
})
.then(([code, fileStats]) => {
return Promise.map(fileStats, file => {
return Promise.map(fileStats, (file) => {
const message = getFileStat(file, _.get(this, 'server.options.file_format', 'ls'));
return {
raw: true,
message
};
})
.then(messages => [code, messages]);
.then((messages) => [code, messages]);
})
.then(([code, messages]) => this.reply(code, 'Status begin', ...messages, 'Status end'))
.catch(err => {
.catch((err) => {
log.error(err);
return this.reply(450, err.message);
});

View File

@@ -12,7 +12,7 @@ module.exports = {
return this.connector.waitForConnection()
.tap(() => this.commandSocket.pause())
.then(() => Promise.try(() => this.fs.write(fileName, {append, start: this.restByteCount})))
.then(fsResponse => {
.then((fsResponse) => {
let {stream, clientPath} = fsResponse;
if (!stream && !clientPath) {
stream = fsResponse;
@@ -20,7 +20,7 @@ module.exports = {
}
const serverPath = stream.path || fileName;
const destroyConnection = (connection, reject) => err => {
const destroyConnection = (connection, reject) => (err) => {
if (connection) {
if (connection.writeable) connection.end();
connection.destroy(err);
@@ -34,13 +34,13 @@ module.exports = {
});
const socketPromise = new Promise((resolve, reject) => {
this.connector.socket.on('data', data => {
this.connector.socket.on('data', (data) => {
if (this.connector.socket) this.connector.socket.pause();
if (stream) {
stream.write(data, this.transferType, () => this.connector.socket && this.connector.socket.resume());
}
});
this.connector.socket.once('close', () => {
this.connector.socket.once('end', () => {
if (stream.listenerCount('close')) stream.emit('close');
else stream.end();
resolve();
@@ -56,16 +56,19 @@ module.exports = {
.then(() => this.reply(226, clientPath))
.finally(() => stream.destroy && stream.destroy());
})
.catch(Promise.TimeoutError, err => {
.catch(Promise.TimeoutError, (err) => {
log.error(err);
return this.reply(425, 'No connection established');
})
.catch(err => {
.catch((err) => {
log.error(err);
this.emit('STOR', err);
return this.reply(550, err.message);
})
.finally(() => this.connector.end().then(() => this.commandSocket.resume()));
.finally(() => {
this.connector.end();
this.commandSocket.resume();
});
},
syntax: '{{cmd}} <path>',
description: 'Store data as a file at the server site'

View File

@@ -11,7 +11,7 @@ module.exports = {
return Promise.try(() => this.fs.get(fileName))
.then(() => Promise.try(() => this.fs.getUniqueName()))
.catch(() => fileName)
.then(name => {
.then((name) => {
args.command.arg = name;
return stor.call(this, args);
});

View File

@@ -13,7 +13,7 @@ module.exports = {
.then(() => {
return this.reply(230);
})
.catch(err => {
.catch((err) => {
log.error(err);
return this.reply(530, err.message || 'Authentication failed');
});

View File

@@ -43,7 +43,7 @@ const commands = [
const registry = commands.reduce((result, cmd) => {
const aliases = Array.isArray(cmd.directive) ? cmd.directive : [cmd.directive];
aliases.forEach(alias => result[alias] = cmd);
aliases.forEach((alias) => result[alias] = cmd);
return result;
}, {});

View File

@@ -25,7 +25,7 @@ class FtpConnection extends EventEmitter {
this.connector = new BaseConnector(this);
this.commandSocket = options.socket;
this.commandSocket.on('error', err => {
this.commandSocket.on('error', (err) => {
this.log.error(err, 'Client error');
this.server.emit('client-error', {connection: this, context: 'commandSocket', error: err});
});
@@ -41,7 +41,7 @@ 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));
return Promise.mapSeries(messages, (message) => this.commands.handle(message));
}
get ip() {
@@ -68,7 +68,7 @@ class FtpConnection extends EventEmitter {
close(code = 421, message = 'Closing connection') {
return Promise.resolve(code)
.then(_code => _code && this.reply(_code, message))
.then((_code) => _code && this.reply(_code, message))
.then(() => this.commandSocket && this.commandSocket.end());
}
@@ -96,7 +96,7 @@ class FtpConnection extends EventEmitter {
if (!letters.length) letters = [{}];
return Promise.map(letters, (promise, index) => {
return Promise.resolve(promise)
.then(letter => {
.then((letter) => {
if (!letter) letter = {};
else if (typeof letter === 'string') letter = {message: letter}; // allow passing in message as first param
@@ -104,7 +104,7 @@ class FtpConnection extends EventEmitter {
if (!letter.message) letter.message = DEFAULT_MESSAGE[options.code] || 'No information';
if (!letter.encoding) letter.encoding = this.encoding;
return Promise.resolve(letter.message) // allow passing in a promise as a message
.then(message => {
.then((message) => {
const seperator = !options.hasOwnProperty('eol') ?
letters.length - 1 === index ? ' ' : '-' :
options.eol ? ' ' : '-';
@@ -116,11 +116,11 @@ class FtpConnection extends EventEmitter {
});
};
const processLetter = letter => {
const processLetter = (letter) => {
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');
letter.socket.write(letter.message + '\r\n', letter.encoding, err => {
letter.socket.write(letter.message + '\r\n', letter.encoding, (err) => {
if (err) {
this.log.error(err);
return reject(err);
@@ -132,10 +132,10 @@ class FtpConnection extends EventEmitter {
};
return satisfyParameters()
.then(satisfiedLetters => Promise.mapSeries(satisfiedLetters, (letter, index) => {
.then((satisfiedLetters) => Promise.mapSeries(satisfiedLetters, (letter, index) => {
return processLetter(letter, index);
}))
.catch(err => {
.catch((err) => {
this.log.error(err);
});
}

View File

@@ -29,7 +29,7 @@ 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.server && this.server.emit('client-error', {connection: this.connection, context: 'dataSocket', error: err}));
this.dataSocket.connect({host, port, family}, () => {
this.dataSocket.pause();

View File

@@ -26,28 +26,28 @@ class Connector {
return Promise.reject(new errors.ConnectorError('No connector setup, send PASV or PORT'));
}
end() {
const closeDataSocket = new Promise(resolve => {
if (this.dataSocket) {
this.dataSocket.end().destroy();
this.dataSocket = null;
}
resolve();
});
const closeDataServer = new Promise(resolve => {
if (this.dataServer) {
this.dataServer.close(() => resolve());
} else resolve();
});
return Promise.all([closeDataSocket, closeDataServer])
.then(() => {
closeSocket() {
if (this.dataSocket) {
const socket = this.dataSocket;
this.dataSocket.end(() => socket.destroy());
this.dataSocket = null;
this.dataServer = null;
this.type = false;
}
}
this.connection.connector = new Connector(this);
});
closeServer() {
if (this.dataServer) {
this.dataServer.close();
this.dataServer = null;
}
}
end() {
this.closeSocket();
this.closeServer();
this.type = false;
this.connection.connector = new Connector(this);
}
}
module.exports = Connector;

View File

@@ -12,7 +12,7 @@ class Passive extends Connector {
this.type = 'passive';
}
waitForConnection({timeout = 5000, delay = 250} = {}) {
waitForConnection({timeout = 5000, delay = 50} = {}) {
if (!this.dataServer) return Promise.reject(new errors.ConnectorError('Passive server not setup'));
const checkSocket = () => {
@@ -27,14 +27,10 @@ class Passive extends Connector {
}
setupServer() {
const closeExistingServer = () => this.dataServer ?
new Promise(resolve => this.dataServer.close(() => resolve())) :
Promise.resolve();
return closeExistingServer()
.then(() => this.server.getNextPasvPort())
.then(port => {
const connectionHandler = socket => {
this.closeServer();
return this.server.getNextPasvPort()
.then((port) => {
const connectionHandler = (socket) => {
if (!ip.isEqual(this.connection.commandSocket.remoteAddress, socket.remoteAddress)) {
this.log.error({
pasv_connection: socket.remoteAddress,
@@ -48,13 +44,12 @@ class Passive extends Connector {
this.log.trace({port, remoteAddress: socket.remoteAddress}, 'Passive connection fulfilled.');
this.dataSocket = socket;
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.once('close', () => {
this.log.trace('Passive connection closed');
this.end();
});
this.dataSocket.on('error', (err) => this.server && this.server.emit('client-error', {connection: this.connection, context: 'dataSocket', error: err}));
if (!this.connection.secure) {
this.dataSocket.connected = true;
}
};
this.dataSocket = null;
@@ -63,14 +58,20 @@ class Passive extends Connector {
this.dataServer = (this.connection.secure ? tls : net).createServer(serverOptions, 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.server && this.server.emit('client-error', {connection: this.connection, context: 'dataServer', error: err}));
this.dataServer.once('close', () => {
this.log.trace('Passive server closed');
this.end();
});
if (this.connection.secure) {
this.dataServer.on('secureConnection', (socket) => {
socket.connected = true;
});
}
return new Promise((resolve, reject) => {
this.dataServer.listen(port, this.server.url.hostname, err => {
this.dataServer.listen(port, this.server.url.hostname, (err) => {
if (err) reject(err);
else {
this.log.debug({port}, 'Passive connection listening');

View File

@@ -44,19 +44,19 @@ class FileSystem {
get(fileName) {
const {fsPath} = this._resolvePath(fileName);
return fs.statAsync(fsPath)
.then(stat => _.set(stat, 'name', fileName));
.then((stat) => _.set(stat, 'name', fileName));
}
list(path = '.') {
const {fsPath} = this._resolvePath(path);
return fs.readdirAsync(fsPath)
.then(fileNames => {
return Promise.map(fileNames, fileName => {
.then((fileNames) => {
return Promise.map(fileNames, (fileName) => {
const filePath = nodePath.join(fsPath, fileName);
return fs.accessAsync(filePath, fs.constants.F_OK)
.then(() => {
return fs.statAsync(filePath)
.then(stat => _.set(stat, 'name', fileName));
.then((stat) => _.set(stat, 'name', fileName));
})
.catch(() => null);
});
@@ -67,7 +67,7 @@ class FileSystem {
chdir(path = '.') {
const {fsPath, clientPath} = this._resolvePath(path);
return fs.statAsync(fsPath)
.tap(stat => {
.tap((stat) => {
if (!stat.isDirectory()) throw new errors.FileSystemError('Not a valid directory');
})
.then(() => {
@@ -90,7 +90,7 @@ class FileSystem {
read(fileName, {start = undefined} = {}) {
const {fsPath, clientPath} = this._resolvePath(fileName);
return fs.statAsync(fsPath)
.tap(stat => {
.tap((stat) => {
if (stat.isDirectory()) throw new errors.FileSystemError('Cannot read a directory');
})
.then(() => {
@@ -105,7 +105,7 @@ class FileSystem {
delete(path) {
const {fsPath} = this._resolvePath(path);
return fs.statAsync(fsPath)
.then(stat => {
.then((stat) => {
if (stat.isDirectory()) return fs.rmdirAsync(fsPath);
else return fs.unlinkAsync(fsPath);
});

View File

@@ -20,14 +20,14 @@ function getNextPortFactory(min, max = Infinity) {
portCheckServer.listen(nextPortNumber.next().value);
});
return () => new Promise(resolve => {
return () => new Promise((resolve) => {
portCheckServer.once('listening', () => {
const {port} = portCheckServer.address();
portCheckServer.close(() => resolve(port));
});
portCheckServer.listen(nextPortNumber.next().value);
})
.catch(RangeError, err => Promise.reject(new errors.ConnectorError(err.message)));
.catch(RangeError, (err) => Promise.reject(new errors.ConnectorError(err.message)));
}
module.exports = {

View File

@@ -8,12 +8,12 @@ module.exports = function (hostname) {
return new Promise((resolve, reject) => {
if (!hostname || hostname === '0.0.0.0') {
let ip = '';
http.get(IP_WEBSITE, response => {
http.get(IP_WEBSITE, (response) => {
if (response.statusCode !== 200) {
return reject(new errors.GeneralError('Unable to resolve hostname', response.statusCode));
}
response.setEncoding('utf8');
response.on('data', chunk => {
response.on('data', (chunk) => {
ip += chunk;
});
response.on('end', () => {

View File

@@ -39,7 +39,7 @@ class FtpServer extends EventEmitter {
_.get(this, 'options.pasv_min'),
_.get(this, 'options.pasv_max'));
const serverConnectionHandler = socket => {
const serverConnectionHandler = (socket) => {
let connection = new Connection(this, {log: this.log, socket});
this.connections[connection.id] = connection;
@@ -53,7 +53,7 @@ class FtpServer extends EventEmitter {
const serverOptions = Object.assign({}, this.isTLS ? this.options.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.error(err, '[Event] error'));
const quit = _.debounce(this.quit.bind(this), 100);
@@ -68,12 +68,12 @@ class FtpServer extends EventEmitter {
listen() {
return resolveHost(this.options.pasv_url || this.url.hostname)
.then(pasvUrl => {
.then((pasvUrl) => {
this.options.pasv_url = pasvUrl;
return new Promise((resolve, reject) => {
this.server.once('error', reject);
this.server.listen(this.url.port, this.url.hostname, err => {
this.server.listen(this.url.port, this.url.hostname, (err) => {
this.server.removeListener('error', reject);
if (err) return reject(err);
this.log.info({
@@ -112,7 +112,7 @@ class FtpServer extends EventEmitter {
}
disconnectClient(id) {
return new Promise(resolve => {
return new Promise((resolve) => {
const client = this.connections[id];
if (!client) return resolve();
delete this.connections[id];
@@ -134,9 +134,9 @@ class FtpServer extends EventEmitter {
close() {
this.log.info('Server closing...');
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 => {
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');
resolve('Closed');
});

View File

@@ -20,7 +20,7 @@ describe('FtpCommands', function () {
};
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
commands = new FtpCommands(mockConnection);

View File

@@ -15,7 +15,7 @@ describe.skip(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.spy(mockClient, 'reply');
sandbox.spy(mockClient.connector, 'waitForConnection');

View File

@@ -11,7 +11,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.spy(mockClient, 'reply');
});

View File

@@ -16,7 +16,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.spy(mockClient, 'reply');
});

View File

@@ -16,7 +16,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.spy(mockClient, 'reply');
sandbox.spy(mockClient.fs, 'chdir');

View File

@@ -13,7 +13,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.stub(mockClient, 'reply').resolves();
sandbox.stub(mockClient.fs, 'chdir').resolves();

View File

@@ -13,7 +13,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.stub(mockClient, 'reply').resolves();
sandbox.stub(mockClient.fs, 'delete').resolves();

View File

@@ -13,7 +13,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.spy(mockClient, 'reply');
sandbox.stub(ActiveConnector.prototype, 'setupConnection').resolves();

View File

@@ -13,7 +13,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.stub(mockClient, 'reply').resolves();
sandbox.stub(PassiveConnector.prototype, 'setupServer').resolves({

View File

@@ -11,7 +11,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.spy(mockClient, 'reply');
});

View File

@@ -25,7 +25,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.stub(mockClient, 'reply').resolves();
sandbox.stub(mockClient.fs, 'get').resolves({

View File

@@ -13,7 +13,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.stub(mockClient, 'reply').resolves();
sandbox.stub(mockClient.fs, 'get').resolves({mtime: 'Mon, 10 Oct 2011 23:24:11 GMT'});

View File

@@ -13,7 +13,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.stub(mockClient, 'reply').resolves();
sandbox.stub(mockClient.fs, 'mkdir').resolves();

View File

@@ -11,7 +11,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.spy(mockClient, 'reply');
});

View File

@@ -25,7 +25,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.stub(mockClient, 'reply').resolves();
sandbox.stub(mockClient.fs, 'get').resolves({

View File

@@ -11,7 +11,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.spy(mockClient, 'reply');
});

View File

@@ -11,7 +11,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.spy(mockClient, 'reply');
});

View File

@@ -15,7 +15,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.stub(mockClient, 'reply').resolves();
sandbox.stub(mockClient, 'login').resolves();

View File

@@ -12,7 +12,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.spy(mockClient, 'reply');
});

View File

@@ -13,7 +13,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.spy(mockClient, 'reply');
sandbox.stub(ActiveConnector.prototype, 'setupConnection').resolves();

View File

@@ -12,7 +12,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.spy(mockClient, 'reply');
});

View File

@@ -13,7 +13,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.stub(mockClient, 'reply').resolves();
sandbox.stub(mockClient.fs, 'currentDirectory').resolves();

View File

@@ -10,7 +10,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.stub(mockClient, 'close').resolves();
});

View File

@@ -11,7 +11,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.spy(mockClient, 'reply');
});

View File

@@ -25,7 +25,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
mockClient.fs = {
read: () => {}
@@ -87,7 +87,7 @@ describe(CMD, function () {
});
let errorEmitted = false;
emitter.once('RETR', err => {
emitter.once('RETR', (err) => {
errorEmitted = !!err;
});

View File

@@ -10,7 +10,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
mockClient.renameFrom = 'test';
mockClient.fs = {

View File

@@ -10,7 +10,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
mockClient.renameFrom = 'test';
mockClient.fs = {

View File

@@ -10,7 +10,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../../src/commands/registration/site/${CMD.toLowerCase()}`).bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
mockClient.fs = {
chmod: () => Promise.resolve()
@@ -23,7 +23,7 @@ describe(CMD, function () {
sandbox.restore();
});
it('// unsuccessful | no file system', done => {
it('// unsuccessful | no file system', (done) => {
delete mockClient.fs;
cmdFn()
@@ -34,7 +34,7 @@ describe(CMD, function () {
.catch(done);
});
it('// unsuccessful | file system does not have functions', done => {
it('// unsuccessful | file system does not have functions', (done) => {
mockClient.fs = {};
cmdFn()
@@ -45,7 +45,7 @@ describe(CMD, function () {
.catch(done);
});
it('777 test // unsuccessful | file chmod fails', done => {
it('777 test // unsuccessful | file chmod fails', (done) => {
mockClient.fs.chmod.restore();
sandbox.stub(mockClient.fs, 'chmod').rejects(new Error('test'));
@@ -57,7 +57,7 @@ describe(CMD, function () {
.catch(done);
});
it('777 test // successful', done => {
it('777 test // successful', (done) => {
cmdFn({log: mockLog, command: {arg: '777 test'}})
.then(() => {
expect(mockClient.fs.chmod.args[0]).to.eql(['test', 511]);

View File

@@ -17,7 +17,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.stub(mockClient, 'reply').resolves();
});

View File

@@ -10,7 +10,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
mockClient.fs = {
get: () => Promise.resolve({size: 1})

View File

@@ -10,7 +10,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
mockClient.fs = {
get: () => Promise.resolve({}),

View File

@@ -25,7 +25,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
mockClient.fs = {
write: () => {}
@@ -86,7 +86,7 @@ describe(CMD, function () {
});
let errorEmitted = false;
emitter.once('STOR', err => {
emitter.once('STOR', (err) => {
errorEmitted = !!err;
});

View File

@@ -13,7 +13,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
mockClient.fs = {
get: () => Promise.resolve(),

View File

@@ -11,7 +11,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.spy(mockClient, 'reply');
});

View File

@@ -11,7 +11,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
sandbox.spy(mockClient, 'reply');
});

View File

@@ -11,7 +11,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
mockClient.transferType = null;
sandbox.spy(mockClient, 'reply');

View File

@@ -16,7 +16,7 @@ describe(CMD, function () {
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
delete mockClient.username;
mockClient.server.options = {};

View File

@@ -19,18 +19,18 @@ describe('Connector - Active //', function () {
before(() => {
active = new ActiveConnector(mockConnection);
});
beforeEach(done => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
beforeEach((done) => {
sandbox = sinon.createSandbox().usingPromise(Promise);
getNextPort()
.then(port => {
.then((port) => {
PORT = port;
server = net.createServer()
.on('connection', socket => socket.destroy())
.on('connection', (socket) => socket.destroy())
.listen(PORT, () => done());
});
});
afterEach(done => {
afterEach((done) => {
sandbox.restore();
server.close(done);
});
@@ -58,7 +58,7 @@ describe('Connector - Active //', function () {
expect(active.dataSocket).to.exist;
return active.waitForConnection();
})
.then(dataSocket => {
.then((dataSocket) => {
expect(dataSocket.connected).to.equal(true);
expect(dataSocket instanceof net.Socket).to.equal(true);
expect(dataSocket instanceof tls.TLSSocket).to.equal(false);
@@ -78,7 +78,7 @@ describe('Connector - Active //', function () {
expect(active.dataSocket).to.exist;
return active.waitForConnection();
})
.then(dataSocket => {
.then((dataSocket) => {
expect(dataSocket.connected).to.equal(true);
expect(dataSocket instanceof net.Socket).to.equal(true);
expect(dataSocket instanceof tls.TLSSocket).to.equal(true);

View File

@@ -40,7 +40,7 @@ describe('Connector - Passive //', function () {
it('cannot wait for connection with no server', function (done) {
let passive = new PassiveConnector(mockConnection);
passive.waitForConnection()
.catch(err => {
.catch((err) => {
expect(err.name).to.equal('ConnectorError');
done();
});
@@ -54,7 +54,7 @@ describe('Connector - Passive //', function () {
it('no pasv range provided', function (done) {
let passive = new PassiveConnector(mockConnection);
passive.setupServer()
.catch(err => {
.catch((err) => {
try {
expect(err.name).to.equal('ConnectorError');
done();
@@ -75,7 +75,7 @@ describe('Connector - Passive //', function () {
it('has invalid pasv range', function (done) {
connection.setupServer()
.catch(err => {
.catch((err) => {
expect(err.name).to.equal('ConnectorError');
done();
});
@@ -108,7 +108,7 @@ describe('Connector - Passive //', function () {
it('destroys existing server, then sets up a server', function () {
return passive.setupServer()
.then(() => {
expect(closeFnSpy.callCount).to.equal(2);
expect(closeFnSpy.callCount).to.equal(1);
expect(passive.dataServer).to.exist;
});
});
@@ -129,8 +129,8 @@ describe('Connector - Passive //', function () {
expect(passive.connection.reply.callCount).to.equal(1);
expect(passive.connection.reply.args[0][0]).to.equal(550);
passive.end()
.then(() => done());
passive.end();
done();
}, 100);
});
})

View File

@@ -28,7 +28,7 @@ describe('FileSystem', function () {
it('handles error', function () {
return Promise.try(() => ovFs.chdir())
.catch(err => {
.catch((err) => {
expect(err).to.be.instanceof(errors.FileSystemError);
});
});

View File

@@ -9,7 +9,7 @@ describe('helpers // file-stat', function () {
let sandbox;
before(function () {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
});
afterEach(function () {
sandbox.restore();

View File

@@ -10,7 +10,7 @@ describe('helpers // find-port', function () {
let getNextPort;
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
getNextPort = getNextPortFactory(1, 2);
});
@@ -25,7 +25,7 @@ describe('helpers // find-port', function () {
});
return getNextPort()
.then(port => {
.then((port) => {
expect(port).to.equal(1);
});
});
@@ -37,15 +37,15 @@ describe('helpers // find-port', function () {
});
return getNextPort()
.then(port => {
.then((port) => {
expect(port).to.equal(1);
})
.then(() => getNextPort())
.then(port => {
.then((port) => {
expect(port).to.equal(2);
})
.then(() => getNextPort())
.then(port => {
.then((port) => {
expect(port).to.equal(1);
});
});

View File

@@ -7,14 +7,14 @@ describe('helpers //resolve-host', function () {
let sandbox;
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
});
afterEach(() => sandbox.restore());
it('fetches ip address', () => {
const hostname = '0.0.0.0';
return resolveHost(hostname)
.then(resolvedHostname => {
.then((resolvedHostname) => {
expect(resolvedHostname).to.match(/^\d+\.\d+\.\d+\.\d+$/);
});
});
@@ -22,7 +22,7 @@ describe('helpers //resolve-host', function () {
it('fetches ip address', () => {
const hostname = null;
return resolveHost(hostname)
.then(resolvedHostname => {
.then((resolvedHostname) => {
expect(resolvedHostname).to.match(/^\d+\.\d+\.\d+\.\d+$/);
});
});
@@ -30,7 +30,7 @@ describe('helpers //resolve-host', function () {
it('does nothing', () => {
const hostname = '127.0.0.1';
return resolveHost(hostname)
.then(resolvedHostname => {
.then((resolvedHostname) => {
expect(resolvedHostname).to.equal(hostname);
});
});
@@ -44,7 +44,7 @@ describe('helpers //resolve-host', function () {
return resolveHost(null)
.then(() => expect(1).to.equal(2))
.catch(err => {
.catch((err) => {
expect(err.code).to.equal(420);
});
});

View File

@@ -25,7 +25,7 @@ describe('Integration', function () {
return startServer({url: 'ftp://127.0.0.1:8880'});
});
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
sandbox = sinon.createSandbox().usingPromise(Promise);
});
afterEach(() => sandbox.restore());
after(() => server.close());
@@ -64,7 +64,7 @@ describe('Integration', function () {
return new Promise((resolve, reject) => {
client = new FtpClient();
client.once('ready', () => resolve(client));
client.once('error', err => reject(err));
client.once('error', (err) => reject(err));
client.connect(_.assign({
host: server.url.hostname,
port: server.url.port,
@@ -72,7 +72,7 @@ describe('Integration', function () {
password: 'test'
}, options));
})
.then(instance => {
.then((instance) => {
client = instance;
});
}
@@ -80,8 +80,8 @@ describe('Integration', function () {
function closeClient() {
return new Promise((resolve, reject) => {
client.once('close', () => resolve());
client.once('error', err => reject(err));
client.logout(err => {
client.once('error', (err) => reject(err));
client.logout((err) => {
expect(err).to.be.undefined;
});
});
@@ -92,7 +92,7 @@ describe('Integration', function () {
if (!dirExists) return;
const list = fs.readdirSync(dir);
list.map(item => nodePath.resolve(dir, item)).forEach(item => {
list.map((item) => nodePath.resolve(dir, item)).forEach((item) => {
const itemExists = fs.existsSync(dir);
if (!itemExists) return;
@@ -113,7 +113,7 @@ describe('Integration', function () {
after(() => directoryPurge(`${clientDirectory}/${name}/`));
it('STAT', done => {
it('STAT', (done) => {
client.status((err, status) => {
expect(err).to.not.exist;
expect(status).to.equal('Status OK');
@@ -121,7 +121,7 @@ describe('Integration', function () {
});
});
it('SYST', done => {
it('SYST', (done) => {
client.system((err, os) => {
expect(err).to.not.exist;
expect(os).to.equal('UNIX');
@@ -129,7 +129,7 @@ describe('Integration', function () {
});
});
it('CWD ..', done => {
it('CWD ..', (done) => {
client.cwd('..', (err, data) => {
expect(err).to.not.exist;
expect(data).to.equal('/');
@@ -137,7 +137,7 @@ describe('Integration', function () {
});
});
it(`CWD ${name}`, done => {
it(`CWD ${name}`, (done) => {
client.cwd(`${name}`, (err, data) => {
expect(err).to.not.exist;
expect(data).to.equal(`/${name}`);
@@ -145,7 +145,7 @@ describe('Integration', function () {
});
});
it('PWD', done => {
it('PWD', (done) => {
client.pwd((err, data) => {
expect(err).to.not.exist;
expect(data).to.equal(`/${name}`);
@@ -153,7 +153,7 @@ describe('Integration', function () {
});
});
it('LIST .', done => {
it('LIST .', (done) => {
client.list('.', (err, data) => {
expect(err).to.not.exist;
expect(data).to.be.an('array');
@@ -163,7 +163,7 @@ describe('Integration', function () {
});
});
it('LIST fake.txt', done => {
it('LIST fake.txt', (done) => {
client.list('fake.txt', (err, data) => {
expect(err).to.not.exist;
expect(data).to.be.an('array');
@@ -173,7 +173,7 @@ describe('Integration', function () {
});
});
it('STOR fail.txt', done => {
it('STOR fail.txt', (done) => {
const buffer = Buffer.from('test text file');
const fsPath = `${clientDirectory}/${name}/fail.txt`;
@@ -185,7 +185,7 @@ describe('Integration', function () {
return stream;
});
client.put(buffer, 'fail.txt', err => {
client.put(buffer, 'fail.txt', (err) => {
setImmediate(() => {
const fileExists = fs.existsSync(fsPath);
expect(err).to.exist;
@@ -195,15 +195,15 @@ describe('Integration', function () {
});
});
it('STOR tést.txt', done => {
it('STOR tést.txt', (done) => {
const buffer = Buffer.from('test text file');
const fsPath = `${clientDirectory}/${name}/tést.txt`;
connection.once('STOR', err => {
connection.once('STOR', (err) => {
expect(err).to.not.exist;
});
client.put(buffer, 'tést.txt', err => {
client.put(buffer, 'tést.txt', (err) => {
expect(err).to.not.exist;
setImmediate(() => {
expect(fs.existsSync(fsPath)).to.equal(true);
@@ -216,10 +216,10 @@ describe('Integration', function () {
});
});
it('APPE tést.txt', done => {
it('APPE tést.txt', (done) => {
const buffer = Buffer.from(', awesome!');
const fsPath = `${clientDirectory}/${name}/tést.txt`;
client.append(buffer, 'tést.txt', err => {
client.append(buffer, 'tést.txt', (err) => {
expect(err).to.not.exist;
setImmediate(() => {
expect(fs.existsSync(fsPath)).to.equal(true);
@@ -232,15 +232,15 @@ describe('Integration', function () {
});
});
it('RETR tést.txt', done => {
connection.once('RETR', err => {
it('RETR tést.txt', (done) => {
connection.once('RETR', (err) => {
expect(err).to.not.exist;
});
client.get('tést.txt', (err, stream) => {
expect(err).to.not.exist;
let text = '';
stream.on('data', data => {
stream.on('data', (data) => {
text += data.toString();
});
stream.on('end', () => {
@@ -251,8 +251,8 @@ describe('Integration', function () {
});
});
it('RNFR tést.txt, RNTO awesome.txt', done => {
client.rename('tést.txt', 'awesome.txt', err => {
it('RNFR tést.txt, RNTO awesome.txt', (done) => {
client.rename('tést.txt', 'awesome.txt', (err) => {
expect(err).to.not.exist;
expect(fs.existsSync(`${clientDirectory}/${name}/tést.txt`)).to.equal(false);
expect(fs.existsSync(`${clientDirectory}/${name}/awesome.txt`)).to.equal(true);
@@ -264,7 +264,7 @@ describe('Integration', function () {
});
});
it('SIZE awesome.txt', done => {
it('SIZE awesome.txt', (done) => {
client.size('awesome.txt', (err, size) => {
expect(err).to.not.exist;
expect(size).to.be.a('number');
@@ -272,7 +272,7 @@ describe('Integration', function () {
});
});
it('MDTM awesome.txt', done => {
it('MDTM awesome.txt', (done) => {
client.lastMod('awesome.txt', (err, modTime) => {
expect(err).to.not.exist;
expect(modTime).to.be.instanceOf(Date);
@@ -281,14 +281,14 @@ describe('Integration', function () {
});
});
it.skip('MLSD .', done => {
it.skip('MLSD .', (done) => {
client.mlsd('.', () => {
done();
});
});
it('SITE CHMOD 700 awesome.txt', done => {
client.site('CHMOD 600 awesome.txt', err => {
it('SITE CHMOD 700 awesome.txt', (done) => {
client.site('CHMOD 600 awesome.txt', (err) => {
expect(err).to.not.exist;
fs.stat(`${clientDirectory}/${name}/awesome.txt`, (fserr, stats) => {
expect(fserr).to.not.exist;
@@ -299,27 +299,27 @@ describe('Integration', function () {
});
});
it('DELE awesome.txt', done => {
client.delete('awesome.txt', err => {
it('DELE awesome.txt', (done) => {
client.delete('awesome.txt', (err) => {
expect(err).to.not.exist;
expect(fs.existsSync(`${clientDirectory}/${name}/awesome.txt`)).to.equal(false);
done();
});
});
it('MKD témp', done => {
it('MKD témp', (done) => {
const path = `${clientDirectory}/${name}/témp`;
if (fs.existsSync(path)) {
fs.rmdirSync(path);
}
client.mkdir('témp', err => {
client.mkdir('témp', (err) => {
expect(err).to.not.exist;
expect(fs.existsSync(path)).to.equal(true);
done();
});
});
it('CWD témp', done => {
it('CWD témp', (done) => {
client.cwd('témp', (err, data) => {
expect(err).to.not.exist;
expect(data).to.to.be.a('string');
@@ -327,23 +327,23 @@ describe('Integration', function () {
});
});
it('CDUP', done => {
client.cdup(err => {
it('CDUP', (done) => {
client.cdup((err) => {
expect(err).to.not.exist;
done();
});
});
it('RMD témp', done => {
client.rmdir('témp', err => {
it('RMD témp', (done) => {
client.rmdir('témp', (err) => {
expect(err).to.not.exist;
expect(fs.existsSync(`${clientDirectory}/${name}/témp`)).to.equal(false);
done();
});
});
it('CDUP', done => {
client.cdup(err => {
it('CDUP', (done) => {
client.cdup((err) => {
expect(err).to.not.exist;
done();
});
@@ -362,8 +362,8 @@ describe('Integration', function () {
after(() => closeClient(client));
it('TYPE A', done => {
client.ascii(err => {
it('TYPE A', (done) => {
client.ascii((err) => {
expect(err).to.not.exist;
done();
});
@@ -384,8 +384,8 @@ describe('Integration', function () {
after(() => closeClient(client));
it('TYPE I', done => {
client.binary(err => {
it('TYPE I', (done) => {
client.binary((err) => {
expect(err).to.not.exist;
done();
});

View File

@@ -1,11 +1,10 @@
require('dotenv').load();
const bunyan = require('bunyan');
const fs = require('fs');
const FtpServer = require('../src');
const server = new FtpServer({
log: bunyan.createLogger({name: 'test', level: 'trace'}),
url: 'ftps://127.0.0.1:8880',
url: 'ftp://127.0.0.1:8880',
pasv_min: 8881,
greeting: ['Welcome', 'to', 'the', 'jungle!'],
tls: {