Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1cfb94829e | ||
|
|
7a51ed9d07 | ||
|
|
cf7c678136 | ||
|
|
c716470b5b | ||
|
|
f443df52a7 | ||
|
|
08ab571f4c | ||
|
|
9035aefa4d | ||
|
|
89bd13dcb1 | ||
|
|
e8efe32126 | ||
|
|
18277e93ea |
19
.github/pull_request_template.md
vendored
Normal file
19
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
---
|
||||
|
||||
### Acceptance Checklist
|
||||
|
||||
- [ ] **Story**: Code is focused on the linked stories and solves a problem
|
||||
- One of:
|
||||
- [ ] **For Bugs**: A unit test is added or an existing one modified
|
||||
- [ ] **For Features**: New unit tests are added covering the new functions or modifications
|
||||
- [ ] Code Documentation changes are included for public interfaces and important / complex additions
|
||||
- [ ] External Documentation is included for API changes, or other external facing interfaces
|
||||
|
||||
### Review Checklist
|
||||
|
||||
- [ ] The code does not duplicate existing functionality that exists elsewhere
|
||||
- [ ] The code has been linted and follows team practices and style guidelines
|
||||
- [ ] The changes in the PR are relevant to the title
|
||||
- changes not related should be moved to a different PR
|
||||
- [ ] All errors or error handling is actionable, and informs the viewer on how to correct it
|
||||
76
bin/index.js
76
bin/index.js
@@ -1,14 +1,14 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const yargs = require('yargs');
|
||||
const path = require('path');
|
||||
const yargs = require('yargs')
|
||||
const path = require('path')
|
||||
|
||||
const FtpSrv = require('../src');
|
||||
const errors = require('../src/errors');
|
||||
const FtpSrv = require('../src')
|
||||
const errors = require('../src/errors')
|
||||
|
||||
const args = setupYargs();
|
||||
const state = setupState(args);
|
||||
startFtpServer(state);
|
||||
const args = setupYargs()
|
||||
const state = setupState(args)
|
||||
startFtpServer(state)
|
||||
|
||||
function setupYargs() {
|
||||
return yargs
|
||||
@@ -54,80 +54,80 @@ function setupYargs() {
|
||||
default: 65535,
|
||||
alias: 'pasv_max'
|
||||
})
|
||||
.parse();
|
||||
.parse()
|
||||
}
|
||||
|
||||
function setupState(_args) {
|
||||
const _state = {};
|
||||
const _state = {}
|
||||
|
||||
function setupOptions() {
|
||||
if (_args._ && _args._.length > 0) {
|
||||
_state.url = _args._[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 === '';
|
||||
_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;
|
||||
const dirPath = _args.root
|
||||
if (dirPath) {
|
||||
_state.root = dirPath;
|
||||
_state.root = dirPath
|
||||
} else {
|
||||
_state.root = process.cwd();
|
||||
_state.root = process.cwd()
|
||||
}
|
||||
}
|
||||
|
||||
function setupCredentials() {
|
||||
_state.credentials = {};
|
||||
_state.credentials = {}
|
||||
|
||||
const setCredentials = (username, password, root = null) => {
|
||||
_state.credentials[username] = {
|
||||
password,
|
||||
root
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (_args.credentials) {
|
||||
const credentialsFile = path.resolve(_args.credentials);
|
||||
const credentials = require(credentialsFile);
|
||||
const credentialsFile = path.resolve(_args.credentials)
|
||||
const credentials = require(credentialsFile)
|
||||
|
||||
for (const cred of credentials) {
|
||||
setCredentials(cred.username, cred.password, cred.root);
|
||||
setCredentials(cred.username, cred.password, cred.root)
|
||||
}
|
||||
} else if (_args.username) {
|
||||
setCredentials(_args.username, _args.password);
|
||||
setCredentials(_args.username, _args.password)
|
||||
}
|
||||
}
|
||||
|
||||
function setupCommandBlacklist() {
|
||||
if (_args.readOnly) {
|
||||
_state.blacklist = ['ALLO', 'APPE', 'DELE', 'MKD', 'RMD', 'RNRF', 'RNTO', 'STOR', 'STRU'];
|
||||
_state.blacklist = ['ALLO', 'APPE', 'DELE', 'MKD', 'RMD', 'RNRF', 'RNTO', 'STOR', 'STRU']
|
||||
}
|
||||
}
|
||||
|
||||
setupOptions();
|
||||
setupRoot();
|
||||
setupCredentials();
|
||||
setupCommandBlacklist();
|
||||
setupOptions()
|
||||
setupRoot()
|
||||
setupCredentials()
|
||||
setupCommandBlacklist()
|
||||
|
||||
return _state;
|
||||
return _state
|
||||
}
|
||||
|
||||
function startFtpServer(_state) {
|
||||
// Remove null/undefined options so they get set to defaults, below
|
||||
for (const key in _state) {
|
||||
if (_state[key] === undefined) delete _state[key];
|
||||
if (_state[key] === undefined) delete _state[key]
|
||||
}
|
||||
|
||||
function checkLogin(data, resolve, reject) {
|
||||
const user = _state.credentials[data.username];
|
||||
if (_state.anonymous || user && user.password === data.password) {
|
||||
return resolve({root: user && user.root || _state.root});
|
||||
const user = _state.credentials[data.username]
|
||||
if (_state.anonymous || (user && user.password === data.password)) {
|
||||
return resolve({ root: (user && user.root) || _state.root })
|
||||
}
|
||||
|
||||
return reject(new errors.GeneralError('Invalid username or password', 401));
|
||||
return reject(new errors.GeneralError('Invalid username or password', 401))
|
||||
}
|
||||
|
||||
const ftpServer = new FtpSrv({
|
||||
@@ -137,8 +137,8 @@ function startFtpServer(_state) {
|
||||
pasv_max: _state.pasv_max,
|
||||
anonymous: _state.anonymous,
|
||||
blacklist: _state.blacklist
|
||||
});
|
||||
})
|
||||
|
||||
ftpServer.on('login', checkLogin);
|
||||
ftpServer.listen();
|
||||
ftpServer.on('login', checkLogin)
|
||||
ftpServer.listen()
|
||||
}
|
||||
|
||||
235
ftp-srv.d.ts
vendored
235
ftp-srv.d.ts
vendored
@@ -1,134 +1,179 @@
|
||||
import * as tls from 'tls'
|
||||
import { Stats } from 'fs'
|
||||
import { EventEmitter } from 'events';
|
||||
import type { Server } from 'node:net'
|
||||
const EventEmitter = import('events').EventEmitter
|
||||
|
||||
export class FileSystem {
|
||||
readonly connection: FtpConnection
|
||||
readonly root: string
|
||||
readonly cwd: string
|
||||
|
||||
readonly connection: FtpConnection;
|
||||
readonly root: string;
|
||||
readonly cwd: string;
|
||||
constructor(
|
||||
connection: FtpConnection,
|
||||
{
|
||||
root,
|
||||
cwd
|
||||
}?: {
|
||||
root: any
|
||||
cwd: any
|
||||
}
|
||||
)
|
||||
|
||||
constructor(connection: FtpConnection, {root, cwd}?: {
|
||||
root: any;
|
||||
cwd: any;
|
||||
});
|
||||
currentDirectory(): string
|
||||
|
||||
currentDirectory(): string;
|
||||
get(fileName: string): Promise<any>
|
||||
|
||||
get(fileName: string): Promise<any>;
|
||||
list(path?: string): Promise<any>
|
||||
|
||||
list(path?: string): Promise<any>;
|
||||
chdir(path?: string): Promise<string>
|
||||
|
||||
chdir(path?: string): Promise<string>;
|
||||
write(
|
||||
fileName: string,
|
||||
{
|
||||
append,
|
||||
start
|
||||
}?: {
|
||||
append?: boolean
|
||||
start?: any
|
||||
}
|
||||
): any
|
||||
|
||||
write(fileName: string, {append, start}?: {
|
||||
append?: boolean;
|
||||
start?: any;
|
||||
}): any;
|
||||
read(
|
||||
fileName: string,
|
||||
{
|
||||
start
|
||||
}?: {
|
||||
start?: any
|
||||
}
|
||||
): Promise<any>
|
||||
|
||||
read(fileName: string, {start}?: {
|
||||
start?: any;
|
||||
}): Promise<any>;
|
||||
delete(path: string): Promise<any>
|
||||
|
||||
delete(path: string): Promise<any>;
|
||||
mkdir(path: string): Promise<any>
|
||||
|
||||
mkdir(path: string): Promise<any>;
|
||||
rename(from: string, to: string): Promise<any>
|
||||
|
||||
rename(from: string, to: string): Promise<any>;
|
||||
chmod(path: string, mode: string): Promise<any>
|
||||
|
||||
chmod(path: string, mode: string): Promise<any>;
|
||||
getUniqueName(fileName: string): string
|
||||
}
|
||||
|
||||
getUniqueName(fileName: string): string;
|
||||
export class GeneralError extends Error {
|
||||
/**
|
||||
* @param message The error message.
|
||||
* @param code Default value is `400`.
|
||||
*/
|
||||
constructor(message: string, code?: number)
|
||||
}
|
||||
|
||||
export class SocketError extends Error {
|
||||
/**
|
||||
* @param message The error message.
|
||||
* @param code Default value is `500`.
|
||||
*/
|
||||
constructor(message: string, code?: number)
|
||||
}
|
||||
|
||||
export class FileSystemError extends Error {
|
||||
/**
|
||||
* @param message The error message.
|
||||
* @param code Default value is `400`.
|
||||
*/
|
||||
constructor(message: string, code?: number)
|
||||
}
|
||||
|
||||
export class ConnectorError extends Error {
|
||||
/**
|
||||
* @param message The error message.
|
||||
* @param code Default value is `400`.
|
||||
*/
|
||||
constructor(message: string, code?: number)
|
||||
}
|
||||
|
||||
export class FtpConnection extends EventEmitter {
|
||||
server: FtpServer;
|
||||
id: string;
|
||||
log: any;
|
||||
transferType: string;
|
||||
encoding: string;
|
||||
bufferSize: boolean;
|
||||
readonly ip: string;
|
||||
restByteCount: number | undefined;
|
||||
secure: boolean
|
||||
|
||||
close (code: number, message: number): Promise<any>
|
||||
login (username: string, password: string): Promise<any>
|
||||
reply (options: number | Object, ...letters: Array<any>): Promise<any>
|
||||
server: FtpServer
|
||||
id: string
|
||||
log: any
|
||||
transferType: string
|
||||
encoding: string
|
||||
bufferSize: boolean
|
||||
readonly ip: string
|
||||
restByteCount: number | undefined
|
||||
secure: boolean
|
||||
|
||||
close(code: number, message: number): Promise<any>
|
||||
login(username: string, password: string): Promise<any>
|
||||
reply(options: number | Object, ...letters: Array<any>): Promise<any>
|
||||
}
|
||||
|
||||
export interface FtpServerOptions {
|
||||
url?: string,
|
||||
pasv_min?: number,
|
||||
pasv_max?: number,
|
||||
pasv_url?: string,
|
||||
greeting?: string | string[],
|
||||
tls?: tls.SecureContextOptions | false,
|
||||
anonymous?: boolean,
|
||||
blacklist?: Array<string>,
|
||||
whitelist?: Array<string>,
|
||||
file_format?: (stat: Stats) => string | Promise<string> | "ls" | "ep",
|
||||
log?: any,
|
||||
timeout?: number
|
||||
url?: string
|
||||
pasv_min?: number
|
||||
pasv_max?: number
|
||||
pasv_url?: string
|
||||
random_pasv_port?: boolean
|
||||
greeting?: string | string[]
|
||||
tls?: import('tls').SecureContextOptions | false
|
||||
anonymous?: boolean
|
||||
blacklist?: Array<string>
|
||||
whitelist?: Array<string>
|
||||
file_format?: ((stat: import('fs').Stats) => string) | 'ls' | 'ep'
|
||||
log?: any
|
||||
timeout?: number
|
||||
}
|
||||
|
||||
export class FtpServer extends EventEmitter {
|
||||
constructor(options?: FtpServerOptions);
|
||||
server: Server
|
||||
constructor(options?: FtpServerOptions)
|
||||
|
||||
readonly isTLS: boolean;
|
||||
readonly isTLS: boolean
|
||||
|
||||
listen(): any;
|
||||
listen(): any
|
||||
|
||||
emitPromise(action: any, ...data: any[]): Promise<any>;
|
||||
emitPromise(action: any, ...data: any[]): Promise<any>
|
||||
|
||||
// emit is exported from super class
|
||||
// emit is exported from super class
|
||||
|
||||
setupTLS(_tls: boolean): boolean | {
|
||||
cert: string;
|
||||
key: string;
|
||||
ca: string
|
||||
};
|
||||
setupTLS(_tls: boolean):
|
||||
| boolean
|
||||
| {
|
||||
cert: string
|
||||
key: string
|
||||
ca: string
|
||||
}
|
||||
|
||||
setupGreeting(greet: string): string[];
|
||||
setupGreeting(greet: string): string[]
|
||||
|
||||
setupFeaturesMessage(): string;
|
||||
setupFeaturesMessage(): string
|
||||
|
||||
disconnectClient(id: string): Promise<any>;
|
||||
disconnectClient(id: string): Promise<any>
|
||||
|
||||
close(): any;
|
||||
close(): any
|
||||
|
||||
on(event: "login", listener: (
|
||||
data: {
|
||||
connection: FtpConnection,
|
||||
username: string,
|
||||
password: string
|
||||
},
|
||||
resolve: (config: {
|
||||
fs?: FileSystem,
|
||||
root?: string,
|
||||
cwd?: string,
|
||||
blacklist?: Array<string>,
|
||||
whitelist?: Array<string>
|
||||
}) => void,
|
||||
reject: (err?: Error) => void
|
||||
) => void): this;
|
||||
on(
|
||||
event: 'login',
|
||||
listener: (
|
||||
data: {
|
||||
connection: FtpConnection
|
||||
username: string
|
||||
password: string
|
||||
},
|
||||
resolve: (config: {
|
||||
fs?: FileSystem
|
||||
root?: string
|
||||
cwd?: string
|
||||
blacklist?: Array<string>
|
||||
whitelist?: Array<string>
|
||||
}) => void,
|
||||
reject: (err?: Error) => void
|
||||
) => void
|
||||
): this
|
||||
|
||||
on(event: "disconnect", listener: (
|
||||
data: {
|
||||
connection: FtpConnection,
|
||||
id: string
|
||||
}
|
||||
) => void): this;
|
||||
on(event: 'disconnect', listener: (data: { connection: FtpConnection; id: string }) => void): this
|
||||
|
||||
on(event: "client-error", listener: (
|
||||
data: {
|
||||
connection: FtpConnection,
|
||||
context: string,
|
||||
error: Error,
|
||||
}
|
||||
) => void): this;
|
||||
on(
|
||||
event: 'client-error',
|
||||
listener: (data: { connection: FtpConnection; context: string; error: Error }) => void
|
||||
): this
|
||||
}
|
||||
|
||||
export {FtpServer as FtpSrv};
|
||||
export default FtpServer;
|
||||
export { FtpServer as FtpSrv }
|
||||
export default FtpServer
|
||||
|
||||
12
ftp-srv.js
12
ftp-srv.js
@@ -1,8 +1,6 @@
|
||||
const FtpSrv = require('./src');
|
||||
const FileSystem = require('./src/fs');
|
||||
const errors = require('./src/errors');
|
||||
import FtpSrv from './src/index.js'
|
||||
import FileSystem from './src/fs.js'
|
||||
import ftpErrors from './src/errors.js'
|
||||
|
||||
module.exports = FtpSrv;
|
||||
module.exports.FtpSrv = FtpSrv;
|
||||
module.exports.FileSystem = FileSystem;
|
||||
module.exports.ftpErrors = errors;
|
||||
export default FtpSrv
|
||||
export { FtpSrv, FileSystem, ftpErrors }
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
const {get} = require('https');
|
||||
const { get } = require('https')
|
||||
|
||||
get('https://api.github.com/repos/trs/ftp-srv/contributors', {
|
||||
headers: {
|
||||
'User-Agent': 'Chrome'
|
||||
}
|
||||
}, (res) => {
|
||||
let response = '';
|
||||
res.on('data', (data) => {
|
||||
response += data;
|
||||
});
|
||||
res.on('end', () => {
|
||||
const contributors = JSON.parse(response)
|
||||
.filter((contributor) => contributor.type === 'User');
|
||||
|
||||
for (const contributor of contributors) {
|
||||
const url = contributor.html_url;
|
||||
const username = contributor.login;
|
||||
|
||||
const markdown = `- [${username}](${url})\n`;
|
||||
|
||||
process.stdout.write(markdown);
|
||||
get(
|
||||
'https://api.github.com/repos/trs/ftp-srv/contributors',
|
||||
{
|
||||
headers: {
|
||||
'User-Agent': 'Chrome'
|
||||
}
|
||||
});
|
||||
}).on('error', (err) => {
|
||||
process.stderr.write(err);
|
||||
});
|
||||
},
|
||||
(res) => {
|
||||
let response = ''
|
||||
res.on('data', (data) => {
|
||||
response += data
|
||||
})
|
||||
res.on('end', () => {
|
||||
const contributors = JSON.parse(response).filter((contributor) => contributor.type === 'User')
|
||||
|
||||
for (const contributor of contributors) {
|
||||
const url = contributor.html_url
|
||||
const username = contributor.login
|
||||
|
||||
const markdown = `- [${username}](${url})\n`
|
||||
|
||||
process.stdout.write(markdown)
|
||||
}
|
||||
})
|
||||
}
|
||||
).on('error', (err) => {
|
||||
process.stderr.write(err)
|
||||
})
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
const puppeteer = require('puppeteer');
|
||||
const logoPath = `file://${process.cwd()}/logo/logo.html`;
|
||||
const puppeteer = require('puppeteer')
|
||||
const logoPath = `file://${process.cwd()}/logo/logo.html`
|
||||
|
||||
puppeteer.launch()
|
||||
.then(browser => {
|
||||
return browser.newPage()
|
||||
.then(page => {
|
||||
return page.goto(logoPath)
|
||||
.then(() => page);
|
||||
})
|
||||
.then(page => {
|
||||
return page.setViewport({
|
||||
width: 600,
|
||||
height: 250,
|
||||
deviceScaleFactor: 2
|
||||
puppeteer.launch().then((browser) => {
|
||||
return browser
|
||||
.newPage()
|
||||
.then((page) => {
|
||||
return page.goto(logoPath).then(() => page)
|
||||
})
|
||||
.then(() => page.screenshot({
|
||||
path: 'logo.png',
|
||||
omitBackground: true
|
||||
}));
|
||||
})
|
||||
.then(() => browser.close());
|
||||
});
|
||||
.then((page) => {
|
||||
return page
|
||||
.setViewport({
|
||||
width: 600,
|
||||
height: 250,
|
||||
deviceScaleFactor: 2
|
||||
})
|
||||
.then(() =>
|
||||
page.screenshot({
|
||||
path: 'logo.png',
|
||||
omitBackground: true
|
||||
})
|
||||
)
|
||||
})
|
||||
.then(() => browser.close())
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "ftp-srv",
|
||||
"name": "@tabshift/ftp-srv",
|
||||
"version": "0.0.0",
|
||||
"description": "Modern, extensible FTP Server",
|
||||
"keywords": [
|
||||
@@ -17,6 +17,7 @@
|
||||
"bin",
|
||||
"ftp-srv.d.ts"
|
||||
],
|
||||
"type": "module",
|
||||
"main": "ftp-srv.js",
|
||||
"bin": "./bin/index.js",
|
||||
"types": "./ftp-srv.d.ts",
|
||||
|
||||
@@ -1,76 +1,76 @@
|
||||
const _ = require('lodash');
|
||||
const Promise = require('bluebird');
|
||||
import _ from 'lodash'
|
||||
import Promise from 'bluebird'
|
||||
import REGISTRY from './registry.js'
|
||||
|
||||
const REGISTRY = require('./registry');
|
||||
const CMD_FLAG_REGEX = new RegExp(/^-(\w{1})$/)
|
||||
|
||||
const CMD_FLAG_REGEX = new RegExp(/^-(\w{1})$/);
|
||||
|
||||
class FtpCommands {
|
||||
export default 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.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))
|
||||
}
|
||||
|
||||
parse(message) {
|
||||
const strippedMessage = message.replace(/"/g, '');
|
||||
let [directive, ...args] = strippedMessage.split(' ');
|
||||
directive = _.chain(directive).trim().toUpper().value();
|
||||
const strippedMessage = message.replace(/"/g, '')
|
||||
let [directive, ...args] = strippedMessage.split(' ')
|
||||
directive = _.chain(directive).trim().toUpper().value()
|
||||
|
||||
const parseCommandFlags = !['RETR', 'SIZE', 'STOR'].includes(directive);
|
||||
const params = args.reduce(({arg, flags}, param) => {
|
||||
if (parseCommandFlags && CMD_FLAG_REGEX.test(param)) flags.push(param);
|
||||
else arg.push(param);
|
||||
return {arg, flags};
|
||||
}, {arg: [], flags: []});
|
||||
const parseCommandFlags = !['RETR', 'SIZE', 'STOR'].includes(directive)
|
||||
const params = args.reduce(
|
||||
({ arg, flags }, param) => {
|
||||
if (parseCommandFlags && CMD_FLAG_REGEX.test(param)) flags.push(param)
|
||||
else arg.push(param)
|
||||
return { arg, flags }
|
||||
},
|
||||
{ arg: [], flags: [] }
|
||||
)
|
||||
|
||||
const command = {
|
||||
directive,
|
||||
arg: params.arg.length ? params.arg.join(' ') : null,
|
||||
flags: params.flags,
|
||||
raw: message
|
||||
};
|
||||
return command;
|
||||
}
|
||||
return command
|
||||
}
|
||||
|
||||
handle(command) {
|
||||
if (typeof command === 'string') command = this.parse(command);
|
||||
if (typeof command === 'string') command = this.parse(command)
|
||||
|
||||
// Obfuscate password from logs
|
||||
const logCommand = _.clone(command);
|
||||
if (logCommand.directive === 'PASS') logCommand.arg = '********';
|
||||
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');
|
||||
const log = this.connection.log.child({ directive: command.directive })
|
||||
log.trace({ command: logCommand }, 'Handle command')
|
||||
|
||||
if (!REGISTRY.hasOwnProperty(command.directive)) {
|
||||
return this.connection.reply(502, `Command not allowed: ${command.directive}`);
|
||||
return this.connection.reply(502, `Command not allowed: ${command.directive}`)
|
||||
}
|
||||
|
||||
if (_.includes(this.blacklist, command.directive)) {
|
||||
return this.connection.reply(502, `Command blacklisted: ${command.directive}`);
|
||||
return this.connection.reply(502, `Command blacklisted: ${command.directive}`)
|
||||
}
|
||||
|
||||
if (this.whitelist.length > 0 && !_.includes(this.whitelist, command.directive)) {
|
||||
return this.connection.reply(502, `Command not whitelisted: ${command.directive}`);
|
||||
return this.connection.reply(502, `Command not whitelisted: ${command.directive}`)
|
||||
}
|
||||
|
||||
const commandRegister = REGISTRY[command.directive];
|
||||
const commandFlags = _.get(commandRegister, 'flags', {});
|
||||
const commandRegister = REGISTRY[command.directive]
|
||||
const commandFlags = _.get(commandRegister, 'flags', {})
|
||||
if (!commandFlags.no_auth && !this.connection.authenticated) {
|
||||
return this.connection.reply(530, `Command requires authentication: ${command.directive}`);
|
||||
return this.connection.reply(530, `Command requires authentication: ${command.directive}`)
|
||||
}
|
||||
|
||||
if (!commandRegister.handler) {
|
||||
return this.connection.reply(502, `Handler not set on command: ${command.directive}`);
|
||||
return this.connection.reply(502, `Handler not set on command: ${command.directive}`)
|
||||
}
|
||||
|
||||
const handler = commandRegister.handler.bind(this.connection);
|
||||
return Promise.resolve(handler({log, command, previous_command: this.previousCommand}))
|
||||
.then(() => {
|
||||
this.previousCommand = _.clone(command);
|
||||
});
|
||||
const handler = commandRegister.handler.bind(this.connection)
|
||||
return Promise.resolve(handler({ log, command, previous_command: this.previousCommand })).then(() => {
|
||||
this.previousCommand = _.clone(command)
|
||||
})
|
||||
}
|
||||
}
|
||||
module.exports = FtpCommands;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'ABOR',
|
||||
handler: function () {
|
||||
return this.connector.waitForConnection()
|
||||
.then((socket) => {
|
||||
return this.reply(426, {socket})
|
||||
.then(() => this.reply(226));
|
||||
})
|
||||
.catch(() => this.reply(225))
|
||||
.then(() => this.connector.end());
|
||||
return this.connector
|
||||
.waitForConnection()
|
||||
.then((socket) => {
|
||||
return this.reply(426, { socket }).then(() => this.reply(226))
|
||||
})
|
||||
.catch(() => this.reply(225))
|
||||
.then(() => this.connector.end())
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Abort an active file transfer'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'ALLO',
|
||||
handler: function () {
|
||||
return this.reply(202);
|
||||
return this.reply(202)
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Allocate sufficient disk space to receive a file',
|
||||
flags: {
|
||||
obsolete: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
const stor = require('./stor').handler;
|
||||
import stor from './stor.js'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'APPE',
|
||||
handler: function (args) {
|
||||
return stor.call(this, args);
|
||||
return stor.handler.call(this, args)
|
||||
},
|
||||
syntax: '{{cmd}} <path>',
|
||||
description: 'Append to a file'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
const _ = require('lodash');
|
||||
const tls = require('tls');
|
||||
import _ from 'lodash'
|
||||
import tls from 'node:tls'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'AUTH',
|
||||
handler: function ({command} = {}) {
|
||||
const method = _.upperCase(command.arg);
|
||||
handler: function ({ 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)
|
||||
default:
|
||||
return this.reply(504)
|
||||
}
|
||||
},
|
||||
syntax: '{{cmd}} <type>',
|
||||
@@ -17,26 +19,25 @@ module.exports = {
|
||||
no_auth: true,
|
||||
feat: 'AUTH TLS'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function handleTLS() {
|
||||
if (!this.server.options.tls) return this.reply(502);
|
||||
if (this.secure) return this.reply(202);
|
||||
if (!this.server.options.tls) return this.reply(502)
|
||||
if (this.secure) return this.reply(202)
|
||||
|
||||
return this.reply(234)
|
||||
.then(() => {
|
||||
const secureContext = tls.createSecureContext(this.server.options.tls);
|
||||
return this.reply(234).then(() => {
|
||||
const secureContext = tls.createSecureContext(this.server.options.tls)
|
||||
const secureSocket = new tls.TLSSocket(this.commandSocket, {
|
||||
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);
|
||||
this.emit.apply(this, arguments)
|
||||
}
|
||||
secureSocket.on(event, forwardEvent.bind(this.commandSocket, event));
|
||||
});
|
||||
this.commandSocket = secureSocket;
|
||||
this.secure = true;
|
||||
});
|
||||
secureSocket.on(event, forwardEvent.bind(this.commandSocket, event))
|
||||
})
|
||||
this.commandSocket = secureSocket
|
||||
this.secure = true
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
const cwd = require('./cwd').handler;
|
||||
import cwd from './cwd.js'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: ['CDUP', 'XCUP'],
|
||||
handler: function (args) {
|
||||
args.command.arg = '..';
|
||||
return cwd.call(this, args);
|
||||
args.command.arg = '..'
|
||||
return cwd.handler.call(this, args)
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Change to Parent Directory'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
const Promise = require('bluebird');
|
||||
const escapePath = require('../../helpers/escape-path');
|
||||
import Promise from 'bluebird'
|
||||
import escapePath from '../../helpers/escape-path.js'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
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 ({ 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')
|
||||
|
||||
return Promise.try(() => this.fs.chdir(command.arg))
|
||||
.then((cwd) => {
|
||||
const path = cwd ? `"${escapePath(cwd)}"` : undefined;
|
||||
return this.reply(250, path);
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err);
|
||||
return this.reply(550, err.message);
|
||||
});
|
||||
.then((cwd) => {
|
||||
const path = cwd ? `"${escapePath(cwd)}"` : undefined
|
||||
return this.reply(250, path)
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err)
|
||||
return this.reply(550, err.message)
|
||||
})
|
||||
},
|
||||
syntax: '{{cmd}} <path>',
|
||||
description: 'Change working directory'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
const Promise = require('bluebird');
|
||||
import Promise from 'bluebird'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
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 ({ 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')
|
||||
|
||||
return Promise.try(() => this.fs.delete(command.arg))
|
||||
.then(() => {
|
||||
return this.reply(250);
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err);
|
||||
return this.reply(550, err.message);
|
||||
});
|
||||
.then(() => {
|
||||
return this.reply(250)
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err)
|
||||
return this.reply(550, err.message)
|
||||
})
|
||||
},
|
||||
syntax: '{{cmd}} <path>',
|
||||
description: 'Delete file'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
const _ = require('lodash');
|
||||
const ActiveConnector = require('../../connector/active');
|
||||
import _ from 'lodash'
|
||||
import ActiveConnector from '../../connector/active.js'
|
||||
|
||||
const FAMILY = {
|
||||
1: 4,
|
||||
2: 6
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'EPRT',
|
||||
handler: function ({log, command} = {}) {
|
||||
const [, protocol, ip, port] = _.chain(command).get('arg', '').split('|').value();
|
||||
const family = FAMILY[protocol];
|
||||
if (!family) return this.reply(504, 'Unknown network protocol');
|
||||
handler: function ({ log, command } = {}) {
|
||||
const [, protocol, ip, port] = _.chain(command).get('arg', '').split('|').value()
|
||||
const family = FAMILY[protocol]
|
||||
if (!family) return this.reply(504, 'Unknown network protocol')
|
||||
|
||||
this.connector = new ActiveConnector(this);
|
||||
return this.connector.setupConnection(ip, port, family)
|
||||
.then(() => this.reply(200))
|
||||
.catch((err) => {
|
||||
log.error(err);
|
||||
return this.reply(err.code || 425, err.message);
|
||||
});
|
||||
this.connector = new ActiveConnector(this)
|
||||
return this.connector
|
||||
.setupConnection(ip, port, family)
|
||||
.then(() => this.reply(200))
|
||||
.catch((err) => {
|
||||
log.error(err)
|
||||
return this.reply(err.code || 425, err.message)
|
||||
})
|
||||
},
|
||||
syntax: '{{cmd}} |<protocol>|<address>|<port>|',
|
||||
description: 'Specifies an address and port to which the server should connect'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
const PassiveConnector = require('../../connector/passive');
|
||||
import PassiveConnector from '../../connector/passive.js'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'EPSV',
|
||||
handler: function ({log}) {
|
||||
this.connector = new PassiveConnector(this);
|
||||
return this.connector.setupServer()
|
||||
.then((server) => {
|
||||
const {port} = server.address();
|
||||
handler: function ({ log }) {
|
||||
this.connector = new PassiveConnector(this)
|
||||
return this.connector
|
||||
.setupServer()
|
||||
.then((server) => {
|
||||
const { port } = server.address()
|
||||
|
||||
return this.reply(229, `EPSV OK (|||${port}|)`);
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err);
|
||||
return this.reply(err.code || 425, err.message);
|
||||
});
|
||||
return this.reply(229, `EPSV OK (|||${port}|)`)
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err)
|
||||
return this.reply(err.code || 425, err.message)
|
||||
})
|
||||
},
|
||||
syntax: '{{cmd}} [<protocol>]',
|
||||
description: 'Initiate passive mode'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
const _ = require('lodash');
|
||||
import _ from 'lodash'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'FEAT',
|
||||
handler: function () {
|
||||
const registry = require('../registry');
|
||||
const registry = import('../registry')
|
||||
const features = Object.keys(registry)
|
||||
.reduce((feats, cmd) => {
|
||||
const feat = _.get(registry[cmd], 'flags.feat', null);
|
||||
if (feat) return _.concat(feats, feat);
|
||||
return feats;
|
||||
}, ['UTF8'])
|
||||
.reduce(
|
||||
(feats, cmd) => {
|
||||
const feat = _.get(registry[cmd], 'flags.feat', null)
|
||||
if (feat) return _.concat(feats, feat)
|
||||
return feats
|
||||
},
|
||||
['UTF8']
|
||||
)
|
||||
.sort()
|
||||
.map((feat) => ({
|
||||
message: ` ${feat}`,
|
||||
raw: true
|
||||
}));
|
||||
}))
|
||||
return features.length
|
||||
? this.reply(211, 'Extensions supported', ...features, 'End')
|
||||
: this.reply(211, 'No features');
|
||||
: this.reply(211, 'No features')
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Get the feature list implemented by the server',
|
||||
flags: {
|
||||
no_auth: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
const _ = require('lodash');
|
||||
import _ from 'lodash'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'HELP',
|
||||
handler: function ({command} = {}) {
|
||||
const registry = require('../registry');
|
||||
const directive = _.upperCase(command.arg);
|
||||
handler: function ({ command } = {}) {
|
||||
const registry = import('../registry')
|
||||
const directive = _.upperCase(command.arg)
|
||||
if (directive) {
|
||||
if (!registry.hasOwnProperty(directive)) return this.reply(502, `Unknown command ${directive}.`);
|
||||
if (!registry.hasOwnProperty(directive)) return this.reply(502, `Unknown command ${directive}.`)
|
||||
|
||||
const {syntax, description} = registry[directive];
|
||||
const reply = _.concat([syntax.replace('{{cmd}}', directive), description]);
|
||||
return this.reply(214, ...reply);
|
||||
const { syntax, description } = registry[directive]
|
||||
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'));
|
||||
return this.reply(211, 'Supported commands:', ...supportedCommands, 'Use "HELP [command]" for syntax help.');
|
||||
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.'
|
||||
)
|
||||
}
|
||||
},
|
||||
syntax: '{{cmd}} [<command>]',
|
||||
description: 'Returns usage documentation on a command if specified, else a general help document is returned',
|
||||
description:
|
||||
'Returns usage documentation on a command if specified, else a general help document is returned',
|
||||
flags: {
|
||||
no_auth: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,57 +1,61 @@
|
||||
const _ = require('lodash');
|
||||
const Promise = require('bluebird');
|
||||
const getFileStat = require('../../helpers/file-stat');
|
||||
import _ from 'lodash'
|
||||
import Promise from 'bluebird'
|
||||
import getFileStat from '../../helpers/file-stat.js'
|
||||
|
||||
// http://cr.yp.to/ftp/list.html
|
||||
// http://cr.yp.to/ftp/list/eplf.html
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'LIST',
|
||||
handler: function ({log, command} = {}) {
|
||||
if (!this.fs) return this.reply(550, 'File system not instantiated');
|
||||
if (!this.fs.get) return this.reply(402, 'Not supported by file system');
|
||||
if (!this.fs.list) return this.reply(402, 'Not supported by file system');
|
||||
handler: function ({ log, command } = {}) {
|
||||
if (!this.fs) return this.reply(550, 'File system not instantiated')
|
||||
if (!this.fs.get) return this.reply(402, 'Not supported by file system')
|
||||
if (!this.fs.list) return this.reply(402, 'Not supported by file system')
|
||||
|
||||
const simple = command.directive === 'NLST';
|
||||
const simple = command.directive === 'NLST'
|
||||
|
||||
const path = command.arg || '.';
|
||||
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) => {
|
||||
if (simple) return file.name;
|
||||
return getFileStat(file, _.get(this, 'server.options.file_format', 'ls'));
|
||||
};
|
||||
const path = command.arg || '.'
|
||||
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) => {
|
||||
if (simple) return file.name
|
||||
return getFileStat(file, _.get(this, 'server.options.file_format', 'ls'))
|
||||
}
|
||||
|
||||
return Promise.try(() => files.map((file) => {
|
||||
const message = getFileMessage(file);
|
||||
return {
|
||||
raw: true,
|
||||
message,
|
||||
socket: this.connector.socket
|
||||
};
|
||||
}));
|
||||
})
|
||||
.tap(() => this.reply(150))
|
||||
.then((fileList) => {
|
||||
if (fileList.length) return this.reply({}, ...fileList);
|
||||
return this.reply({socket: this.connector.socket, useEmptyMessage: true});
|
||||
})
|
||||
.tap(() => this.reply(226))
|
||||
.catch(Promise.TimeoutError, (err) => {
|
||||
log.error(err);
|
||||
return this.reply(425, 'No connection established');
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err);
|
||||
return this.reply(451, err.message || 'No directory');
|
||||
})
|
||||
.then(() => {
|
||||
this.connector.end();
|
||||
this.commandSocket.resume();
|
||||
});
|
||||
return Promise.try(() =>
|
||||
files.map((file) => {
|
||||
const message = getFileMessage(file)
|
||||
return {
|
||||
raw: true,
|
||||
message,
|
||||
socket: this.connector.socket
|
||||
}
|
||||
})
|
||||
)
|
||||
})
|
||||
.tap(() => this.reply(150))
|
||||
.then((fileList) => {
|
||||
if (fileList.length) return this.reply({}, ...fileList)
|
||||
return this.reply({ socket: this.connector.socket, useEmptyMessage: true })
|
||||
})
|
||||
.tap(() => this.reply(226))
|
||||
.catch(Promise.TimeoutError, (err) => {
|
||||
log.error(err)
|
||||
return this.reply(425, 'No connection established')
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err)
|
||||
return this.reply(451, err.message || 'No directory')
|
||||
})
|
||||
.then(() => {
|
||||
this.connector.end()
|
||||
this.commandSocket.resume()
|
||||
})
|
||||
},
|
||||
syntax: '{{cmd}} [<path>]',
|
||||
description: 'Returns information of a file or directory if specified, else information of the current working directory is returned'
|
||||
};
|
||||
description:
|
||||
'Returns information of a file or directory if specified, else information of the current working directory is returned'
|
||||
}
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
const Promise = require('bluebird');
|
||||
const moment = require('moment');
|
||||
import Promise from 'bluebird'
|
||||
import moment from 'moment'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
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 ({ 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')
|
||||
|
||||
return Promise.try(() => this.fs.get(command.arg))
|
||||
.then((fileStat) => {
|
||||
const modificationTime = moment.utc(fileStat.mtime).format('YYYYMMDDHHmmss.SSS');
|
||||
return this.reply(213, modificationTime);
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err);
|
||||
return this.reply(550, err.message);
|
||||
});
|
||||
.then((fileStat) => {
|
||||
const modificationTime = moment.utc(fileStat.mtime).format('YYYYMMDDHHmmss.SSS')
|
||||
return this.reply(213, modificationTime)
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err)
|
||||
return this.reply(550, err.message)
|
||||
})
|
||||
},
|
||||
syntax: '{{cmd}} <path>',
|
||||
description: 'Return the last-modified time of a specified file',
|
||||
flags: {
|
||||
feat: 'MDTM'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
const Promise = require('bluebird');
|
||||
const escapePath = require('../../helpers/escape-path');
|
||||
import Promise from 'bluebird'
|
||||
import escapePath from '../../helpers/escape-path.js'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
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 ({ 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')
|
||||
|
||||
return Promise.try(() => this.fs.mkdir(command.arg, { recursive: true }))
|
||||
.then((dir) => {
|
||||
const path = dir ? `"${escapePath(dir)}"` : undefined;
|
||||
return this.reply(257, path);
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err);
|
||||
return this.reply(550, err.message);
|
||||
});
|
||||
.then((dir) => {
|
||||
const path = dir ? `"${escapePath(dir)}"` : undefined
|
||||
return this.reply(257, path)
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err)
|
||||
return this.reply(550, err.message)
|
||||
})
|
||||
},
|
||||
syntax: '{{cmd}} <path>',
|
||||
description: 'Make directory'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'MODE',
|
||||
handler: function ({command} = {}) {
|
||||
return this.reply(/^S$/i.test(command.arg) ? 200 : 504);
|
||||
handler: function ({ command } = {}) {
|
||||
return this.reply(/^S$/i.test(command.arg) ? 200 : 504)
|
||||
},
|
||||
syntax: '{{cmd}} <mode>',
|
||||
description: 'Sets the transfer mode (Stream, Block, or Compressed)',
|
||||
flags: {
|
||||
obsolete: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
const list = require('./list').handler;
|
||||
import list from './list.js'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'NLST',
|
||||
handler: function (args) {
|
||||
return list.call(this, args);
|
||||
return list.handler.call(this, args)
|
||||
},
|
||||
syntax: '{{cmd}} [<path>]',
|
||||
description: 'Returns a list of file names in a specified directory'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'NOOP',
|
||||
handler: function () {
|
||||
return this.reply(200);
|
||||
return this.reply(200)
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'No operation',
|
||||
flags: {
|
||||
no_auth: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,38 +1,41 @@
|
||||
const _ = require('lodash');
|
||||
import _ from 'lodash'
|
||||
|
||||
const OPTIONS = {
|
||||
UTF8: utf8,
|
||||
'UTF-8': utf8
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'OPTS',
|
||||
handler: function ({command} = {}) {
|
||||
if (!_.has(command, 'arg')) return this.reply(501);
|
||||
handler: function ({ command } = {}) {
|
||||
if (!_.has(command, 'arg')) return this.reply(501)
|
||||
|
||||
const [_option, ...args] = command.arg.split(' ');
|
||||
const option = _.toUpper(_option);
|
||||
const [_option, ...args] = command.arg.split(' ')
|
||||
const option = _.toUpper(_option)
|
||||
|
||||
if (!OPTIONS.hasOwnProperty(option)) return this.reply(501, 'Unknown option command');
|
||||
return OPTIONS[option].call(this, args);
|
||||
if (!OPTIONS.hasOwnProperty(option)) return this.reply(501, 'Unknown option command')
|
||||
return OPTIONS[option].call(this, args)
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Select options for a feature'
|
||||
};
|
||||
}
|
||||
|
||||
function utf8([setting] = []) {
|
||||
const getEncoding = () => {
|
||||
switch (_.toUpper(setting)) {
|
||||
case 'ON': return 'utf8';
|
||||
case 'OFF': return 'ascii';
|
||||
default: return null;
|
||||
case 'ON':
|
||||
return 'utf8'
|
||||
case 'OFF':
|
||||
return 'ascii'
|
||||
default:
|
||||
return null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const encoding = getEncoding();
|
||||
if (!encoding) return this.reply(501, 'Unknown setting for option');
|
||||
const encoding = getEncoding()
|
||||
if (!encoding) return this.reply(501, 'Unknown setting for option')
|
||||
|
||||
this.encoding = encoding;
|
||||
this.encoding = encoding
|
||||
|
||||
return this.reply(200, `UTF8 encoding ${_.toLower(setting)}`);
|
||||
return this.reply(200, `UTF8 encoding ${_.toLower(setting)}`)
|
||||
}
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'PASS',
|
||||
handler: function ({log, command} = {}) {
|
||||
if (!this.username) return this.reply(503);
|
||||
if (this.authenticated) return this.reply(202);
|
||||
handler: function ({ log, command } = {}) {
|
||||
if (!this.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');
|
||||
const password = command.arg
|
||||
if (!password) return this.reply(501, 'Must provide password')
|
||||
return this.login(this.username, password)
|
||||
.then(() => {
|
||||
return this.reply(230);
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err);
|
||||
return this.reply(530, err.message || 'Authentication failed');
|
||||
});
|
||||
.then(() => {
|
||||
return this.reply(230)
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err)
|
||||
return this.reply(530, err.message || 'Authentication failed')
|
||||
})
|
||||
},
|
||||
syntax: '{{cmd}} <password>',
|
||||
description: 'Authentication password',
|
||||
flags: {
|
||||
no_auth: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
const Promise = require('bluebird');
|
||||
const PassiveConnector = require('../../connector/passive');
|
||||
const {isLocalIP} = require('../../helpers/is-local');
|
||||
import Promise from 'bluebird'
|
||||
import PassiveConnector from '../../connector/passive.js'
|
||||
import { isLocalIP } from '../../helpers/is-local.js'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'PASV',
|
||||
handler: function ({log} = {}) {
|
||||
handler: function ({ log } = {}) {
|
||||
if (!this.server.options.pasv_url) {
|
||||
return this.reply(502);
|
||||
return this.reply(502)
|
||||
}
|
||||
|
||||
this.connector = new PassiveConnector(this);
|
||||
return this.connector.setupServer()
|
||||
.then((server) => {
|
||||
const {port} = server.address();
|
||||
let pasvAddress = this.server.options.pasv_url;
|
||||
if (typeof pasvAddress === "function") {
|
||||
return Promise.try(() => pasvAddress(this.ip))
|
||||
.then((address) => ({address, port}));
|
||||
}
|
||||
// Allow connecting from local
|
||||
if (isLocalIP(this.ip)) pasvAddress = this.ip;
|
||||
return {address: pasvAddress, port};
|
||||
})
|
||||
.then(({address, port}) => {
|
||||
const host = address.replace(/\./g, ',');
|
||||
const portByte1 = port / 256 | 0;
|
||||
const portByte2 = port % 256;
|
||||
this.connector = new PassiveConnector(this)
|
||||
return this.connector
|
||||
.setupServer()
|
||||
.then((server) => {
|
||||
const { port } = server.address()
|
||||
let pasvAddress = this.server.options.pasv_url
|
||||
if (typeof pasvAddress === 'function') {
|
||||
return Promise.try(() => pasvAddress(this.ip)).then((address) => ({ address, port }))
|
||||
}
|
||||
// Allow connecting from local
|
||||
if (isLocalIP(this.ip)) pasvAddress = this.ip
|
||||
return { address: pasvAddress, port }
|
||||
})
|
||||
.then(({ address, port }) => {
|
||||
const host = address.replace(/\./g, ',')
|
||||
const portByte1 = (port / 256) | 0
|
||||
const portByte2 = port % 256
|
||||
|
||||
return this.reply(227, `PASV OK (${host},${portByte1},${portByte2})`);
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err);
|
||||
return this.reply(err.code || 425, err.message);
|
||||
});
|
||||
return this.reply(227, `PASV OK (${host},${portByte1},${portByte2})`)
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err)
|
||||
return this.reply(err.code || 425, err.message)
|
||||
})
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Initiate passive mode'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'PBSZ',
|
||||
handler: function ({command} = {}) {
|
||||
if (!this.secure) return this.reply(202, 'Not supported');
|
||||
this.bufferSize = parseInt(command.arg, 10);
|
||||
return this.reply(200, this.bufferSize === 0 ? 'OK' : 'Buffer too large: PBSZ=0');
|
||||
handler: function ({ command } = {}) {
|
||||
if (!this.secure) return this.reply(202, 'Not supported')
|
||||
this.bufferSize = parseInt(command.arg, 10)
|
||||
return this.reply(200, this.bufferSize === 0 ? 'OK' : 'Buffer too large: PBSZ=0')
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Protection Buffer Size',
|
||||
@@ -11,4 +11,4 @@ module.exports = {
|
||||
no_auth: true,
|
||||
feat: 'PBSZ'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
const _ = require('lodash');
|
||||
const ActiveConnector = require('../../connector/active');
|
||||
import _ from 'lodash'
|
||||
import ActiveConnector from '../../connector/active.js'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'PORT',
|
||||
handler: function ({log, command} = {}) {
|
||||
this.connector = new ActiveConnector(this);
|
||||
handler: function ({ log, command } = {}) {
|
||||
this.connector = new ActiveConnector(this)
|
||||
|
||||
const rawConnection = _.get(command, 'arg', '').split(',');
|
||||
if (rawConnection.length !== 6) return this.reply(425);
|
||||
const rawConnection = _.get(command, 'arg', '').split(',')
|
||||
if (rawConnection.length !== 6) return this.reply(425)
|
||||
|
||||
const ip = rawConnection.slice(0, 4).map((b) => parseInt(b)).join('.');
|
||||
const portBytes = rawConnection.slice(4).map((p) => parseInt(p));
|
||||
const port = portBytes[0] * 256 + portBytes[1];
|
||||
const ip = rawConnection
|
||||
.slice(0, 4)
|
||||
.map((b) => parseInt(b))
|
||||
.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))
|
||||
.catch((err) => {
|
||||
log.error(err);
|
||||
return this.reply(err.code || 425, err.message);
|
||||
});
|
||||
return this.connector
|
||||
.setupConnection(ip, port)
|
||||
.then(() => this.reply(200))
|
||||
.catch((err) => {
|
||||
log.error(err)
|
||||
return this.reply(err.code || 425, err.message)
|
||||
})
|
||||
},
|
||||
syntax: '{{cmd}} <x>,<x>,<x>,<x>,<y>,<y>',
|
||||
description: 'Specifies an address and port to which the server should connect'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
const _ = require('lodash');
|
||||
import _ from 'lodash'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'PROT',
|
||||
handler: function ({command} = {}) {
|
||||
if (!this.secure) return this.reply(202, 'Not supported');
|
||||
if (!this.bufferSize && typeof this.bufferSize !== 'number') return this.reply(503);
|
||||
handler: function ({ command } = {}) {
|
||||
if (!this.secure) return this.reply(202, 'Not supported')
|
||||
if (!this.bufferSize && typeof this.bufferSize !== 'number') return this.reply(503)
|
||||
|
||||
switch (_.toUpper(command.arg)) {
|
||||
case 'P': return this.reply(200, 'OK');
|
||||
case 'P':
|
||||
return this.reply(200, 'OK')
|
||||
case 'C':
|
||||
case 'S':
|
||||
case 'E': return this.reply(536, 'Not supported');
|
||||
default: return this.reply(504);
|
||||
case 'E':
|
||||
return this.reply(536, 'Not supported')
|
||||
default:
|
||||
return this.reply(504)
|
||||
}
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
@@ -20,4 +23,4 @@ module.exports = {
|
||||
no_auth: true,
|
||||
feat: 'PROT'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
const Promise = require('bluebird');
|
||||
const escapePath = require('../../helpers/escape-path');
|
||||
import Promise from 'bluebird'
|
||||
import escapePath from '../../helpers/escape-path.js'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
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 ({ 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')
|
||||
|
||||
return Promise.try(() => this.fs.currentDirectory())
|
||||
.then((cwd) => {
|
||||
const path = cwd ? `"${escapePath(cwd)}"` : undefined;
|
||||
return this.reply(257, path);
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err);
|
||||
return this.reply(550, err.message);
|
||||
});
|
||||
.then((cwd) => {
|
||||
const path = cwd ? `"${escapePath(cwd)}"` : undefined
|
||||
return this.reply(257, path)
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err)
|
||||
return this.reply(550, err.message)
|
||||
})
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Print current working directory'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'QUIT',
|
||||
handler: function () {
|
||||
return this.close(221, 'Client called QUIT');
|
||||
return this.close(221, 'Client called QUIT')
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Disconnect',
|
||||
flags: {
|
||||
no_auth: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
const _ = require('lodash');
|
||||
import _ from 'lodash'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'REST',
|
||||
handler: function ({command} = {}) {
|
||||
const arg = _.get(command, 'arg');
|
||||
const byteCount = parseInt(arg, 10);
|
||||
handler: function ({ 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 this.reply(501, 'Byte count must be 0 or greater')
|
||||
|
||||
this.restByteCount = byteCount;
|
||||
return this.reply(350, `Restarting next transfer at ${byteCount}`);
|
||||
this.restByteCount = byteCount
|
||||
return this.reply(350, `Restarting next transfer at ${byteCount}`)
|
||||
},
|
||||
syntax: '{{cmd}} <byte-count>',
|
||||
description: 'Restart transfer from the specified point. Resets after any STORE or RETRIEVE'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,64 +1,66 @@
|
||||
const Promise = require('bluebird');
|
||||
import Promise from 'bluebird'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
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 ({ 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')
|
||||
|
||||
const filePath = command.arg;
|
||||
const filePath = command.arg
|
||||
|
||||
return this.connector.waitForConnection()
|
||||
.tap(() => this.commandSocket.pause())
|
||||
.then(() => Promise.try(() => this.fs.read(filePath, {start: this.restByteCount})))
|
||||
.then((fsResponse) => {
|
||||
let {stream, clientPath} = fsResponse;
|
||||
if (!stream && !clientPath) {
|
||||
stream = fsResponse;
|
||||
clientPath = filePath;
|
||||
}
|
||||
const serverPath = stream.path || filePath;
|
||||
return this.connector
|
||||
.waitForConnection()
|
||||
.tap(() => this.commandSocket.pause())
|
||||
.then(() => Promise.try(() => this.fs.read(filePath, { start: this.restByteCount })))
|
||||
.then((fsResponse) => {
|
||||
let { stream, clientPath } = fsResponse
|
||||
if (!stream && !clientPath) {
|
||||
stream = fsResponse
|
||||
clientPath = filePath
|
||||
}
|
||||
const serverPath = stream.path || filePath
|
||||
|
||||
const destroyConnection = (connection, reject) => (err) => {
|
||||
if (connection) connection.destroy(err);
|
||||
reject(err);
|
||||
};
|
||||
const destroyConnection = (connection, reject) => (err) => {
|
||||
if (connection) connection.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, () => stream && stream.resume());
|
||||
}
|
||||
});
|
||||
stream.once('end', () => resolve());
|
||||
stream.once('error', destroyConnection(this.connector.socket, reject));
|
||||
const eventsPromise = new Promise((resolve, reject) => {
|
||||
stream.on('data', (data) => {
|
||||
if (stream) stream.pause()
|
||||
if (this.connector.socket) {
|
||||
this.connector.socket.write(data, () => stream && stream.resume())
|
||||
}
|
||||
})
|
||||
stream.once('end', () => resolve())
|
||||
stream.once('error', destroyConnection(this.connector.socket, reject))
|
||||
|
||||
this.connector.socket.once('error', destroyConnection(stream, reject));
|
||||
});
|
||||
this.connector.socket.once('error', destroyConnection(stream, reject))
|
||||
})
|
||||
|
||||
this.restByteCount = 0;
|
||||
this.restByteCount = 0
|
||||
|
||||
return this.reply(150).then(() => stream.resume() && this.connector.socket.resume())
|
||||
.then(() => eventsPromise)
|
||||
.tap(() => this.emit('RETR', null, serverPath))
|
||||
.then(() => this.reply(226, clientPath))
|
||||
.then(() => stream.destroy && stream.destroy());
|
||||
})
|
||||
.catch(Promise.TimeoutError, (err) => {
|
||||
log.error(err);
|
||||
return this.reply(425, 'No connection established');
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err);
|
||||
this.emit('RETR', err);
|
||||
return this.reply(551, err.message);
|
||||
})
|
||||
.then(() => {
|
||||
this.connector.end();
|
||||
this.commandSocket.resume();
|
||||
});
|
||||
return this.reply(150)
|
||||
.then(() => stream.resume() && this.connector.socket.resume())
|
||||
.then(() => eventsPromise)
|
||||
.tap(() => this.emit('RETR', null, serverPath))
|
||||
.then(() => this.reply(226, clientPath))
|
||||
.then(() => stream.destroy && stream.destroy())
|
||||
})
|
||||
.catch(Promise.TimeoutError, (err) => {
|
||||
log.error(err)
|
||||
return this.reply(425, 'No connection established')
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err)
|
||||
this.emit('RETR', err)
|
||||
return this.reply(551, err.message)
|
||||
})
|
||||
.then(() => {
|
||||
this.connector.end()
|
||||
this.commandSocket.resume()
|
||||
})
|
||||
},
|
||||
syntax: '{{cmd}} <path>',
|
||||
description: 'Retrieve a copy of the file'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
const {handler: dele} = require('./dele');
|
||||
import dele from './dele.js'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: ['RMD', 'XRMD'],
|
||||
handler: function (args) {
|
||||
return dele.call(this, args);
|
||||
return dele.handler.call(this, args)
|
||||
},
|
||||
syntax: '{{cmd}} <path>',
|
||||
description: 'Remove a directory'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
const Promise = require('bluebird');
|
||||
import Promise from 'bluebird'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
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 ({ 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')
|
||||
|
||||
const fileName = command.arg;
|
||||
const fileName = command.arg
|
||||
return Promise.try(() => this.fs.get(fileName))
|
||||
.then(() => {
|
||||
this.renameFrom = fileName;
|
||||
return this.reply(350);
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err);
|
||||
return this.reply(550, err.message);
|
||||
});
|
||||
.then(() => {
|
||||
this.renameFrom = fileName
|
||||
return this.reply(350)
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err)
|
||||
return this.reply(550, err.message)
|
||||
})
|
||||
},
|
||||
syntax: '{{cmd}} <name>',
|
||||
description: 'Rename from'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
const Promise = require('bluebird');
|
||||
import Promise from 'bluebird'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'RNTO',
|
||||
handler: function ({log, command} = {}) {
|
||||
if (!this.renameFrom) return this.reply(503);
|
||||
handler: function ({ log, command } = {}) {
|
||||
if (!this.renameFrom) return this.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 (!this.fs) return this.reply(550, 'File system not instantiated')
|
||||
if (!this.fs.rename) return this.reply(402, 'Not supported by file system')
|
||||
|
||||
const from = this.renameFrom;
|
||||
const to = command.arg;
|
||||
const from = this.renameFrom
|
||||
const to = command.arg
|
||||
|
||||
return Promise.try(() => this.fs.rename(from, to))
|
||||
.then(() => {
|
||||
return this.reply(250);
|
||||
})
|
||||
.tap(() => this.emit('RNTO', null, to))
|
||||
.catch((err) => {
|
||||
log.error(err);
|
||||
this.emit('RNTO', err);
|
||||
return this.reply(550, err.message);
|
||||
})
|
||||
.then(() => {
|
||||
delete this.renameFrom;
|
||||
});
|
||||
.then(() => {
|
||||
return this.reply(250)
|
||||
})
|
||||
.tap(() => this.emit('RNTO', null, to))
|
||||
.catch((err) => {
|
||||
log.error(err)
|
||||
this.emit('RNTO', err)
|
||||
return this.reply(550, err.message)
|
||||
})
|
||||
.then(() => {
|
||||
delete this.renameFrom
|
||||
})
|
||||
},
|
||||
syntax: '{{cmd}} <name>',
|
||||
description: 'Rename to'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
const Promise = require('bluebird');
|
||||
import Promise from 'bluebird'
|
||||
|
||||
module.exports = function ({log, command} = {}) {
|
||||
if (!this.fs) return this.reply(550, 'File system not instantiated');
|
||||
if (!this.fs.chmod) return this.reply(402, 'Not supported by file system');
|
||||
export default function ({ log, command } = {}) {
|
||||
if (!this.fs) return this.reply(550, 'File system not instantiated')
|
||||
if (!this.fs.chmod) return this.reply(402, 'Not supported by file system')
|
||||
|
||||
const [mode, ...fileNameParts] = command.arg.split(' ');
|
||||
const fileName = fileNameParts.join(' ');
|
||||
const [mode, ...fileNameParts] = command.arg.split(' ')
|
||||
const fileName = fileNameParts.join(' ')
|
||||
return Promise.try(() => this.fs.chmod(fileName, parseInt(mode, 8)))
|
||||
.then(() => {
|
||||
return this.reply(200);
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err);
|
||||
return this.reply(500);
|
||||
});
|
||||
};
|
||||
.then(() => {
|
||||
return this.reply(200)
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err)
|
||||
return this.reply(500)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
const Promise = require('bluebird');
|
||||
const _ = require('lodash');
|
||||
import Promise from 'bluebird'
|
||||
import _ from 'lodash'
|
||||
|
||||
const registry = require('./registry');
|
||||
import registry from './registry.js'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'SITE',
|
||||
handler: function ({log, command} = {}) {
|
||||
const rawSubCommand = _.get(command, 'arg', '');
|
||||
const subCommand = this.commands.parse(rawSubCommand);
|
||||
const subLog = log.child({subverb: subCommand.directive});
|
||||
handler: function ({ log, command } = {}) {
|
||||
const rawSubCommand = _.get(command, 'arg', '')
|
||||
const subCommand = this.commands.parse(rawSubCommand)
|
||||
const subLog = log.child({ subverb: subCommand.directive })
|
||||
|
||||
if (!registry.hasOwnProperty(subCommand.directive)) return this.reply(502);
|
||||
if (!registry.hasOwnProperty(subCommand.directive)) return this.reply(502)
|
||||
|
||||
const handler = registry[subCommand.directive].handler.bind(this);
|
||||
return Promise.resolve(handler({log: subLog, command: subCommand}));
|
||||
const handler = registry[subCommand.directive].handler.bind(this)
|
||||
return Promise.resolve(handler({ log: subLog, command: subCommand }))
|
||||
},
|
||||
syntax: '{{cmd}} <subVerb> [...<subParams>]',
|
||||
description: 'Sends site specific commands to remote server'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
module.exports = {
|
||||
import chmod from './chmod.js'
|
||||
export default {
|
||||
CHMOD: {
|
||||
handler: require('./chmod')
|
||||
handler: chmod
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
const Promise = require('bluebird');
|
||||
import Promise from 'bluebird'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
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 ({ 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')
|
||||
|
||||
return Promise.try(() => this.fs.get(command.arg))
|
||||
.then((fileStat) => {
|
||||
return this.reply(213, {message: fileStat.size});
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err);
|
||||
return this.reply(550, err.message);
|
||||
});
|
||||
.then((fileStat) => {
|
||||
return this.reply(213, { message: fileStat.size })
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err)
|
||||
return this.reply(550, err.message)
|
||||
})
|
||||
},
|
||||
syntax: '{{cmd}} <path>',
|
||||
description: 'Return the size of a file',
|
||||
flags: {
|
||||
feat: 'SIZE'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,45 +1,43 @@
|
||||
const _ = require('lodash');
|
||||
const Promise = require('bluebird');
|
||||
const getFileStat = require('../../helpers/file-stat');
|
||||
import _ from 'lodash'
|
||||
import Promise from 'bluebird'
|
||||
import getFileStat from '../../helpers/file-stat.js'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'STAT',
|
||||
handler: function (args = {}) {
|
||||
const {log, command} = args;
|
||||
const path = _.get(command, 'arg');
|
||||
const { log, command } = args
|
||||
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 (!this.fs) return this.reply(550, 'File system not instantiated')
|
||||
if (!this.fs.get) return this.reply(402, 'Not supported by file system')
|
||||
|
||||
return Promise.try(() => this.fs.get(path))
|
||||
.then((stat) => {
|
||||
if (stat.isDirectory()) {
|
||||
if (!this.fs.list) return this.reply(402, 'Not supported by file system');
|
||||
.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]);
|
||||
}
|
||||
return [212, [stat]];
|
||||
})
|
||||
.then(([code, fileStats]) => {
|
||||
return Promise.map(fileStats, (file) => {
|
||||
const message = getFileStat(file, _.get(this, 'server.options.file_format', 'ls'));
|
||||
return {
|
||||
raw: true,
|
||||
message
|
||||
};
|
||||
return Promise.try(() => this.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'))
|
||||
return {
|
||||
raw: true,
|
||||
message
|
||||
}
|
||||
}).then((messages) => [code, messages])
|
||||
})
|
||||
.then(([code, messages]) => this.reply(code, 'Status begin', ...messages, 'Status end'))
|
||||
.catch((err) => {
|
||||
log.error(err)
|
||||
return this.reply(450, err.message)
|
||||
})
|
||||
.then((messages) => [code, messages]);
|
||||
})
|
||||
.then(([code, messages]) => this.reply(code, 'Status begin', ...messages, 'Status end'))
|
||||
.catch((err) => {
|
||||
log.error(err);
|
||||
return this.reply(450, err.message);
|
||||
});
|
||||
} else {
|
||||
return this.reply(211, 'Status OK');
|
||||
return this.reply(211, 'Status OK')
|
||||
}
|
||||
},
|
||||
syntax: '{{cmd}} [<path>]',
|
||||
description: 'Returns the current status'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,73 +1,75 @@
|
||||
const Promise = require('bluebird');
|
||||
import Promise from 'bluebird'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
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 ({ 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')
|
||||
|
||||
const append = command.directive === 'APPE';
|
||||
const fileName = command.arg;
|
||||
const append = command.directive === 'APPE'
|
||||
const fileName = command.arg
|
||||
|
||||
return this.connector.waitForConnection()
|
||||
.tap(() => this.commandSocket.pause())
|
||||
.then(() => Promise.try(() => this.fs.write(fileName, {append, start: this.restByteCount})))
|
||||
.then((fsResponse) => {
|
||||
let {stream, clientPath} = fsResponse;
|
||||
if (!stream && !clientPath) {
|
||||
stream = fsResponse;
|
||||
clientPath = fileName;
|
||||
}
|
||||
const serverPath = stream.path || fileName;
|
||||
|
||||
const destroyConnection = (connection, reject) => (err) => {
|
||||
try {
|
||||
if (connection) {
|
||||
if (connection.writable) connection.end();
|
||||
connection.destroy(err);
|
||||
}
|
||||
} finally {
|
||||
reject(err);
|
||||
return this.connector
|
||||
.waitForConnection()
|
||||
.tap(() => this.commandSocket.pause())
|
||||
.then(() => Promise.try(() => this.fs.write(fileName, { append, start: this.restByteCount })))
|
||||
.then((fsResponse) => {
|
||||
let { stream, clientPath } = fsResponse
|
||||
if (!stream && !clientPath) {
|
||||
stream = fsResponse
|
||||
clientPath = fileName
|
||||
}
|
||||
};
|
||||
const serverPath = stream.path || fileName
|
||||
|
||||
const streamPromise = new Promise((resolve, reject) => {
|
||||
stream.once('error', destroyConnection(this.connector.socket, reject));
|
||||
stream.once('finish', () => resolve());
|
||||
});
|
||||
const destroyConnection = (connection, reject) => (err) => {
|
||||
try {
|
||||
if (connection) {
|
||||
if (connection.writable) connection.end()
|
||||
connection.destroy(err)
|
||||
}
|
||||
} finally {
|
||||
reject(err)
|
||||
}
|
||||
}
|
||||
|
||||
const socketPromise = new Promise((resolve, reject) => {
|
||||
this.connector.socket.pipe(stream, {end: false});
|
||||
this.connector.socket.once('end', () => {
|
||||
if (stream.listenerCount('close')) stream.emit('close');
|
||||
else stream.end();
|
||||
resolve();
|
||||
});
|
||||
this.connector.socket.once('error', destroyConnection(stream, reject));
|
||||
});
|
||||
const streamPromise = new Promise((resolve, reject) => {
|
||||
stream.once('error', destroyConnection(this.connector.socket, reject))
|
||||
stream.once('finish', () => resolve())
|
||||
})
|
||||
|
||||
this.restByteCount = 0;
|
||||
const socketPromise = new Promise((resolve, reject) => {
|
||||
this.connector.socket.pipe(stream, { end: false })
|
||||
this.connector.socket.once('end', () => {
|
||||
if (stream.listenerCount('close')) stream.emit('close')
|
||||
else stream.end()
|
||||
resolve()
|
||||
})
|
||||
this.connector.socket.once('error', destroyConnection(stream, reject))
|
||||
})
|
||||
|
||||
return this.reply(150).then(() => this.connector.socket && this.connector.socket.resume())
|
||||
.then(() => Promise.all([streamPromise, socketPromise]))
|
||||
.tap(() => this.emit('STOR', null, serverPath))
|
||||
.then(() => this.reply(226, clientPath))
|
||||
.then(() => stream.destroy && stream.destroy());
|
||||
})
|
||||
.catch(Promise.TimeoutError, (err) => {
|
||||
log.error(err);
|
||||
return this.reply(425, 'No connection established');
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err);
|
||||
this.emit('STOR', err);
|
||||
return this.reply(550, err.message);
|
||||
})
|
||||
.then(() => {
|
||||
this.connector.end();
|
||||
this.commandSocket.resume();
|
||||
});
|
||||
this.restByteCount = 0
|
||||
|
||||
return this.reply(150)
|
||||
.then(() => this.connector.socket && this.connector.socket.resume())
|
||||
.then(() => Promise.all([streamPromise, socketPromise]))
|
||||
.tap(() => this.emit('STOR', null, serverPath))
|
||||
.then(() => this.reply(226, clientPath))
|
||||
.then(() => stream.destroy && stream.destroy())
|
||||
})
|
||||
.catch(Promise.TimeoutError, (err) => {
|
||||
log.error(err)
|
||||
return this.reply(425, 'No connection established')
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err)
|
||||
this.emit('STOR', err)
|
||||
return this.reply(550, err.message)
|
||||
})
|
||||
.then(() => {
|
||||
this.connector.end()
|
||||
this.commandSocket.resume()
|
||||
})
|
||||
},
|
||||
syntax: '{{cmd}} <path>',
|
||||
description: 'Store data as a file at the server site'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
const Promise = require('bluebird');
|
||||
const {handler: stor} = require('./stor');
|
||||
import Promise from 'bluebird'
|
||||
import stor from './stor.js'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'STOU',
|
||||
handler: function (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');
|
||||
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 = args.command.arg
|
||||
return Promise.try(() => this.fs.get(fileName))
|
||||
.then(() => Promise.try(() => this.fs.getUniqueName(fileName)))
|
||||
.catch(() => fileName)
|
||||
.then((name) => {
|
||||
args.command.arg = name;
|
||||
return stor.call(this, args);
|
||||
});
|
||||
.then(() => Promise.try(() => this.fs.getUniqueName(fileName)))
|
||||
.catch(() => fileName)
|
||||
.then((name) => {
|
||||
args.command.arg = name
|
||||
return stor.handler.call(this, args)
|
||||
})
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Store file uniquely'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'STRU',
|
||||
handler: function ({command} = {}) {
|
||||
return this.reply(/^F$/i.test(command.arg) ? 200 : 504);
|
||||
handler: function ({ command } = {}) {
|
||||
return this.reply(/^F$/i.test(command.arg) ? 200 : 504)
|
||||
},
|
||||
syntax: '{{cmd}} <structure>',
|
||||
description: 'Set file transfer structure',
|
||||
flags: {
|
||||
obsolete: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'SYST',
|
||||
handler: function () {
|
||||
return this.reply(215);
|
||||
return this.reply(215)
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Return system type',
|
||||
flags: {
|
||||
no_auth: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
module.exports = {
|
||||
export default {
|
||||
directive: 'TYPE',
|
||||
handler: function ({command} = {}) {
|
||||
handler: function ({ command } = {}) {
|
||||
if (/^A[0-9]?$/i.test(command.arg)) {
|
||||
this.transferType = 'ascii';
|
||||
this.transferType = 'ascii'
|
||||
} else if (/^L[0-9]?$/i.test(command.arg) || /^I$/i.test(command.arg)) {
|
||||
this.transferType = 'binary';
|
||||
this.transferType = 'binary'
|
||||
} else {
|
||||
return this.reply(501);
|
||||
return this.reply(501)
|
||||
}
|
||||
return this.reply(200, `Switch to "${this.transferType}" transfer mode.`);
|
||||
return this.reply(200, `Switch to "${this.transferType}" transfer mode.`)
|
||||
},
|
||||
syntax: '{{cmd}} <mode>',
|
||||
description: 'Set the transfer mode, binary (I) or ascii (A)',
|
||||
flags: {
|
||||
feat: 'TYPE A,I,L'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,28 +1,30 @@
|
||||
module.exports = {
|
||||
export default {
|
||||
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 ({ log, command } = {}) {
|
||||
if (this.username) return this.reply(530, 'Username already set')
|
||||
if (this.authenticated) return this.reply(230)
|
||||
|
||||
this.username = command.arg;
|
||||
if (!this.username) return this.reply(501, 'Must provide username');
|
||||
this.username = command.arg
|
||||
if (!this.username) return this.reply(501, 'Must provide username')
|
||||
|
||||
if (this.server.options.anonymous === true && this.username === 'anonymous' ||
|
||||
this.username === this.server.options.anonymous) {
|
||||
if (
|
||||
(this.server.options.anonymous === true && this.username === 'anonymous') ||
|
||||
this.username === this.server.options.anonymous
|
||||
) {
|
||||
return this.login(this.username, '@anonymous')
|
||||
.then(() => {
|
||||
return this.reply(230);
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err);
|
||||
return this.reply(530, err.message || 'Authentication failed');
|
||||
});
|
||||
.then(() => {
|
||||
return this.reply(230)
|
||||
})
|
||||
.catch((err) => {
|
||||
log.error(err)
|
||||
return this.reply(530, err.message || 'Authentication failed')
|
||||
})
|
||||
}
|
||||
return this.reply(331);
|
||||
return this.reply(331)
|
||||
},
|
||||
syntax: '{{cmd}} <username>',
|
||||
description: 'Authentication username',
|
||||
flags: {
|
||||
no_auth: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,50 +1,90 @@
|
||||
import abor from './registration/abor.js'
|
||||
import allo from './registration/allo.js'
|
||||
import appe from './registration/appe.js'
|
||||
import auth from './registration/auth.js'
|
||||
import cdup from './registration/cdup.js'
|
||||
import cwd from './registration/cwd.js'
|
||||
import dele from './registration/dele.js'
|
||||
import feat from './registration/feat.js'
|
||||
import help from './registration/help.js'
|
||||
import list from './registration/list.js'
|
||||
import mdtm from './registration/mdtm.js'
|
||||
import mkd from './registration/mkd.js'
|
||||
import mode from './registration/mode.js'
|
||||
import nlst from './registration/nlst.js'
|
||||
import noop from './registration/noop.js'
|
||||
import opts from './registration/opts.js'
|
||||
import pass from './registration/pass.js'
|
||||
import pasv from './registration/pasv.js'
|
||||
import port from './registration/port.js'
|
||||
import pwd from './registration/pwd.js'
|
||||
import quit from './registration/quit.js'
|
||||
import rest from './registration/rest.js'
|
||||
import retr from './registration/retr.js'
|
||||
import rmd from './registration/rmd.js'
|
||||
import rnfr from './registration/rnfr.js'
|
||||
import rnto from './registration/rnto.js'
|
||||
import site from './registration/site/index.js'
|
||||
import size from './registration/size.js'
|
||||
import stat from './registration/stat.js'
|
||||
import stor from './registration/stor.js'
|
||||
import stou from './registration/stou.js'
|
||||
import stru from './registration/stru.js'
|
||||
import syst from './registration/syst.js'
|
||||
import type from './registration/type.js'
|
||||
import user from './registration/user.js'
|
||||
import pbsz from './registration/pbsz.js'
|
||||
import prot from './registration/prot.js'
|
||||
import eprt from './registration/eprt.js'
|
||||
import epsv from './registration/epsv.js'
|
||||
|
||||
/* eslint no-return-assign: 0 */
|
||||
const commands = [
|
||||
require('./registration/abor'),
|
||||
require('./registration/allo'),
|
||||
require('./registration/appe'),
|
||||
require('./registration/auth'),
|
||||
require('./registration/cdup'),
|
||||
require('./registration/cwd'),
|
||||
require('./registration/dele'),
|
||||
require('./registration/feat'),
|
||||
require('./registration/help'),
|
||||
require('./registration/list'),
|
||||
require('./registration/mdtm'),
|
||||
require('./registration/mkd'),
|
||||
require('./registration/mode'),
|
||||
require('./registration/nlst'),
|
||||
require('./registration/noop'),
|
||||
require('./registration/opts'),
|
||||
require('./registration/pass'),
|
||||
require('./registration/pasv'),
|
||||
require('./registration/port'),
|
||||
require('./registration/pwd'),
|
||||
require('./registration/quit'),
|
||||
require('./registration/rest'),
|
||||
require('./registration/retr'),
|
||||
require('./registration/rmd'),
|
||||
require('./registration/rnfr'),
|
||||
require('./registration/rnto'),
|
||||
require('./registration/site'),
|
||||
require('./registration/size'),
|
||||
require('./registration/stat'),
|
||||
require('./registration/stor'),
|
||||
require('./registration/stou'),
|
||||
require('./registration/stru'),
|
||||
require('./registration/syst'),
|
||||
require('./registration/type'),
|
||||
require('./registration/user'),
|
||||
require('./registration/pbsz'),
|
||||
require('./registration/prot'),
|
||||
require('./registration/eprt'),
|
||||
require('./registration/epsv')
|
||||
];
|
||||
abor,
|
||||
allo,
|
||||
appe,
|
||||
auth,
|
||||
cdup,
|
||||
cwd,
|
||||
dele,
|
||||
feat,
|
||||
help,
|
||||
list,
|
||||
mdtm,
|
||||
mkd,
|
||||
mode,
|
||||
nlst,
|
||||
noop,
|
||||
opts,
|
||||
pass,
|
||||
pasv,
|
||||
port,
|
||||
pwd,
|
||||
quit,
|
||||
rest,
|
||||
retr,
|
||||
rmd,
|
||||
rnfr,
|
||||
rnto,
|
||||
site,
|
||||
size,
|
||||
stat,
|
||||
stor,
|
||||
stou,
|
||||
stru,
|
||||
syst,
|
||||
type,
|
||||
user,
|
||||
pbsz,
|
||||
prot,
|
||||
eprt,
|
||||
epsv
|
||||
]
|
||||
|
||||
const registry = commands.reduce((result, cmd) => {
|
||||
const aliases = Array.isArray(cmd.directive) ? cmd.directive : [cmd.directive];
|
||||
aliases.forEach((alias) => result[alias] = cmd);
|
||||
return result;
|
||||
}, {});
|
||||
const aliases = Array.isArray(cmd.directive) ? cmd.directive : [cmd.directive]
|
||||
aliases.forEach((alias) => (result[alias] = cmd))
|
||||
return result
|
||||
}, {})
|
||||
|
||||
module.exports = registry;
|
||||
export default registry
|
||||
|
||||
@@ -1,155 +1,162 @@
|
||||
const _ = require('lodash');
|
||||
const uuid = require('uuid');
|
||||
const Promise = require('bluebird');
|
||||
const EventEmitter = require('events');
|
||||
import _ from 'lodash'
|
||||
import uuid from 'uuid'
|
||||
import Promise from 'bluebird'
|
||||
import { EventEmitter } from 'node:events'
|
||||
import BaseConnector from './connector/base.js'
|
||||
import { FileSystem } from './fs.js'
|
||||
import Commands from './commands/index.js'
|
||||
import errors from './errors.js'
|
||||
import DEFAULT_MESSAGE from './messages.js'
|
||||
|
||||
const BaseConnector = require('./connector/base');
|
||||
const FileSystem = require('./fs');
|
||||
const Commands = require('./commands');
|
||||
const errors = require('./errors');
|
||||
const DEFAULT_MESSAGE = require('./messages');
|
||||
|
||||
class FtpConnection extends EventEmitter {
|
||||
export default class FtpConnection extends EventEmitter {
|
||||
constructor(server, options) {
|
||||
super();
|
||||
this.server = server;
|
||||
this.id = uuid.v4();
|
||||
this.commandSocket = options.socket;
|
||||
this.log = options.log.child({id: this.id, ip: this.ip});
|
||||
this.commands = new Commands(this);
|
||||
this.transferType = 'binary';
|
||||
this.encoding = 'utf8';
|
||||
this.bufferSize = false;
|
||||
this._restByteCount = 0;
|
||||
this._secure = false;
|
||||
super()
|
||||
this.server = server
|
||||
this.id = uuid.v4()
|
||||
this.commandSocket = options.socket
|
||||
this.log = options.log.child({ id: this.id, ip: this.ip })
|
||||
this.commands = new Commands(this)
|
||||
this.transferType = 'binary'
|
||||
this.encoding = 'utf8'
|
||||
this.bufferSize = false
|
||||
this._restByteCount = 0
|
||||
this._secure = false
|
||||
|
||||
this.connector = new BaseConnector(this);
|
||||
this.connector = new BaseConnector(this)
|
||||
|
||||
this.commandSocket.on('error', (err) => {
|
||||
this.log.error(err, 'Client error');
|
||||
this.server.emit('client-error', {connection: this, context: 'commandSocket', error: err});
|
||||
});
|
||||
this.commandSocket.on('data', this._handleData.bind(this));
|
||||
this.log.error(err, 'Client error')
|
||||
this.server.emit('client-error', { connection: this, context: 'commandSocket', error: err })
|
||||
})
|
||||
this.commandSocket.on('data', this._handleData.bind(this))
|
||||
this.commandSocket.on('timeout', () => {
|
||||
this.log.trace('Client timeout');
|
||||
this.close();
|
||||
});
|
||||
this.log.trace('Client timeout')
|
||||
this.close()
|
||||
})
|
||||
this.commandSocket.on('close', () => {
|
||||
if (this.connector) this.connector.end();
|
||||
if (this.commandSocket && !this.commandSocket.destroyed) this.commandSocket.destroy();
|
||||
this.removeAllListeners();
|
||||
});
|
||||
if (this.connector) this.connector.end()
|
||||
if (this.commandSocket && !this.commandSocket.destroyed) this.commandSocket.destroy()
|
||||
this.removeAllListeners()
|
||||
})
|
||||
}
|
||||
|
||||
_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));
|
||||
const messages = _.compact(data.toString(this.encoding).split('\r\n'))
|
||||
this.log.trace(messages)
|
||||
return Promise.mapSeries(messages, (message) => this.commands.handle(message))
|
||||
}
|
||||
|
||||
get ip() {
|
||||
try {
|
||||
return this.commandSocket ? this.commandSocket.remoteAddress : undefined;
|
||||
return this.commandSocket ? this.commandSocket.remoteAddress : undefined
|
||||
} catch (ex) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
get restByteCount() {
|
||||
return this._restByteCount > 0 ? this._restByteCount : undefined;
|
||||
return this._restByteCount > 0 ? this._restByteCount : undefined
|
||||
}
|
||||
set restByteCount(rbc) {
|
||||
this._restByteCount = rbc;
|
||||
this._restByteCount = rbc
|
||||
}
|
||||
|
||||
get secure() {
|
||||
return this.server.isTLS || this._secure;
|
||||
return this.server.isTLS || this._secure
|
||||
}
|
||||
set secure(sec) {
|
||||
this._secure = sec;
|
||||
this._secure = sec
|
||||
}
|
||||
|
||||
close(code = 421, message = 'Closing connection') {
|
||||
return Promise.resolve(code)
|
||||
.then((_code) => _code && this.reply(_code, message))
|
||||
.then(() => this.commandSocket && this.commandSocket.destroy());
|
||||
.then(() => this.commandSocket && this.commandSocket.destroy())
|
||||
}
|
||||
|
||||
login(username, password) {
|
||||
return Promise.try(() => {
|
||||
const loginListeners = this.server.listeners('login');
|
||||
const loginListeners = this.server.listeners('login')
|
||||
if (!loginListeners || !loginListeners.length) {
|
||||
if (!this.server.options.anonymous) throw new errors.GeneralError('No "login" listener setup', 500);
|
||||
if (!this.server.options.anonymous) throw new errors.GeneralError('No "login" listener setup', 500)
|
||||
} else {
|
||||
return this.server.emitPromise('login', {connection: this, username, password});
|
||||
return this.server.emitPromise('login', { connection: this, username, password })
|
||||
}
|
||||
}).then(({ root, cwd, fs, blacklist = [], whitelist = [] } = {}) => {
|
||||
this.authenticated = true
|
||||
this.commands.blacklist = _.concat(this.commands.blacklist, blacklist)
|
||||
this.commands.whitelist = _.concat(this.commands.whitelist, whitelist)
|
||||
this.fs = fs || new FileSystem(this, { root, cwd })
|
||||
})
|
||||
.then(({root, cwd, fs, blacklist = [], whitelist = []} = {}) => {
|
||||
this.authenticated = true;
|
||||
this.commands.blacklist = _.concat(this.commands.blacklist, blacklist);
|
||||
this.commands.whitelist = _.concat(this.commands.whitelist, whitelist);
|
||||
this.fs = fs || new FileSystem(this, {root, cwd});
|
||||
});
|
||||
}
|
||||
|
||||
reply(options = {}, ...letters) {
|
||||
const satisfyParameters = () => {
|
||||
if (typeof options === 'number') options = {code: options}; // allow passing in code as first param
|
||||
if (!Array.isArray(letters)) letters = [letters];
|
||||
if (!letters.length) letters = [{}];
|
||||
if (typeof options === 'number') options = { code: options } // allow passing in code as first param
|
||||
if (!Array.isArray(letters)) letters = [letters]
|
||||
if (!letters.length) letters = [{}]
|
||||
return Promise.map(letters, (promise, index) => {
|
||||
return Promise.resolve(promise)
|
||||
.then((letter) => {
|
||||
if (!letter) letter = {};
|
||||
else if (typeof letter === 'string') letter = {message: letter}; // allow passing in message as first param
|
||||
return Promise.resolve(promise).then((letter) => {
|
||||
if (!letter) letter = {}
|
||||
else if (typeof letter === 'string') letter = { message: letter } // allow passing in message as first param
|
||||
|
||||
if (!letter.socket) letter.socket = options.socket ? options.socket : this.commandSocket;
|
||||
if (!letter.socket) letter.socket = options.socket ? options.socket : this.commandSocket
|
||||
if (!options.useEmptyMessage) {
|
||||
if (!letter.message) letter.message = DEFAULT_MESSAGE[options.code] || 'No information';
|
||||
if (!letter.encoding) letter.encoding = this.encoding;
|
||||
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) => {
|
||||
if (!options.useEmptyMessage) {
|
||||
const seperator = !options.hasOwnProperty('eol') ?
|
||||
letters.length - 1 === index ? ' ' : '-' :
|
||||
options.eol ? ' ' : '-';
|
||||
message = !letter.raw ? _.compact([letter.code || options.code, message]).join(seperator) : message;
|
||||
letter.message = message;
|
||||
} else {
|
||||
letter.message = '';
|
||||
}
|
||||
return letter;
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
.then((message) => {
|
||||
if (!options.useEmptyMessage) {
|
||||
const seperator = !options.hasOwnProperty('eol')
|
||||
? letters.length - 1 === index
|
||||
? ' '
|
||||
: '-'
|
||||
: options.eol
|
||||
? ' '
|
||||
: '-'
|
||||
message = !letter.raw
|
||||
? _.compact([letter.code || options.code, message]).join(seperator)
|
||||
: message
|
||||
letter.message = message
|
||||
} else {
|
||||
letter.message = ''
|
||||
}
|
||||
return 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');
|
||||
this.log.trace(
|
||||
{ port: letter.socket.address().port, encoding: letter.encoding, message: letter.message },
|
||||
'Reply'
|
||||
)
|
||||
letter.socket.write(letter.message + '\r\n', letter.encoding, (error) => {
|
||||
if (error) {
|
||||
this.log.error('[Process Letter] Socket Write Error', { error: error.message });
|
||||
return reject(error);
|
||||
this.log.error('[Process Letter] Socket Write Error', { error: error.message })
|
||||
return reject(error)
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
resolve()
|
||||
})
|
||||
} else {
|
||||
this.log.trace({message: letter.message}, 'Could not write message');
|
||||
reject(new errors.SocketError('Socket not writable'));
|
||||
this.log.trace({ message: letter.message }, 'Could not write message')
|
||||
reject(new errors.SocketError('Socket not writable'))
|
||||
}
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
return satisfyParameters()
|
||||
.then((satisfiedLetters) => Promise.mapSeries(satisfiedLetters, (letter, index) => {
|
||||
return processLetter(letter, index);
|
||||
}))
|
||||
.catch((error) => {
|
||||
this.log.error('Satisfy Parameters Error', { error: error.message });
|
||||
});
|
||||
.then((satisfiedLetters) =>
|
||||
Promise.mapSeries(satisfiedLetters, (letter, index) => {
|
||||
return processLetter(letter, index)
|
||||
})
|
||||
)
|
||||
.catch((error) => {
|
||||
this.log.error('Satisfy Parameters Error', { error: error.message })
|
||||
})
|
||||
}
|
||||
}
|
||||
module.exports = FtpConnection;
|
||||
|
||||
@@ -1,54 +1,57 @@
|
||||
const {Socket} = require('net');
|
||||
const tls = require('tls');
|
||||
const ip = require('ip');
|
||||
const Promise = require('bluebird');
|
||||
const Connector = require('./base');
|
||||
const {SocketError} = require('../errors');
|
||||
import { Socket } from 'node:net'
|
||||
import tls from 'node:tls'
|
||||
import ip from 'ip'
|
||||
import Promise from 'bluebird'
|
||||
import Connector from './base.js'
|
||||
import errors from '../errors.js'
|
||||
|
||||
class Active extends Connector {
|
||||
export default class Active extends Connector {
|
||||
constructor(connection) {
|
||||
super(connection);
|
||||
this.type = 'active';
|
||||
super(connection)
|
||||
this.type = 'active'
|
||||
}
|
||||
|
||||
waitForConnection({timeout = 5000, delay = 250} = {}) {
|
||||
waitForConnection({ timeout = 5000, delay = 250 } = {}) {
|
||||
const checkSocket = () => {
|
||||
if (this.dataSocket && this.dataSocket.connected) {
|
||||
return Promise.resolve(this.dataSocket);
|
||||
return Promise.resolve(this.dataSocket)
|
||||
}
|
||||
return Promise.resolve().delay(delay)
|
||||
.then(() => checkSocket());
|
||||
};
|
||||
return Promise.resolve()
|
||||
.delay(delay)
|
||||
.then(() => checkSocket())
|
||||
}
|
||||
|
||||
return checkSocket().timeout(timeout);
|
||||
return checkSocket().timeout(timeout)
|
||||
}
|
||||
|
||||
setupConnection(host, port, family = 4) {
|
||||
const closeExistingServer = () => Promise.resolve(
|
||||
this.dataSocket ? this.dataSocket.destroy() : undefined);
|
||||
const closeExistingServer = () => Promise.resolve(this.dataSocket ? this.dataSocket.destroy() : undefined)
|
||||
|
||||
return closeExistingServer()
|
||||
.then(() => {
|
||||
return closeExistingServer().then(() => {
|
||||
if (!ip.isEqual(this.connection.commandSocket.remoteAddress, host)) {
|
||||
throw new SocketError('The given address is not yours', 500);
|
||||
throw new errors.SocketError('The given address is not yours', 500)
|
||||
}
|
||||
|
||||
this.dataSocket = new Socket();
|
||||
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();
|
||||
this.dataSocket = new Socket()
|
||||
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()
|
||||
|
||||
if (this.connection.secure) {
|
||||
const secureContext = tls.createSecureContext(this.server.options.tls);
|
||||
const secureContext = tls.createSecureContext(this.server.options.tls)
|
||||
const secureSocket = new tls.TLSSocket(this.dataSocket, {
|
||||
isServer: true,
|
||||
secureContext
|
||||
});
|
||||
this.dataSocket = secureSocket;
|
||||
})
|
||||
this.dataSocket = secureSocket
|
||||
}
|
||||
this.dataSocket.connected = true;
|
||||
});
|
||||
});
|
||||
this.dataSocket.connected = true
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
module.exports = Active;
|
||||
|
||||
@@ -1,53 +1,51 @@
|
||||
const Promise = require('bluebird');
|
||||
const errors = require('../errors');
|
||||
import Promise from 'bluebird'
|
||||
import errors from '../errors.js'
|
||||
|
||||
class Connector {
|
||||
export default class Connector {
|
||||
constructor(connection) {
|
||||
this.connection = connection;
|
||||
this.connection = connection
|
||||
|
||||
this.dataSocket = null;
|
||||
this.dataServer = null;
|
||||
this.type = false;
|
||||
this.dataSocket = null
|
||||
this.dataServer = null
|
||||
this.type = false
|
||||
}
|
||||
|
||||
get log() {
|
||||
return this.connection.log;
|
||||
return this.connection.log
|
||||
}
|
||||
|
||||
get socket() {
|
||||
return this.dataSocket;
|
||||
return this.dataSocket
|
||||
}
|
||||
|
||||
get server() {
|
||||
return this.connection.server;
|
||||
return this.connection.server
|
||||
}
|
||||
|
||||
waitForConnection() {
|
||||
return Promise.reject(new errors.ConnectorError('No connector setup, send PASV or PORT'));
|
||||
return Promise.reject(new errors.ConnectorError('No connector setup, send PASV or PORT'))
|
||||
}
|
||||
|
||||
closeSocket() {
|
||||
if (this.dataSocket) {
|
||||
const socket = this.dataSocket;
|
||||
this.dataSocket.end(() => socket && socket.destroy());
|
||||
this.dataSocket = null;
|
||||
const socket = this.dataSocket
|
||||
this.dataSocket.end(() => socket && socket.destroy())
|
||||
this.dataSocket = null
|
||||
}
|
||||
}
|
||||
|
||||
closeServer() {
|
||||
if (this.dataServer) {
|
||||
this.dataServer.close();
|
||||
this.dataServer = null;
|
||||
this.dataServer.close()
|
||||
this.dataServer = null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
end() {
|
||||
this.closeSocket();
|
||||
this.closeServer();
|
||||
this.closeSocket()
|
||||
this.closeServer()
|
||||
|
||||
this.type = false;
|
||||
this.connection.connector = new Connector(this);
|
||||
this.type = false
|
||||
this.connection.connector = new Connector(this)
|
||||
}
|
||||
}
|
||||
module.exports = Connector;
|
||||
|
||||
@@ -1,97 +1,121 @@
|
||||
const net = require('net');
|
||||
const tls = require('tls');
|
||||
const ip = require('ip');
|
||||
const Promise = require('bluebird');
|
||||
import net from 'node:net'
|
||||
import tls from 'node:tls'
|
||||
import ip from 'ip'
|
||||
import Promise from 'bluebird'
|
||||
|
||||
const Connector = require('./base');
|
||||
const errors = require('../errors');
|
||||
import Connector from './base.js'
|
||||
import errors from '../errors.js'
|
||||
|
||||
const CONNECT_TIMEOUT = 30 * 1000;
|
||||
const CONNECT_TIMEOUT = 30 * 1000
|
||||
|
||||
class Passive extends Connector {
|
||||
export default class Passive extends Connector {
|
||||
constructor(connection) {
|
||||
super(connection);
|
||||
this.type = 'passive';
|
||||
super(connection)
|
||||
this.type = 'passive'
|
||||
}
|
||||
|
||||
waitForConnection({timeout = 5000, delay = 50} = {}) {
|
||||
if (!this.dataServer) return Promise.reject(new errors.ConnectorError('Passive server not setup'));
|
||||
waitForConnection({ timeout = 5000, delay = 50 } = {}) {
|
||||
if (!this.dataServer) return Promise.reject(new errors.ConnectorError('Passive server not setup'))
|
||||
|
||||
const checkSocket = () => {
|
||||
if (this.dataServer && this.dataServer.listening && this.dataSocket && this.dataSocket.connected) {
|
||||
return Promise.resolve(this.dataSocket);
|
||||
return Promise.resolve(this.dataSocket)
|
||||
}
|
||||
return Promise.resolve().delay(delay)
|
||||
.then(() => checkSocket());
|
||||
};
|
||||
return Promise.resolve()
|
||||
.delay(delay)
|
||||
.then(() => checkSocket())
|
||||
}
|
||||
|
||||
return checkSocket().timeout(timeout);
|
||||
return checkSocket().timeout(timeout)
|
||||
}
|
||||
|
||||
setupServer() {
|
||||
this.closeServer();
|
||||
return this.server.getNextPasvPort()
|
||||
.then((port) => {
|
||||
this.dataSocket = null;
|
||||
let idleServerTimeout;
|
||||
this.closeServer()
|
||||
return this.server
|
||||
.getNextPasvPort()
|
||||
.then((port) => {
|
||||
this.dataSocket = null
|
||||
let idleServerTimeout
|
||||
|
||||
const connectionHandler = (socket) => {
|
||||
if (!ip.isEqual(this.connection.commandSocket.remoteAddress, socket.remoteAddress)) {
|
||||
this.log.error({
|
||||
pasv_connection: socket.remoteAddress,
|
||||
cmd_connection: this.connection.commandSocket.remoteAddress
|
||||
}, 'Connecting addresses do not match');
|
||||
const connectionHandler = (socket) => {
|
||||
if (!ip.isEqual(this.connection.commandSocket.remoteAddress, socket.remoteAddress)) {
|
||||
this.log.error(
|
||||
{
|
||||
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')
|
||||
.then(() => this.connection.close());
|
||||
}
|
||||
clearTimeout(idleServerTimeout);
|
||||
|
||||
this.log.trace({port, remoteAddress: socket.remoteAddress}, 'Passive connection fulfilled.');
|
||||
|
||||
this.dataSocket = socket;
|
||||
this.dataSocket.on('error', (err) => this.server && this.server.emit('client-error', {connection: this.connection, context: 'dataSocket', error: err}));
|
||||
this.dataSocket.once('close', () => this.closeServer());
|
||||
|
||||
if (!this.connection.secure) {
|
||||
this.dataSocket.connected = true;
|
||||
}
|
||||
};
|
||||
|
||||
const serverOptions = Object.assign({}, this.connection.secure ? this.server.options.tls : {}, {pauseOnConnect: true});
|
||||
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.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) => {
|
||||
if (err) reject(err);
|
||||
else {
|
||||
idleServerTimeout = setTimeout(() => this.closeServer(), CONNECT_TIMEOUT);
|
||||
|
||||
this.log.debug({port}, 'Passive connection listening');
|
||||
resolve(this.dataServer);
|
||||
socket.destroy()
|
||||
return this.connection
|
||||
.reply(550, 'Remote addresses do not match')
|
||||
.then(() => this.connection.close())
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
this.log.trace(error.message);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
clearTimeout(idleServerTimeout)
|
||||
|
||||
this.log.trace({ port, remoteAddress: socket.remoteAddress }, 'Passive connection fulfilled.')
|
||||
|
||||
this.dataSocket = socket
|
||||
this.dataSocket.on(
|
||||
'error',
|
||||
(err) =>
|
||||
this.server &&
|
||||
this.server.emit('client-error', {
|
||||
connection: this.connection,
|
||||
context: 'dataSocket',
|
||||
error: err
|
||||
})
|
||||
)
|
||||
this.dataSocket.once('close', () => this.closeServer())
|
||||
|
||||
if (!this.connection.secure) {
|
||||
this.dataSocket.connected = true
|
||||
}
|
||||
}
|
||||
|
||||
const serverOptions = Object.assign({}, this.connection.secure ? this.server.options.tls : {}, {
|
||||
pauseOnConnect: true
|
||||
})
|
||||
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.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) => {
|
||||
if (err) reject(err)
|
||||
else {
|
||||
idleServerTimeout = setTimeout(() => this.closeServer(), CONNECT_TIMEOUT)
|
||||
|
||||
this.log.debug({ port }, 'Passive connection listening')
|
||||
resolve(this.dataServer)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
.catch((error) => {
|
||||
this.log.trace(error.message)
|
||||
throw error
|
||||
})
|
||||
}
|
||||
}
|
||||
module.exports = Passive;
|
||||
|
||||
@@ -1,44 +1,38 @@
|
||||
|
||||
class GeneralError extends Error {
|
||||
constructor(message, code = 400) {
|
||||
super();
|
||||
this.code = code;
|
||||
this.name = 'GeneralError';
|
||||
this.message = message;
|
||||
super(message)
|
||||
this.code = code
|
||||
this.name = 'GeneralError'
|
||||
}
|
||||
}
|
||||
|
||||
class SocketError extends Error {
|
||||
constructor(message, code = 500) {
|
||||
super();
|
||||
this.code = code;
|
||||
this.name = 'SocketError';
|
||||
this.message = message;
|
||||
super(message)
|
||||
this.code = code
|
||||
this.name = 'SocketError'
|
||||
}
|
||||
}
|
||||
|
||||
class FileSystemError extends Error {
|
||||
constructor(message, code = 400) {
|
||||
super();
|
||||
this.code = code;
|
||||
this.name = 'FileSystemError';
|
||||
this.message = message;
|
||||
super(message)
|
||||
this.code = code
|
||||
this.name = 'FileSystemError'
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectorError extends Error {
|
||||
constructor(message, code = 400) {
|
||||
super();
|
||||
this.code = code;
|
||||
this.name = 'ConnectorError';
|
||||
this.message = message;
|
||||
super(message)
|
||||
this.code = code
|
||||
this.name = 'ConnectorError'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
SocketError,
|
||||
FileSystemError,
|
||||
ConnectorError,
|
||||
GeneralError
|
||||
};
|
||||
}
|
||||
|
||||
170
src/fs.js
170
src/fs.js
@@ -1,141 +1,145 @@
|
||||
const _ = require('lodash');
|
||||
const nodePath = require('path');
|
||||
const uuid = require('uuid');
|
||||
const Promise = require('bluebird');
|
||||
const {createReadStream, createWriteStream, constants} = require('fs');
|
||||
const fsAsync = require('./helpers/fs-async');
|
||||
const errors = require('./errors');
|
||||
import _ from 'lodash'
|
||||
import nodePath from 'node:path'
|
||||
import uuid from 'uuid'
|
||||
import Promise from 'bluebird'
|
||||
import { createReadStream, createWriteStream, constants } from 'node:fs'
|
||||
import fsAsync from './helpers/fs-async.js'
|
||||
import errors from './errors.js'
|
||||
|
||||
const UNIX_SEP_REGEX = /\//g;
|
||||
const WIN_SEP_REGEX = /\\/g;
|
||||
const UNIX_SEP_REGEX = /\//g
|
||||
const WIN_SEP_REGEX = /\\/g
|
||||
|
||||
class FileSystem {
|
||||
constructor(connection, {root, cwd} = {}) {
|
||||
this.connection = connection;
|
||||
this.cwd = nodePath.normalize((cwd || '/').replace(WIN_SEP_REGEX, '/'));
|
||||
this._root = nodePath.resolve(root || process.cwd());
|
||||
export default class FileSystem {
|
||||
constructor(connection, { root, cwd } = {}) {
|
||||
this.connection = connection
|
||||
this.cwd = nodePath.normalize((cwd || '/').replace(WIN_SEP_REGEX, '/'))
|
||||
this._root = nodePath.resolve(root || process.cwd())
|
||||
}
|
||||
|
||||
get root() {
|
||||
return this._root;
|
||||
return this._root
|
||||
}
|
||||
|
||||
_resolvePath(path = '.') {
|
||||
// Unix separators normalize nicer on both unix and win platforms
|
||||
const resolvedPath = path.replace(WIN_SEP_REGEX, '/');
|
||||
const resolvedPath = path.replace(WIN_SEP_REGEX, '/')
|
||||
|
||||
// Join cwd with new path
|
||||
const joinedPath = nodePath.isAbsolute(resolvedPath)
|
||||
? nodePath.normalize(resolvedPath)
|
||||
: nodePath.join('/', this.cwd, resolvedPath);
|
||||
: nodePath.join('/', this.cwd, resolvedPath)
|
||||
|
||||
// Create local filesystem path using the platform separator
|
||||
const fsPath = nodePath.resolve(nodePath.join(this.root, joinedPath)
|
||||
.replace(UNIX_SEP_REGEX, nodePath.sep)
|
||||
.replace(WIN_SEP_REGEX, nodePath.sep));
|
||||
const fsPath = nodePath.resolve(
|
||||
nodePath
|
||||
.join(this.root, joinedPath)
|
||||
.replace(UNIX_SEP_REGEX, nodePath.sep)
|
||||
.replace(WIN_SEP_REGEX, nodePath.sep)
|
||||
)
|
||||
|
||||
// Create FTP client path using unix separator
|
||||
const clientPath = joinedPath.replace(WIN_SEP_REGEX, '/');
|
||||
const clientPath = joinedPath.replace(WIN_SEP_REGEX, '/')
|
||||
|
||||
return {
|
||||
clientPath,
|
||||
fsPath
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
currentDirectory() {
|
||||
return this.cwd;
|
||||
return this.cwd
|
||||
}
|
||||
|
||||
get(fileName) {
|
||||
const {fsPath} = this._resolvePath(fileName);
|
||||
return fsAsync.stat(fsPath)
|
||||
.then((stat) => _.set(stat, 'name', fileName));
|
||||
const { fsPath } = this._resolvePath(fileName)
|
||||
return fsAsync.stat(fsPath).then((stat) => _.set(stat, 'name', fileName))
|
||||
}
|
||||
|
||||
list(path = '.') {
|
||||
const {fsPath} = this._resolvePath(path);
|
||||
return fsAsync.readdir(fsPath)
|
||||
.then((fileNames) => {
|
||||
return Promise.map(fileNames, (fileName) => {
|
||||
const filePath = nodePath.join(fsPath, fileName);
|
||||
return fsAsync.access(filePath, constants.F_OK)
|
||||
.then(() => {
|
||||
return fsAsync.stat(filePath)
|
||||
.then((stat) => _.set(stat, 'name', fileName));
|
||||
const { fsPath } = this._resolvePath(path)
|
||||
return fsAsync
|
||||
.readdir(fsPath)
|
||||
.then((fileNames) => {
|
||||
return Promise.map(fileNames, (fileName) => {
|
||||
const filePath = nodePath.join(fsPath, fileName)
|
||||
return fsAsync
|
||||
.access(filePath, constants.F_OK)
|
||||
.then(() => {
|
||||
return fsAsync.stat(filePath).then((stat) => _.set(stat, 'name', fileName))
|
||||
})
|
||||
.catch(() => null)
|
||||
})
|
||||
.catch(() => null);
|
||||
});
|
||||
})
|
||||
.then(_.compact);
|
||||
})
|
||||
.then(_.compact)
|
||||
}
|
||||
|
||||
chdir(path = '.') {
|
||||
const {fsPath, clientPath} = this._resolvePath(path);
|
||||
return fsAsync.stat(fsPath)
|
||||
.tap((stat) => {
|
||||
if (!stat.isDirectory()) throw new errors.FileSystemError('Not a valid directory');
|
||||
})
|
||||
.then(() => {
|
||||
this.cwd = clientPath;
|
||||
return this.currentDirectory();
|
||||
});
|
||||
const { fsPath, clientPath } = this._resolvePath(path)
|
||||
return fsAsync
|
||||
.stat(fsPath)
|
||||
.tap((stat) => {
|
||||
if (!stat.isDirectory()) throw new errors.FileSystemError('Not a valid directory')
|
||||
})
|
||||
.then(() => {
|
||||
this.cwd = clientPath
|
||||
return this.currentDirectory()
|
||||
})
|
||||
}
|
||||
|
||||
write(fileName, {append = false, start = undefined} = {}) {
|
||||
const {fsPath, clientPath} = this._resolvePath(fileName);
|
||||
const stream = createWriteStream(fsPath, {flags: !append ? 'w+' : 'a+', start});
|
||||
stream.once('error', () => fsAsync.unlink(fsPath));
|
||||
stream.once('close', () => stream.end());
|
||||
write(fileName, { append = false, start = undefined } = {}) {
|
||||
const { fsPath, clientPath } = this._resolvePath(fileName)
|
||||
const stream = createWriteStream(fsPath, { flags: !append ? 'w+' : 'a+', start })
|
||||
stream.once('error', () => fsAsync.unlink(fsPath))
|
||||
stream.once('close', () => stream.end())
|
||||
return {
|
||||
stream,
|
||||
clientPath
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
read(fileName, {start = undefined} = {}) {
|
||||
const {fsPath, clientPath} = this._resolvePath(fileName);
|
||||
return fsAsync.stat(fsPath)
|
||||
.tap((stat) => {
|
||||
if (stat.isDirectory()) throw new errors.FileSystemError('Cannot read a directory');
|
||||
})
|
||||
.then(() => {
|
||||
const stream = createReadStream(fsPath, {flags: 'r', start});
|
||||
return {
|
||||
stream,
|
||||
clientPath
|
||||
};
|
||||
});
|
||||
read(fileName, { start = undefined } = {}) {
|
||||
const { fsPath, clientPath } = this._resolvePath(fileName)
|
||||
return fsAsync
|
||||
.stat(fsPath)
|
||||
.tap((stat) => {
|
||||
if (stat.isDirectory()) throw new errors.FileSystemError('Cannot read a directory')
|
||||
})
|
||||
.then(() => {
|
||||
const stream = createReadStream(fsPath, { flags: 'r', start })
|
||||
return {
|
||||
stream,
|
||||
clientPath
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
delete(path) {
|
||||
const {fsPath} = this._resolvePath(path);
|
||||
return fsAsync.stat(fsPath)
|
||||
.then((stat) => {
|
||||
if (stat.isDirectory()) return fsAsync.rmdir(fsPath);
|
||||
else return fsAsync.unlink(fsPath);
|
||||
});
|
||||
const { fsPath } = this._resolvePath(path)
|
||||
return fsAsync.stat(fsPath).then((stat) => {
|
||||
if (stat.isDirectory()) return fsAsync.rmdir(fsPath)
|
||||
else return fsAsync.unlink(fsPath)
|
||||
})
|
||||
}
|
||||
|
||||
mkdir(path) {
|
||||
const {fsPath} = this._resolvePath(path);
|
||||
return fsAsync.mkdir(fsPath, { recursive: true })
|
||||
.then(() => fsPath);
|
||||
const { fsPath } = this._resolvePath(path)
|
||||
return fsAsync.mkdir(fsPath, { recursive: true }).then(() => fsPath)
|
||||
}
|
||||
|
||||
rename(from, to) {
|
||||
const {fsPath: fromPath} = this._resolvePath(from);
|
||||
const {fsPath: toPath} = this._resolvePath(to);
|
||||
return fsAsync.rename(fromPath, toPath);
|
||||
const { fsPath: fromPath } = this._resolvePath(from)
|
||||
const { fsPath: toPath } = this._resolvePath(to)
|
||||
return fsAsync.rename(fromPath, toPath)
|
||||
}
|
||||
|
||||
chmod(path, mode) {
|
||||
const {fsPath} = this._resolvePath(path);
|
||||
return fsAsync.chmod(fsPath, mode);
|
||||
const { fsPath } = this._resolvePath(path)
|
||||
return fsAsync.chmod(fsPath, mode)
|
||||
}
|
||||
|
||||
getUniqueName() {
|
||||
return uuid.v4().replace(/\W/g, '');
|
||||
return uuid.v4().replace(/\W/g, '')
|
||||
}
|
||||
}
|
||||
module.exports = FileSystem;
|
||||
|
||||
export { FileSystem }
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
module.exports = function (path) {
|
||||
return path
|
||||
.replace(/"/g, '""');
|
||||
};
|
||||
export default function (path) {
|
||||
return path.replace(/"/g, '""')
|
||||
}
|
||||
|
||||
@@ -1,46 +1,50 @@
|
||||
const _ = require('lodash');
|
||||
const moment = require('moment');
|
||||
const errors = require('../errors');
|
||||
import _ from 'lodash'
|
||||
import moment from 'moment'
|
||||
import errors from '../errors.js'
|
||||
|
||||
const FORMATS = {
|
||||
ls,
|
||||
ep
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = function (fileStat, format = 'ls') {
|
||||
if (typeof format === 'function') return format(fileStat);
|
||||
export default function (fileStat, format = 'ls') {
|
||||
if (typeof format === 'function') return format(fileStat)
|
||||
if (!FORMATS.hasOwnProperty(format)) {
|
||||
throw new errors.FileSystemError('Bad file stat formatter');
|
||||
throw new errors.FileSystemError('Bad file stat formatter')
|
||||
}
|
||||
return FORMATS[format](fileStat);
|
||||
};
|
||||
return FORMATS[format](fileStat)
|
||||
}
|
||||
|
||||
function ls(fileStat) {
|
||||
const now = moment.utc();
|
||||
const mtime = moment.utc(new Date(fileStat.mtime));
|
||||
const timeDiff = now.diff(mtime, 'months');
|
||||
const dateFormat = timeDiff < 6 ? 'MMM DD HH:mm' : 'MMM DD YYYY';
|
||||
const now = moment.utc()
|
||||
const mtime = moment.utc(new Date(fileStat.mtime))
|
||||
const timeDiff = now.diff(mtime, 'months')
|
||||
const dateFormat = timeDiff < 6 ? 'MMM DD HH:mm' : 'MMM DD YYYY'
|
||||
|
||||
return [
|
||||
fileStat.mode ? [
|
||||
fileStat.isDirectory() ? 'd' : '-',
|
||||
fileStat.mode & 256 ? 'r' : '-',
|
||||
fileStat.mode & 128 ? 'w' : '-',
|
||||
fileStat.mode & 64 ? 'x' : '-',
|
||||
fileStat.mode & 32 ? 'r' : '-',
|
||||
fileStat.mode & 16 ? 'w' : '-',
|
||||
fileStat.mode & 8 ? 'x' : '-',
|
||||
fileStat.mode & 4 ? 'r' : '-',
|
||||
fileStat.mode & 2 ? 'w' : '-',
|
||||
fileStat.mode & 1 ? 'x' : '-'
|
||||
].join('') : fileStat.isDirectory() ? 'drwxr-xr-x' : '-rwxr-xr-x',
|
||||
fileStat.mode
|
||||
? [
|
||||
fileStat.isDirectory() ? 'd' : '-',
|
||||
fileStat.mode & 256 ? 'r' : '-',
|
||||
fileStat.mode & 128 ? 'w' : '-',
|
||||
fileStat.mode & 64 ? 'x' : '-',
|
||||
fileStat.mode & 32 ? 'r' : '-',
|
||||
fileStat.mode & 16 ? 'w' : '-',
|
||||
fileStat.mode & 8 ? 'x' : '-',
|
||||
fileStat.mode & 4 ? 'r' : '-',
|
||||
fileStat.mode & 2 ? 'w' : '-',
|
||||
fileStat.mode & 1 ? 'x' : '-'
|
||||
].join('')
|
||||
: fileStat.isDirectory()
|
||||
? 'drwxr-xr-x'
|
||||
: '-rwxr-xr-x',
|
||||
'1',
|
||||
fileStat.uid || 1,
|
||||
fileStat.gid || 1,
|
||||
_.padStart(fileStat.size, 12),
|
||||
_.padStart(mtime.format(dateFormat), 12),
|
||||
fileStat.name
|
||||
].join(' ');
|
||||
].join(' ')
|
||||
}
|
||||
|
||||
function ep(fileStat) {
|
||||
@@ -50,6 +54,6 @@ function ep(fileStat) {
|
||||
fileStat.mtime ? `m${moment.utc(new Date(fileStat.mtime)).format('X')}` : null,
|
||||
fileStat.mode ? `up${(fileStat.mode & 4095).toString(8)}` : null,
|
||||
fileStat.isDirectory() ? '/' : 'r'
|
||||
]).join(',');
|
||||
return `+${facts}\t${fileStat.name}`;
|
||||
]).join(',')
|
||||
return `+${facts}\t${fileStat.name}`
|
||||
}
|
||||
|
||||
@@ -1,61 +1,59 @@
|
||||
const net = require('net');
|
||||
const errors = require('../errors');
|
||||
import net from 'node:net'
|
||||
import errors from '../errors.js'
|
||||
|
||||
const MAX_PORT = 65535;
|
||||
const MAX_PORT_CHECK_ATTEMPT = 5;
|
||||
const MAX_PORT = 65535
|
||||
const MAX_PORT_CHECK_ATTEMPT = 5
|
||||
|
||||
function* portNumberGenerator(min, max = MAX_PORT) {
|
||||
let current = min;
|
||||
let current = min
|
||||
while (true) {
|
||||
if (current > MAX_PORT || current > max) {
|
||||
current = min;
|
||||
current = min
|
||||
}
|
||||
yield current++;
|
||||
yield current++
|
||||
}
|
||||
}
|
||||
|
||||
function getNextPortFactory(host, portMin, portMax, maxAttempts = MAX_PORT_CHECK_ATTEMPT) {
|
||||
const nextPortNumber = portNumberGenerator(portMin, portMax);
|
||||
const nextPortNumber = portNumberGenerator(portMin, portMax)
|
||||
|
||||
return () => new Promise((resolve, reject) => {
|
||||
const portCheckServer = net.createServer();
|
||||
portCheckServer.maxConnections = 0;
|
||||
return () =>
|
||||
new Promise((resolve, reject) => {
|
||||
const portCheckServer = net.createServer()
|
||||
portCheckServer.maxConnections = 0
|
||||
|
||||
let attemptCount = 0;
|
||||
const tryGetPort = () => {
|
||||
attemptCount++;
|
||||
if (attemptCount > maxAttempts) {
|
||||
reject(new errors.ConnectorError('Unable to find valid port'));
|
||||
return;
|
||||
}
|
||||
|
||||
const {value: port} = nextPortNumber.next();
|
||||
|
||||
portCheckServer.removeAllListeners();
|
||||
portCheckServer.once('error', (err) => {
|
||||
if (['EADDRINUSE'].includes(err.code)) {
|
||||
tryGetPort();
|
||||
} else {
|
||||
reject(err);
|
||||
let attemptCount = 0
|
||||
const tryGetPort = () => {
|
||||
attemptCount++
|
||||
if (attemptCount > maxAttempts) {
|
||||
reject(new errors.ConnectorError('Unable to find valid port'))
|
||||
return
|
||||
}
|
||||
});
|
||||
portCheckServer.once('listening', () => {
|
||||
portCheckServer.removeAllListeners();
|
||||
portCheckServer.close(() => resolve(port));
|
||||
});
|
||||
|
||||
try {
|
||||
portCheckServer.listen(port, host);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
const { value: port } = nextPortNumber.next()
|
||||
|
||||
portCheckServer.removeAllListeners()
|
||||
portCheckServer.once('error', (err) => {
|
||||
if (['EADDRINUSE'].includes(err.code)) {
|
||||
tryGetPort()
|
||||
} else {
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
portCheckServer.once('listening', () => {
|
||||
portCheckServer.removeAllListeners()
|
||||
portCheckServer.close(() => resolve(port))
|
||||
})
|
||||
|
||||
try {
|
||||
portCheckServer.listen(port, host)
|
||||
} catch (err) {
|
||||
reject(err)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
tryGetPort();
|
||||
});
|
||||
tryGetPort()
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getNextPortFactory,
|
||||
portNumberGenerator
|
||||
};
|
||||
export { getNextPortFactory, portNumberGenerator }
|
||||
|
||||
@@ -1,18 +1,9 @@
|
||||
const fs = require('fs');
|
||||
const {promisify} = require('bluebird');
|
||||
import fs from 'node:fs'
|
||||
import Promise from 'bluebird'
|
||||
|
||||
const methods = [
|
||||
'stat',
|
||||
'readdir',
|
||||
'access',
|
||||
'unlink',
|
||||
'rmdir',
|
||||
'mkdir',
|
||||
'rename',
|
||||
'chmod'
|
||||
];
|
||||
const methods = ['stat', 'readdir', 'access', 'unlink', 'rmdir', 'mkdir', 'rename', 'chmod']
|
||||
|
||||
module.exports = methods.reduce((obj, method) => {
|
||||
obj[method] = promisify(fs[method]);
|
||||
return obj;
|
||||
}, {});
|
||||
export default methods.reduce((obj, method) => {
|
||||
obj[method] = Promise.promisify(fs[method])
|
||||
return obj
|
||||
}, {})
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module.exports.isLocalIP = function(ip) {
|
||||
return ip === '127.0.0.1' || ip == '::1';
|
||||
export const isLocalIP = function (ip) {
|
||||
return ip === '127.0.0.1' || ip == '::1'
|
||||
}
|
||||
|
||||
245
src/index.js
245
src/index.js
@@ -1,173 +1,194 @@
|
||||
const _ = require('lodash');
|
||||
const Promise = require('bluebird');
|
||||
const nodeUrl = require('url');
|
||||
const buyan = require('bunyan');
|
||||
const net = require('net');
|
||||
const tls = require('tls');
|
||||
const EventEmitter = require('events');
|
||||
import _ from 'lodash'
|
||||
import Promise from 'bluebird'
|
||||
import nodeUrl from 'node:url'
|
||||
import buyan from 'bunyan'
|
||||
import net from 'node:net'
|
||||
import tls from 'node:tls'
|
||||
import { EventEmitter } from 'node:events'
|
||||
import Connection from './connection.js'
|
||||
import { getNextPortFactory } from './helpers/find-port.js'
|
||||
|
||||
const Connection = require('./connection');
|
||||
const {getNextPortFactory} = require('./helpers/find-port');
|
||||
|
||||
class FtpServer extends EventEmitter {
|
||||
export default class FtpServer extends EventEmitter {
|
||||
constructor(options = {}) {
|
||||
super();
|
||||
this.options = Object.assign({
|
||||
log: buyan.createLogger({name: 'ftp-srv'}),
|
||||
url: 'ftp://127.0.0.1:21',
|
||||
pasv_min: 1024,
|
||||
pasv_max: 65535,
|
||||
pasv_url: null,
|
||||
anonymous: false,
|
||||
file_format: 'ls',
|
||||
blacklist: [],
|
||||
whitelist: [],
|
||||
greeting: null,
|
||||
tls: false,
|
||||
timeout: 0
|
||||
}, options);
|
||||
super()
|
||||
this.options = Object.assign(
|
||||
{
|
||||
log: buyan.createLogger({ name: 'ftp-srv' }),
|
||||
url: 'ftp://127.0.0.1:21',
|
||||
pasv_min: 1024,
|
||||
pasv_max: 65535,
|
||||
pasv_url: null,
|
||||
random_pasv_port: false,
|
||||
anonymous: false,
|
||||
file_format: 'ls',
|
||||
blacklist: [],
|
||||
whitelist: [],
|
||||
greeting: null,
|
||||
tls: false,
|
||||
timeout: 0
|
||||
},
|
||||
options
|
||||
)
|
||||
|
||||
this._greeting = this.setupGreeting(this.options.greeting);
|
||||
this._features = this.setupFeaturesMessage();
|
||||
this._greeting = this.setupGreeting(this.options.greeting)
|
||||
this._features = this.setupFeaturesMessage()
|
||||
|
||||
delete this.options.greeting;
|
||||
delete this.options.greeting
|
||||
|
||||
this.connections = {};
|
||||
this.log = this.options.log;
|
||||
this.url = nodeUrl.parse(this.options.url);
|
||||
this.getNextPasvPort = getNextPortFactory(
|
||||
_.get(this, 'url.hostname'),
|
||||
_.get(this, 'options.pasv_min'),
|
||||
_.get(this, 'options.pasv_max'));
|
||||
this.connections = {}
|
||||
this.log = this.options.log
|
||||
this.url = nodeUrl.parse(this.options.url)
|
||||
this.getNextPasvPort = this.getNextPortFactory()
|
||||
|
||||
const timeout = Number(this.options.timeout);
|
||||
this.options.timeout = isNaN(timeout) ? 0 : Number(timeout);
|
||||
const timeout = Number(this.options.timeout)
|
||||
this.options.timeout = isNaN(timeout) ? 0 : Number(timeout)
|
||||
|
||||
const serverConnectionHandler = (socket) => {
|
||||
this.options.timeout > 0 && socket.setTimeout(this.options.timeout);
|
||||
let connection = new Connection(this, {log: this.log, socket});
|
||||
this.connections[connection.id] = connection;
|
||||
this.options.timeout > 0 && socket.setTimeout(this.options.timeout)
|
||||
let connection = new Connection(this, { log: this.log, socket })
|
||||
this.connections[connection.id] = connection
|
||||
|
||||
socket.on('close', () => this.disconnectClient(connection.id));
|
||||
socket.on('close', () => this.disconnectClient(connection.id))
|
||||
socket.once('close', () => {
|
||||
this.emit('disconnect', {connection, id: connection.id, newConnectionCount: Object.keys(this.connections).length});
|
||||
this.emit('disconnect', {
|
||||
connection,
|
||||
id: connection.id,
|
||||
newConnectionCount: Object.keys(this.connections).length
|
||||
})
|
||||
})
|
||||
|
||||
this.emit('connect', {connection, id: connection.id, newConnectionCount: Object.keys(this.connections).length});
|
||||
|
||||
const greeting = this._greeting || [];
|
||||
const features = this._features || 'Ready';
|
||||
return connection.reply(220, ...greeting, features)
|
||||
.then(() => socket.resume());
|
||||
};
|
||||
const serverOptions = Object.assign({}, this.isTLS ? this.options.tls : {}, {pauseOnConnect: true});
|
||||
this.emit('connect', {
|
||||
connection,
|
||||
id: connection.id,
|
||||
newConnectionCount: Object.keys(this.connections).length
|
||||
})
|
||||
|
||||
this.server = (this.isTLS ? tls : net).createServer(serverOptions, serverConnectionHandler);
|
||||
const greeting = this._greeting || []
|
||||
const features = this._features || 'Ready'
|
||||
return connection.reply(220, ...greeting, features).then(() => socket.resume())
|
||||
}
|
||||
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.emit('server-error', {error: err});
|
||||
});
|
||||
|
||||
const quit = _.debounce(this.quit.bind(this), 100);
|
||||
this.log.error(err, '[Event] error')
|
||||
this.emit('server-error', { error: err })
|
||||
})
|
||||
|
||||
process.on('SIGTERM', quit);
|
||||
process.on('SIGINT', quit);
|
||||
process.on('SIGQUIT', quit);
|
||||
const quit = _.debounce(this.quit.bind(this), 100)
|
||||
|
||||
process.on('SIGTERM', quit)
|
||||
process.on('SIGINT', quit)
|
||||
process.on('SIGQUIT', quit)
|
||||
}
|
||||
|
||||
getNextPortFactory() {
|
||||
const nextPortFactory = getNextPortFactory(
|
||||
_.get(this, 'url.hostname'),
|
||||
_.get(this, 'options.pasv_min'),
|
||||
_.get(this, 'options.pasv_max')
|
||||
)
|
||||
const randomPortFactory = async () => 0
|
||||
|
||||
return _.get(this, 'options.random_pasv_port') ? randomPortFactory : nextPortFactory
|
||||
}
|
||||
|
||||
get isTLS() {
|
||||
return this.url.protocol === 'ftps:' && this.options.tls;
|
||||
return this.url.protocol === 'ftps:' && this.options.tls
|
||||
}
|
||||
|
||||
listen() {
|
||||
if (!this.options.pasv_url) {
|
||||
this.log.warn('Passive URL not set. Passive connections not available.');
|
||||
this.log.warn('Passive URL not set. Passive connections not available.')
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.server.once('error', reject);
|
||||
this.server.once('error', reject)
|
||||
this.server.listen(this.url.port, this.url.hostname, (err) => {
|
||||
this.server.removeListener('error', reject);
|
||||
if (err) return reject(err);
|
||||
this.log.info({
|
||||
protocol: this.url.protocol.replace(/\W/g, ''),
|
||||
ip: this.url.hostname,
|
||||
port: this.url.port
|
||||
}, 'Listening');
|
||||
resolve('Listening');
|
||||
});
|
||||
});
|
||||
this.server.removeListener('error', reject)
|
||||
if (err) return reject(err)
|
||||
this.log.info(
|
||||
{
|
||||
protocol: this.url.protocol.replace(/\W/g, ''),
|
||||
ip: this.url.hostname,
|
||||
port: this.url.port
|
||||
},
|
||||
'Listening'
|
||||
)
|
||||
resolve('Listening')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
emitPromise(action, ...data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const params = _.concat(data, [resolve, reject]);
|
||||
this.emit.call(this, action, ...params);
|
||||
});
|
||||
const params = _.concat(data, [resolve, reject])
|
||||
this.emit.call(this, action, ...params)
|
||||
})
|
||||
}
|
||||
|
||||
setupGreeting(greet) {
|
||||
if (!greet) return [];
|
||||
const greeting = Array.isArray(greet) ? greet : greet.split('\n');
|
||||
return greeting;
|
||||
if (!greet) return []
|
||||
const greeting = Array.isArray(greet) ? greet : greet.split('\n')
|
||||
return greeting
|
||||
}
|
||||
|
||||
setupFeaturesMessage() {
|
||||
let features = [];
|
||||
if (this.options.anonymous) features.push('a');
|
||||
let features = []
|
||||
if (this.options.anonymous) features.push('a')
|
||||
|
||||
if (features.length) {
|
||||
features.unshift('Features:');
|
||||
features.push('.');
|
||||
features.unshift('Features:')
|
||||
features.push('.')
|
||||
}
|
||||
return features.length ? features.join(' ') : 'Ready';
|
||||
return features.length ? features.join(' ') : 'Ready'
|
||||
}
|
||||
|
||||
disconnectClient(id) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const client = this.connections[id];
|
||||
if (!client) return resolve();
|
||||
delete this.connections[id];
|
||||
const client = this.connections[id]
|
||||
if (!client) return resolve()
|
||||
delete this.connections[id]
|
||||
|
||||
setTimeout(() => {
|
||||
reject(new Error('Timed out disconnecting the client'))
|
||||
}, this.options.timeout || 1000)
|
||||
|
||||
try {
|
||||
client.close(0);
|
||||
client.close(0)
|
||||
} catch (err) {
|
||||
this.log.error(err, 'Error closing connection', {id});
|
||||
this.log.error(err, 'Error closing connection', { id })
|
||||
}
|
||||
|
||||
resolve('Disconnected');
|
||||
});
|
||||
|
||||
resolve('Disconnected')
|
||||
})
|
||||
}
|
||||
|
||||
quit() {
|
||||
return this.close()
|
||||
.then(() => process.exit(0));
|
||||
return this.close().then(() => process.exit(0))
|
||||
}
|
||||
|
||||
close() {
|
||||
this.server.maxConnections = 0;
|
||||
this.emit('closing');
|
||||
this.log.info('Closing connections:', Object.keys(this.connections).length);
|
||||
this.server.maxConnections = 0
|
||||
this.emit('closing')
|
||||
this.log.info('Closing connections:', Object.keys(this.connections).length)
|
||||
|
||||
return Promise.all(Object.keys(this.connections).map((id) => this.disconnectClient(id)))
|
||||
.then(() => new Promise((resolve) => {
|
||||
this.server.close((err) => {
|
||||
this.log.info('Server closing...');
|
||||
if (err) this.log.error(err, 'Error closing server');
|
||||
resolve('Closed');
|
||||
});
|
||||
}))
|
||||
.then(() => {
|
||||
this.log.debug('Removing event listeners...')
|
||||
this.emit('closed', {});
|
||||
this.removeAllListeners();
|
||||
return;
|
||||
});
|
||||
.then(
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
this.server.close((err) => {
|
||||
this.log.info('Server closing...')
|
||||
if (err) this.log.error(err, 'Error closing server')
|
||||
resolve('Closed')
|
||||
})
|
||||
})
|
||||
)
|
||||
.then(() => {
|
||||
this.log.debug('Removing event listeners...')
|
||||
this.emit('closed', {})
|
||||
this.removeAllListeners()
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
module.exports = FtpServer;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module.exports = {
|
||||
export default {
|
||||
// 100 - 199 :: Remarks
|
||||
100: 'The requested action is being initiated',
|
||||
110: 'Restart marker reply',
|
||||
@@ -23,7 +23,7 @@ module.exports = {
|
||||
230: 'User logged in, proceed',
|
||||
234: 'Honored',
|
||||
250: 'Requested file action okay, completed',
|
||||
257: '\'%s\' created',
|
||||
257: "'%s' created",
|
||||
/// 300 - 399 :: Positive Intermediate Replies
|
||||
/// These types of replies indicate that the requested action was taken and that the server is awaiting further information to complete the request.
|
||||
331: 'Username okay, awaiting password',
|
||||
@@ -53,4 +53,4 @@ module.exports = {
|
||||
551: 'Requested action aborted. Page type unknown',
|
||||
552: 'Requested file action aborted. Exceeded storage allocation', // (for current directory or dataset).
|
||||
553: 'Requested action not taken. File name not allowed'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,125 +1,121 @@
|
||||
const {expect} = require('chai');
|
||||
const Promise = require('bluebird');
|
||||
const bunyan = require('bunyan');
|
||||
const sinon = require('sinon');
|
||||
const { expect } = require('chai')
|
||||
const Promise = require('bluebird')
|
||||
const bunyan = require('bunyan')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const FtpCommands = require('../../src/commands');
|
||||
const FtpCommands = require('../../src/commands')
|
||||
|
||||
describe('FtpCommands', function () {
|
||||
let sandbox;
|
||||
let commands;
|
||||
let sandbox
|
||||
let commands
|
||||
let mockConnection = {
|
||||
authenticated: false,
|
||||
log: bunyan.createLogger({name: 'FtpCommands'}),
|
||||
log: bunyan.createLogger({ name: 'FtpCommands' }),
|
||||
reply: () => Promise.resolve({}),
|
||||
server: {
|
||||
options: {
|
||||
blacklist: ['allo']
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
commands = new FtpCommands(mockConnection);
|
||||
commands = new FtpCommands(mockConnection)
|
||||
|
||||
sandbox.spy(mockConnection, 'reply');
|
||||
sandbox.spy(commands, 'handle');
|
||||
sandbox.spy(commands, 'parse');
|
||||
});
|
||||
sandbox.spy(mockConnection, 'reply')
|
||||
sandbox.spy(commands, 'handle')
|
||||
sandbox.spy(commands, 'parse')
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
describe('parse', function () {
|
||||
it('no args: test', () => {
|
||||
const cmd = commands.parse('test');
|
||||
expect(cmd.directive).to.equal('TEST');
|
||||
expect(cmd.arg).to.equal(null);
|
||||
expect(cmd.raw).to.equal('test');
|
||||
});
|
||||
const cmd = commands.parse('test')
|
||||
expect(cmd.directive).to.equal('TEST')
|
||||
expect(cmd.arg).to.equal(null)
|
||||
expect(cmd.raw).to.equal('test')
|
||||
})
|
||||
|
||||
it('one arg: test arg', () => {
|
||||
const cmd = commands.parse('test arg');
|
||||
expect(cmd.directive).to.equal('TEST');
|
||||
expect(cmd.arg).to.equal('arg');
|
||||
expect(cmd.raw).to.equal('test arg');
|
||||
});
|
||||
const cmd = commands.parse('test arg')
|
||||
expect(cmd.directive).to.equal('TEST')
|
||||
expect(cmd.arg).to.equal('arg')
|
||||
expect(cmd.raw).to.equal('test arg')
|
||||
})
|
||||
|
||||
it('two args: test arg1 arg2', () => {
|
||||
const cmd = commands.parse('test arg1 arg2');
|
||||
expect(cmd.directive).to.equal('TEST');
|
||||
expect(cmd.arg).to.equal('arg1 arg2');
|
||||
expect(cmd.raw).to.equal('test arg1 arg2');
|
||||
});
|
||||
const cmd = commands.parse('test arg1 arg2')
|
||||
expect(cmd.directive).to.equal('TEST')
|
||||
expect(cmd.arg).to.equal('arg1 arg2')
|
||||
expect(cmd.raw).to.equal('test arg1 arg2')
|
||||
})
|
||||
|
||||
it('two args with quotes: test "hello world"', () => {
|
||||
const cmd = commands.parse('test "hello world"');
|
||||
expect(cmd.directive).to.equal('TEST');
|
||||
expect(cmd.arg).to.equal('hello world');
|
||||
expect(cmd.raw).to.equal('test "hello world"');
|
||||
});
|
||||
const cmd = commands.parse('test "hello world"')
|
||||
expect(cmd.directive).to.equal('TEST')
|
||||
expect(cmd.arg).to.equal('hello world')
|
||||
expect(cmd.raw).to.equal('test "hello world"')
|
||||
})
|
||||
|
||||
it('two args, with flags: test -l arg1 -A arg2 --zz88A', () => {
|
||||
const cmd = commands.parse('test -l arg1 -A arg2 --zz88A');
|
||||
expect(cmd.directive).to.equal('TEST');
|
||||
expect(cmd.arg).to.equal('arg1 arg2 --zz88A');
|
||||
expect(cmd.flags).to.deep.equal(['-l', '-A']);
|
||||
expect(cmd.raw).to.equal('test -l arg1 -A arg2 --zz88A');
|
||||
});
|
||||
const cmd = commands.parse('test -l arg1 -A arg2 --zz88A')
|
||||
expect(cmd.directive).to.equal('TEST')
|
||||
expect(cmd.arg).to.equal('arg1 arg2 --zz88A')
|
||||
expect(cmd.flags).to.deep.equal(['-l', '-A'])
|
||||
expect(cmd.raw).to.equal('test -l arg1 -A arg2 --zz88A')
|
||||
})
|
||||
|
||||
it('one arg, with flags: list -l', () => {
|
||||
const cmd = commands.parse('list -l');
|
||||
expect(cmd.directive).to.equal('LIST');
|
||||
expect(cmd.arg).to.equal(null);
|
||||
expect(cmd.flags).to.deep.equal(['-l']);
|
||||
expect(cmd.raw).to.equal('list -l');
|
||||
});
|
||||
const cmd = commands.parse('list -l')
|
||||
expect(cmd.directive).to.equal('LIST')
|
||||
expect(cmd.arg).to.equal(null)
|
||||
expect(cmd.flags).to.deep.equal(['-l'])
|
||||
expect(cmd.raw).to.equal('list -l')
|
||||
})
|
||||
|
||||
it('does not check for option flags', () => {
|
||||
const cmd = commands.parse('retr -test');
|
||||
expect(cmd.directive).to.equal('RETR');
|
||||
expect(cmd.arg).to.equal('-test');
|
||||
expect(cmd.flags).to.deep.equal([]);
|
||||
});
|
||||
});
|
||||
const cmd = commands.parse('retr -test')
|
||||
expect(cmd.directive).to.equal('RETR')
|
||||
expect(cmd.arg).to.equal('-test')
|
||||
expect(cmd.flags).to.deep.equal([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('handle', function () {
|
||||
it('fails with unsupported command', () => {
|
||||
return commands.handle('bad')
|
||||
.then(() => {
|
||||
expect(mockConnection.reply.callCount).to.equal(1);
|
||||
expect(mockConnection.reply.args[0][0]).to.equal(502);
|
||||
});
|
||||
});
|
||||
return commands.handle('bad').then(() => {
|
||||
expect(mockConnection.reply.callCount).to.equal(1)
|
||||
expect(mockConnection.reply.args[0][0]).to.equal(502)
|
||||
})
|
||||
})
|
||||
|
||||
it('fails with blacklisted command', () => {
|
||||
return commands.handle('allo')
|
||||
.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/);
|
||||
});
|
||||
});
|
||||
return commands.handle('allo').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/)
|
||||
})
|
||||
})
|
||||
|
||||
it('fails with non whitelisted command', () => {
|
||||
commands.whitelist.push('USER');
|
||||
return commands.handle('auth')
|
||||
.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/);
|
||||
});
|
||||
});
|
||||
commands.whitelist.push('USER')
|
||||
return commands.handle('auth').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/)
|
||||
})
|
||||
})
|
||||
|
||||
it('fails due to being unauthenticated', () => {
|
||||
return commands.handle('stor')
|
||||
.then(() => {
|
||||
expect(mockConnection.reply.callCount).to.equal(1);
|
||||
expect(mockConnection.reply.args[0][0]).to.equal(530);
|
||||
expect(mockConnection.reply.args[0][1]).to.match(/authentication/);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
return commands.handle('stor').then(() => {
|
||||
expect(mockConnection.reply.callCount).to.equal(1)
|
||||
expect(mockConnection.reply.args[0][0]).to.equal(530)
|
||||
expect(mockConnection.reply.args[0][1]).to.match(/authentication/)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,49 +1,47 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'ABOR';
|
||||
const CMD = 'ABOR'
|
||||
describe.skip(CMD, function () {
|
||||
let sandbox;
|
||||
let sandbox
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve(),
|
||||
connector: {
|
||||
waitForConnection: () => Promise.resolve(),
|
||||
end: () => Promise.resolve()
|
||||
}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
}
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
sandbox.spy(mockClient.connector, 'waitForConnection');
|
||||
sandbox.spy(mockClient.connector, 'end');
|
||||
});
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
sandbox.spy(mockClient.connector, 'waitForConnection')
|
||||
sandbox.spy(mockClient.connector, 'end')
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// successful | no active connection', () => {
|
||||
mockClient.connector.waitForConnection.restore();
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').rejects();
|
||||
mockClient.connector.waitForConnection.restore()
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').rejects()
|
||||
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.connector.waitForConnection.callCount).to.equal(1);
|
||||
expect(mockClient.connector.end.callCount).to.equal(0);
|
||||
expect(mockClient.reply.args[0][0]).to.equal(225);
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.connector.waitForConnection.callCount).to.equal(1)
|
||||
expect(mockClient.connector.end.callCount).to.equal(0)
|
||||
expect(mockClient.reply.args[0][0]).to.equal(225)
|
||||
})
|
||||
})
|
||||
|
||||
it('// successful | active connection', () => {
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.connector.waitForConnection.callCount).to.equal(1);
|
||||
expect(mockClient.connector.end.callCount).to.equal(1);
|
||||
expect(mockClient.reply.args[0][0]).to.equal(426);
|
||||
expect(mockClient.reply.args[1][0]).to.equal(226);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.connector.waitForConnection.callCount).to.equal(1)
|
||||
expect(mockClient.connector.end.callCount).to.equal(1)
|
||||
expect(mockClient.reply.args[0][0]).to.equal(426)
|
||||
expect(mockClient.reply.args[1][0]).to.equal(226)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'ALLO';
|
||||
const CMD = 'ALLO'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let sandbox
|
||||
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.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(202);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(202)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'AUTH';
|
||||
const CMD = 'AUTH'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let sandbox
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve(),
|
||||
server: {
|
||||
@@ -12,37 +12,34 @@ describe(CMD, function () {
|
||||
tls: {}
|
||||
}
|
||||
}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
}
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('TLS // supported', () => {
|
||||
return cmdFn({command: {arg: 'TLS', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(234);
|
||||
expect(mockClient.secure).to.equal(true);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'TLS', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(234)
|
||||
expect(mockClient.secure).to.equal(true)
|
||||
})
|
||||
})
|
||||
|
||||
it('SSL // not supported', () => {
|
||||
return cmdFn({command: {arg: 'SSL', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'SSL', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504)
|
||||
})
|
||||
})
|
||||
|
||||
it('bad // bad', () => {
|
||||
return cmdFn({command: {arg: 'bad', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'bad', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,35 +1,34 @@
|
||||
const Promise = require('bluebird');
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const bunyan = require('bunyan')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'CDUP';
|
||||
const CMD = 'CDUP'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
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.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
sandbox.spy(mockClient.fs, 'chdir');
|
||||
});
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
sandbox.spy(mockClient.fs, 'chdir')
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('.. // successful', () => {
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(250);
|
||||
expect(mockClient.fs.chdir.args[0][0]).to.equal('..');
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(250)
|
||||
expect(mockClient.fs.chdir.args[0][0]).to.equal('..')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,78 +1,77 @@
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const bunyan = require('bunyan')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'CWD';
|
||||
const CMD = 'CWD'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
let sandbox
|
||||
let log = bunyan.createLogger({ name: CMD })
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
fs: {chdir: () => {}}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
fs: { chdir: () => {} }
|
||||
}
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.stub(mockClient, 'reply').resolves();
|
||||
sandbox.stub(mockClient.fs, 'chdir').resolves();
|
||||
});
|
||||
sandbox.stub(mockClient, 'reply').resolves()
|
||||
sandbox.stub(mockClient.fs, 'chdir').resolves()
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = {reply: () => {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
const badMockClient = { reply: () => {} }
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(
|
||||
badMockClient
|
||||
)
|
||||
sandbox.stub(badMockClient, 'reply').resolves()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
return badCmdFn().then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550)
|
||||
})
|
||||
})
|
||||
|
||||
it('fails on no fs chdir command', () => {
|
||||
const badMockClient = {reply: () => {}, fs: {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
const badMockClient = { reply: () => {}, fs: {} }
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(
|
||||
badMockClient
|
||||
)
|
||||
sandbox.stub(badMockClient, 'reply').resolves()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
});
|
||||
});
|
||||
return badCmdFn().then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('test // successful', () => {
|
||||
return cmdFn({log, command: {arg: 'test', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(250);
|
||||
expect(mockClient.fs.chdir.args[0][0]).to.equal('test');
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { arg: 'test', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(250)
|
||||
expect(mockClient.fs.chdir.args[0][0]).to.equal('test')
|
||||
})
|
||||
})
|
||||
|
||||
it('test // successful', () => {
|
||||
mockClient.fs.chdir.restore();
|
||||
sandbox.stub(mockClient.fs, 'chdir').resolves('/test');
|
||||
mockClient.fs.chdir.restore()
|
||||
sandbox.stub(mockClient.fs, 'chdir').resolves('/test')
|
||||
|
||||
return cmdFn({log, command: {arg: 'test', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(250);
|
||||
expect(mockClient.fs.chdir.args[0][0]).to.equal('test');
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { arg: 'test', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(250)
|
||||
expect(mockClient.fs.chdir.args[0][0]).to.equal('test')
|
||||
})
|
||||
})
|
||||
|
||||
it('bad // unsuccessful', () => {
|
||||
mockClient.fs.chdir.restore();
|
||||
sandbox.stub(mockClient.fs, 'chdir').rejects(new Error('Bad'));
|
||||
mockClient.fs.chdir.restore()
|
||||
sandbox.stub(mockClient.fs, 'chdir').rejects(new Error('Bad'))
|
||||
|
||||
return cmdFn({log, command: {arg: 'bad', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
expect(mockClient.fs.chdir.args[0][0]).to.equal('bad');
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { 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,67 +1,67 @@
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const bunyan = require('bunyan')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'DELE';
|
||||
const CMD = 'DELE'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
let sandbox
|
||||
let log = bunyan.createLogger({ name: CMD })
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
fs: {delete: () => {}}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
fs: { delete: () => {} }
|
||||
}
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.stub(mockClient, 'reply').resolves();
|
||||
sandbox.stub(mockClient.fs, 'delete').resolves();
|
||||
});
|
||||
sandbox.stub(mockClient, 'reply').resolves()
|
||||
sandbox.stub(mockClient.fs, 'delete').resolves()
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = {reply: () => {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
const badMockClient = { reply: () => {} }
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(
|
||||
badMockClient
|
||||
)
|
||||
sandbox.stub(badMockClient, 'reply').resolves()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
return badCmdFn().then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550)
|
||||
})
|
||||
})
|
||||
|
||||
it('fails on no fs delete command', () => {
|
||||
const badMockClient = {reply: () => {}, fs: {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
const badMockClient = { reply: () => {}, fs: {} }
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(
|
||||
badMockClient
|
||||
)
|
||||
sandbox.stub(badMockClient, 'reply').resolves()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
});
|
||||
});
|
||||
return badCmdFn().then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('test // successful', () => {
|
||||
return cmdFn({log, command: {arg: 'test', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(250);
|
||||
expect(mockClient.fs.delete.args[0][0]).to.equal('test');
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { arg: 'test', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(250)
|
||||
expect(mockClient.fs.delete.args[0][0]).to.equal('test')
|
||||
})
|
||||
})
|
||||
|
||||
it('bad // unsuccessful', () => {
|
||||
mockClient.fs.delete.restore();
|
||||
sandbox.stub(mockClient.fs, 'delete').rejects(new Error('Bad'));
|
||||
mockClient.fs.delete.restore()
|
||||
sandbox.stub(mockClient.fs, 'delete').rejects(new Error('Bad'))
|
||||
|
||||
return cmdFn({log, command: {arg: 'bad', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
expect(mockClient.fs.delete.args[0][0]).to.equal('bad');
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { arg: 'bad', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550)
|
||||
expect(mockClient.fs.delete.args[0][0]).to.equal('bad')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,60 +1,56 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const ActiveConnector = require('../../../src/connector/active');
|
||||
const ActiveConnector = require('../../../src/connector/active')
|
||||
|
||||
const CMD = 'EPRT';
|
||||
const CMD = 'EPRT'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let sandbox
|
||||
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.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
sandbox.stub(ActiveConnector.prototype, 'setupConnection').resolves();
|
||||
});
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
sandbox.stub(ActiveConnector.prototype, 'setupConnection').resolves()
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// unsuccessful | no argument', () => {
|
||||
return cmdFn({})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504);
|
||||
});
|
||||
});
|
||||
return cmdFn({}).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful | invalid argument', () => {
|
||||
return cmdFn({command: {arg: 'blah'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { 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|'}})
|
||||
.then(() => {
|
||||
const [ip, port, family] = ActiveConnector.prototype.setupConnection.args[0];
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
expect(ip).to.equal('192.168.0.100');
|
||||
expect(port).to.equal('35286');
|
||||
expect(family).to.equal(4);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { 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)
|
||||
expect(ip).to.equal('192.168.0.100')
|
||||
expect(port).to.equal('35286')
|
||||
expect(family).to.equal(4)
|
||||
})
|
||||
})
|
||||
|
||||
it('// successful IPv6', () => {
|
||||
return cmdFn({command: {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);
|
||||
expect(ip).to.equal('8536:933f:e7f3:3e91:6dc1:e8c6:8482:7b23');
|
||||
expect(port).to.equal('35286');
|
||||
expect(family).to.equal(6);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { 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)
|
||||
expect(ip).to.equal('8536:933f:e7f3:3e91:6dc1:e8c6:8482:7b23')
|
||||
expect(port).to.equal('35286')
|
||||
expect(family).to.equal(6)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,35 +1,34 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const PassiveConnector = require('../../../src/connector/passive');
|
||||
const PassiveConnector = require('../../../src/connector/passive')
|
||||
|
||||
const CMD = 'EPSV';
|
||||
const CMD = 'EPSV'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let sandbox
|
||||
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.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.stub(mockClient, 'reply').resolves();
|
||||
sandbox.stub(mockClient, 'reply').resolves()
|
||||
sandbox.stub(PassiveConnector.prototype, 'setupServer').resolves({
|
||||
address: () => ({port: 12345})
|
||||
});
|
||||
});
|
||||
address: () => ({ port: 12345 })
|
||||
})
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// successful IPv4', () => {
|
||||
return cmdFn({})
|
||||
.then(() => {
|
||||
const [code, message] = mockClient.reply.args[0];
|
||||
expect(code).to.equal(229);
|
||||
expect(message).to.equal('EPSV OK (|||12345|)');
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({}).then(() => {
|
||||
const [code, message] = mockClient.reply.args[0]
|
||||
expect(code).to.equal(229)
|
||||
expect(message).to.equal('EPSV OK (|||12345|)')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,29 +1,28 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'FEAT';
|
||||
const CMD = 'FEAT'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let sandbox
|
||||
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.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn({command: {directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(211);
|
||||
expect(mockClient.reply.args[0][2].message).to.equal(' AUTH TLS');
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(211)
|
||||
expect(mockClient.reply.args[0][2].message).to.equal(' AUTH TLS')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,49 +1,45 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'HELP';
|
||||
const CMD = 'HELP'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let sandbox
|
||||
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.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn({command: {directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(211);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(211)
|
||||
})
|
||||
})
|
||||
|
||||
it('help // successful', () => {
|
||||
return cmdFn({command: {arg: 'help', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(214);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'help', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(214)
|
||||
})
|
||||
})
|
||||
|
||||
it('allo // successful', () => {
|
||||
return cmdFn({command: {arg: 'allo', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(214);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'allo', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(214)
|
||||
})
|
||||
})
|
||||
|
||||
it('bad // unsuccessful', () => {
|
||||
return cmdFn({command: {arg: 'bad', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(502);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'bad', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(502)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
const Promise = require('bluebird');
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const bunyan = require('bunyan')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'LIST';
|
||||
const CMD = 'LIST'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
let sandbox
|
||||
let log = bunyan.createLogger({ name: CMD })
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
fs: {
|
||||
@@ -21,13 +21,13 @@ describe(CMD, function () {
|
||||
resume: () => {},
|
||||
pause: () => {}
|
||||
}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
}
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.stub(mockClient, 'reply').resolves();
|
||||
sandbox.stub(mockClient, 'reply').resolves()
|
||||
sandbox.stub(mockClient.fs, 'get').resolves({
|
||||
name: 'testdir',
|
||||
dev: 2114,
|
||||
@@ -45,85 +45,89 @@ describe(CMD, function () {
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => true
|
||||
});
|
||||
sandbox.stub(mockClient.fs, 'list').resolves([{
|
||||
name: 'test1',
|
||||
dev: 2114,
|
||||
ino: 48064969,
|
||||
mode: 33188,
|
||||
nlink: 1,
|
||||
uid: 85,
|
||||
gid: 100,
|
||||
rdev: 0,
|
||||
size: 527,
|
||||
blksize: 4096,
|
||||
blocks: 8,
|
||||
atime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
mtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => false
|
||||
}, {
|
||||
name: 'test2',
|
||||
dev: 2114,
|
||||
ino: 48064969,
|
||||
mode: 33188,
|
||||
nlink: 1,
|
||||
uid: 85,
|
||||
gid: 100,
|
||||
rdev: 0,
|
||||
size: 527,
|
||||
blksize: 4096,
|
||||
blocks: 8,
|
||||
atime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
mtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => true
|
||||
}]);
|
||||
});
|
||||
})
|
||||
sandbox.stub(mockClient.fs, 'list').resolves([
|
||||
{
|
||||
name: 'test1',
|
||||
dev: 2114,
|
||||
ino: 48064969,
|
||||
mode: 33188,
|
||||
nlink: 1,
|
||||
uid: 85,
|
||||
gid: 100,
|
||||
rdev: 0,
|
||||
size: 527,
|
||||
blksize: 4096,
|
||||
blocks: 8,
|
||||
atime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
mtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => false
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
dev: 2114,
|
||||
ino: 48064969,
|
||||
mode: 33188,
|
||||
nlink: 1,
|
||||
uid: 85,
|
||||
gid: 100,
|
||||
rdev: 0,
|
||||
size: 527,
|
||||
blksize: 4096,
|
||||
blocks: 8,
|
||||
atime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
mtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => true
|
||||
}
|
||||
])
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = {reply: () => {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
const badMockClient = { reply: () => {} }
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(
|
||||
badMockClient
|
||||
)
|
||||
sandbox.stub(badMockClient, 'reply').resolves()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
return badCmdFn().then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550)
|
||||
})
|
||||
})
|
||||
|
||||
it('fails on no fs list command', () => {
|
||||
const badMockClient = {reply: () => {}, fs: {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
const badMockClient = { reply: () => {}, fs: {} }
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(
|
||||
badMockClient
|
||||
)
|
||||
sandbox.stub(badMockClient, 'reply').resolves()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
});
|
||||
});
|
||||
return badCmdFn().then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('. // successful', () => {
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(150);
|
||||
expect(mockClient.reply.args[1].length).to.equal(3);
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('raw');
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('message');
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('socket');
|
||||
expect(mockClient.reply.args[2][0]).to.equal(226);
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(150)
|
||||
expect(mockClient.reply.args[1].length).to.equal(3)
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('raw')
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('message')
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('socket')
|
||||
expect(mockClient.reply.args[2][0]).to.equal(226)
|
||||
})
|
||||
})
|
||||
|
||||
it('testfile.txt // successful', () => {
|
||||
mockClient.fs.get.restore();
|
||||
mockClient.fs.get.restore()
|
||||
sandbox.stub(mockClient.fs, 'get').resolves({
|
||||
name: 'testfile.txt',
|
||||
dev: 2114,
|
||||
@@ -141,35 +145,34 @@ describe(CMD, function () {
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => false
|
||||
});
|
||||
})
|
||||
|
||||
return cmdFn({log, command: {directive: CMD, arg: 'testfile.txt'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(150);
|
||||
expect(mockClient.reply.args[1].length).to.equal(2);
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('raw');
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('message');
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('socket');
|
||||
expect(mockClient.reply.args[2][0]).to.equal(226);
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { directive: CMD, arg: 'testfile.txt' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(150)
|
||||
expect(mockClient.reply.args[1].length).to.equal(2)
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('raw')
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('message')
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('socket')
|
||||
expect(mockClient.reply.args[2][0]).to.equal(226)
|
||||
})
|
||||
})
|
||||
|
||||
it('. // unsuccessful', () => {
|
||||
mockClient.fs.list.restore();
|
||||
sandbox.stub(mockClient.fs, 'list').rejects(new Error());
|
||||
mockClient.fs.list.restore()
|
||||
sandbox.stub(mockClient.fs, 'list').rejects(new Error())
|
||||
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(451);
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(451)
|
||||
})
|
||||
})
|
||||
|
||||
it('. // unsuccessful (timeout)', () => {
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').returns(Promise.reject(new Promise.TimeoutError()));
|
||||
sandbox
|
||||
.stub(mockClient.connector, 'waitForConnection')
|
||||
.returns(Promise.reject(new Promise.TimeoutError()))
|
||||
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(425);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(425)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,66 +1,66 @@
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const bunyan = require('bunyan')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'MDTM';
|
||||
const CMD = 'MDTM'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
let sandbox
|
||||
let log = bunyan.createLogger({ name: CMD })
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
fs: {get: () => {}}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
fs: { get: () => {} }
|
||||
}
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.stub(mockClient, 'reply').resolves();
|
||||
sandbox.stub(mockClient.fs, 'get').resolves({mtime: 'Mon, 10 Oct 2011 23:24:11 GMT'});
|
||||
});
|
||||
sandbox.stub(mockClient, 'reply').resolves()
|
||||
sandbox.stub(mockClient.fs, 'get').resolves({ mtime: 'Mon, 10 Oct 2011 23:24:11 GMT' })
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = {reply: () => {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
const badMockClient = { reply: () => {} }
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(
|
||||
badMockClient
|
||||
)
|
||||
sandbox.stub(badMockClient, 'reply').resolves()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
return badCmdFn().then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550)
|
||||
})
|
||||
})
|
||||
|
||||
it('fails on no fs get command', () => {
|
||||
const badMockClient = {reply: () => {}, fs: {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
const badMockClient = { reply: () => {}, fs: {} }
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(
|
||||
badMockClient
|
||||
)
|
||||
sandbox.stub(badMockClient, 'reply').resolves()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
});
|
||||
});
|
||||
return badCmdFn().then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('. // successful', () => {
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(213);
|
||||
return cmdFn({ log, command: { directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(213)
|
||||
//expect(mockClient.reply.args[0][1]).to.equal('20111010172411.000');
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
it('. // unsuccessful', () => {
|
||||
mockClient.fs.get.restore();
|
||||
sandbox.stub(mockClient.fs, 'get').rejects(new Error());
|
||||
mockClient.fs.get.restore()
|
||||
sandbox.stub(mockClient.fs, 'get').rejects(new Error())
|
||||
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,78 +1,77 @@
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const bunyan = require('bunyan')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'MKD';
|
||||
const CMD = 'MKD'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
let sandbox
|
||||
let log = bunyan.createLogger({ name: CMD })
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
fs: {mkdir: () => {}}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
fs: { mkdir: () => {} }
|
||||
}
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.stub(mockClient, 'reply').resolves();
|
||||
sandbox.stub(mockClient.fs, 'mkdir').resolves();
|
||||
});
|
||||
sandbox.stub(mockClient, 'reply').resolves()
|
||||
sandbox.stub(mockClient.fs, 'mkdir').resolves()
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = {reply: () => {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
const badMockClient = { reply: () => {} }
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(
|
||||
badMockClient
|
||||
)
|
||||
sandbox.stub(badMockClient, 'reply').resolves()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
return badCmdFn().then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550)
|
||||
})
|
||||
})
|
||||
|
||||
it('fails on no fs mkdir command', () => {
|
||||
const badMockClient = {reply: () => {}, fs: {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
const badMockClient = { reply: () => {}, fs: {} }
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(
|
||||
badMockClient
|
||||
)
|
||||
sandbox.stub(badMockClient, 'reply').resolves()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
});
|
||||
});
|
||||
return badCmdFn().then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('test // successful', () => {
|
||||
return cmdFn({log, command: {arg: 'test', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(257);
|
||||
expect(mockClient.fs.mkdir.args[0][0]).to.equal('test');
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { arg: 'test', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(257)
|
||||
expect(mockClient.fs.mkdir.args[0][0]).to.equal('test')
|
||||
})
|
||||
})
|
||||
|
||||
it('test // successful', () => {
|
||||
mockClient.fs.mkdir.restore();
|
||||
sandbox.stub(mockClient.fs, 'mkdir').resolves('test');
|
||||
mockClient.fs.mkdir.restore()
|
||||
sandbox.stub(mockClient.fs, 'mkdir').resolves('test')
|
||||
|
||||
return cmdFn({log, command: {arg: 'test', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(257);
|
||||
expect(mockClient.fs.mkdir.args[0][0]).to.equal('test');
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { arg: 'test', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(257)
|
||||
expect(mockClient.fs.mkdir.args[0][0]).to.equal('test')
|
||||
})
|
||||
})
|
||||
|
||||
it('bad // unsuccessful', () => {
|
||||
mockClient.fs.mkdir.restore();
|
||||
sandbox.stub(mockClient.fs, 'mkdir').rejects(new Error('Bad'));
|
||||
mockClient.fs.mkdir.restore()
|
||||
sandbox.stub(mockClient.fs, 'mkdir').rejects(new Error('Bad'))
|
||||
|
||||
return cmdFn({log, command: {arg: 'bad', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
expect(mockClient.fs.mkdir.args[0][0]).to.equal('bad');
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { arg: 'bad', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550)
|
||||
expect(mockClient.fs.mkdir.args[0][0]).to.equal('bad')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,35 +1,33 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'MODE';
|
||||
const CMD = 'MODE'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let sandbox
|
||||
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.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('S // successful', () => {
|
||||
return cmdFn({command: {arg: 'S'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'S' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200)
|
||||
})
|
||||
})
|
||||
|
||||
it('Q // unsuccessful', () => {
|
||||
return cmdFn({command: {arg: 'Q'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'Q' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
const Promise = require('bluebird');
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const bunyan = require('bunyan')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'NLST';
|
||||
const CMD = 'NLST'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
let sandbox
|
||||
let log = bunyan.createLogger({ name: CMD })
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
fs: {
|
||||
@@ -21,13 +21,13 @@ describe(CMD, function () {
|
||||
resume: () => {},
|
||||
pause: () => {}
|
||||
}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
}
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.stub(mockClient, 'reply').resolves();
|
||||
sandbox.stub(mockClient, 'reply').resolves()
|
||||
sandbox.stub(mockClient.fs, 'get').resolves({
|
||||
name: 'testdir',
|
||||
dev: 2114,
|
||||
@@ -45,61 +45,63 @@ describe(CMD, function () {
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => true
|
||||
});
|
||||
sandbox.stub(mockClient.fs, 'list').resolves([{
|
||||
name: 'test1',
|
||||
dev: 2114,
|
||||
ino: 48064969,
|
||||
mode: 33188,
|
||||
nlink: 1,
|
||||
uid: 85,
|
||||
gid: 100,
|
||||
rdev: 0,
|
||||
size: 527,
|
||||
blksize: 4096,
|
||||
blocks: 8,
|
||||
atime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
mtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => false
|
||||
}, {
|
||||
name: 'test2',
|
||||
dev: 2114,
|
||||
ino: 48064969,
|
||||
mode: 33188,
|
||||
nlink: 1,
|
||||
uid: 85,
|
||||
gid: 100,
|
||||
rdev: 0,
|
||||
size: 527,
|
||||
blksize: 4096,
|
||||
blocks: 8,
|
||||
atime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
mtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => true
|
||||
}]);
|
||||
});
|
||||
})
|
||||
sandbox.stub(mockClient.fs, 'list').resolves([
|
||||
{
|
||||
name: 'test1',
|
||||
dev: 2114,
|
||||
ino: 48064969,
|
||||
mode: 33188,
|
||||
nlink: 1,
|
||||
uid: 85,
|
||||
gid: 100,
|
||||
rdev: 0,
|
||||
size: 527,
|
||||
blksize: 4096,
|
||||
blocks: 8,
|
||||
atime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
mtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => false
|
||||
},
|
||||
{
|
||||
name: 'test2',
|
||||
dev: 2114,
|
||||
ino: 48064969,
|
||||
mode: 33188,
|
||||
nlink: 1,
|
||||
uid: 85,
|
||||
gid: 100,
|
||||
rdev: 0,
|
||||
size: 527,
|
||||
blksize: 4096,
|
||||
blocks: 8,
|
||||
atime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
mtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => true
|
||||
}
|
||||
])
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('. // successful', () => {
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(150);
|
||||
expect(mockClient.reply.args[1].length).to.equal(3);
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('raw');
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('message');
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('socket');
|
||||
expect(mockClient.reply.args[2][0]).to.equal(226);
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(150)
|
||||
expect(mockClient.reply.args[1].length).to.equal(3)
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('raw')
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('message')
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('socket')
|
||||
expect(mockClient.reply.args[2][0]).to.equal(226)
|
||||
})
|
||||
})
|
||||
|
||||
it('testfile.txt // successful', () => {
|
||||
mockClient.fs.get.restore();
|
||||
mockClient.fs.get.restore()
|
||||
sandbox.stub(mockClient.fs, 'get').resolves({
|
||||
name: 'testfile.txt',
|
||||
dev: 2114,
|
||||
@@ -117,16 +119,15 @@ describe(CMD, function () {
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => false
|
||||
});
|
||||
})
|
||||
|
||||
return cmdFn({log, command: {directive: CMD, arg: 'testfile.txt'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(150);
|
||||
expect(mockClient.reply.args[1].length).to.equal(2);
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('raw');
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('message');
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('socket');
|
||||
expect(mockClient.reply.args[2][0]).to.equal(226);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { directive: CMD, arg: 'testfile.txt' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(150)
|
||||
expect(mockClient.reply.args[1].length).to.equal(2)
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('raw')
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('message')
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('socket')
|
||||
expect(mockClient.reply.args[2][0]).to.equal(226)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'NOOP';
|
||||
const CMD = 'NOOP'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let sandbox
|
||||
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.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,58 +1,53 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'OPTS';
|
||||
const CMD = 'OPTS'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let sandbox
|
||||
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.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// unsuccessful', () => {
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501);
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501)
|
||||
})
|
||||
})
|
||||
|
||||
it('BAD // unsuccessful', () => {
|
||||
return cmdFn({command: {arg: 'BAD', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'BAD', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501)
|
||||
})
|
||||
})
|
||||
|
||||
it('UTF8 BAD // unsuccessful', () => {
|
||||
return cmdFn({command: {arg: 'UTF8 BAD', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { 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}})
|
||||
.then(() => {
|
||||
expect(mockClient.encoding).to.equal('ascii');
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'UTF8 OFF', directive: CMD } }).then(() => {
|
||||
expect(mockClient.encoding).to.equal('ascii')
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200)
|
||||
})
|
||||
})
|
||||
|
||||
it('UTF8 ON // successful', () => {
|
||||
return cmdFn({command: {arg: 'UTF8 ON', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.encoding).to.equal('utf8');
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'UTF8 ON', directive: CMD } }).then(() => {
|
||||
expect(mockClient.encoding).to.equal('utf8')
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,74 +1,69 @@
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const bunyan = require('bunyan')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'PASS';
|
||||
const CMD = 'PASS'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
let sandbox
|
||||
let log = bunyan.createLogger({ name: CMD })
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
login: () => {},
|
||||
server: {options: {anonymous: false}},
|
||||
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.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.stub(mockClient, 'reply').resolves();
|
||||
sandbox.stub(mockClient, 'login').resolves();
|
||||
});
|
||||
sandbox.stub(mockClient, 'reply').resolves()
|
||||
sandbox.stub(mockClient, 'login').resolves()
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('pass // successful', () => {
|
||||
return cmdFn({log, command: {arg: 'pass', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(230);
|
||||
expect(mockClient.login.args[0]).to.eql(['anonymous', 'pass']);
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { arg: 'pass', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(230)
|
||||
expect(mockClient.login.args[0]).to.eql(['anonymous', 'pass'])
|
||||
})
|
||||
})
|
||||
|
||||
it('// successful (already authenticated)', () => {
|
||||
mockClient.server.options.anonymous = true;
|
||||
mockClient.authenticated = true;
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(202);
|
||||
expect(mockClient.login.callCount).to.equal(0);
|
||||
mockClient.server.options.anonymous = false;
|
||||
mockClient.authenticated = false;
|
||||
});
|
||||
});
|
||||
mockClient.server.options.anonymous = true
|
||||
mockClient.authenticated = true
|
||||
return cmdFn({ log, command: { directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(202)
|
||||
expect(mockClient.login.callCount).to.equal(0)
|
||||
mockClient.server.options.anonymous = false
|
||||
mockClient.authenticated = false
|
||||
})
|
||||
})
|
||||
|
||||
it('bad // unsuccessful', () => {
|
||||
mockClient.login.restore();
|
||||
sandbox.stub(mockClient, 'login').rejects('bad');
|
||||
mockClient.login.restore()
|
||||
sandbox.stub(mockClient, 'login').rejects('bad')
|
||||
|
||||
return cmdFn({log, command: {arg: 'bad', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(530);
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { arg: 'bad', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(530)
|
||||
})
|
||||
})
|
||||
|
||||
it('bad // unsuccessful', () => {
|
||||
mockClient.login.restore();
|
||||
sandbox.stub(mockClient, 'login').rejects({});
|
||||
mockClient.login.restore()
|
||||
sandbox.stub(mockClient, 'login').rejects({})
|
||||
|
||||
return cmdFn({log, command: {arg: 'bad', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(530);
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { arg: 'bad', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(530)
|
||||
})
|
||||
})
|
||||
|
||||
it('bad // unsuccessful', () => {
|
||||
delete mockClient.username;
|
||||
return cmdFn({log, command: {arg: 'bad', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(503);
|
||||
});
|
||||
});
|
||||
});
|
||||
delete mockClient.username
|
||||
return cmdFn({ log, command: { arg: 'bad', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(503)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,51 +1,48 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'PBSZ';
|
||||
const CMD = 'PBSZ'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let sandbox
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve(),
|
||||
server: {}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
}
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// unsuccessful', () => {
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(202);
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(202)
|
||||
})
|
||||
})
|
||||
|
||||
it('// successful', () => {
|
||||
mockClient.secure = true;
|
||||
mockClient.server._tls = {};
|
||||
mockClient.secure = true
|
||||
mockClient.server._tls = {}
|
||||
|
||||
return cmdFn({command: {arg: '0'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
expect(mockClient.bufferSize).to.equal(0);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: '0' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200)
|
||||
expect(mockClient.bufferSize).to.equal(0)
|
||||
})
|
||||
})
|
||||
|
||||
it('// successful', () => {
|
||||
mockClient.secure = true;
|
||||
mockClient.server._tls = {};
|
||||
mockClient.secure = true
|
||||
mockClient.server._tls = {}
|
||||
|
||||
return cmdFn({command: {arg: '10'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
expect(mockClient.bufferSize).to.equal(10);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: '10' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200)
|
||||
expect(mockClient.bufferSize).to.equal(10)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,48 +1,45 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const ActiveConnector = require('../../../src/connector/active');
|
||||
const ActiveConnector = require('../../../src/connector/active')
|
||||
|
||||
const CMD = 'PORT';
|
||||
const CMD = 'PORT'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let sandbox
|
||||
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.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
sandbox.stub(ActiveConnector.prototype, 'setupConnection').resolves();
|
||||
});
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
sandbox.stub(ActiveConnector.prototype, 'setupConnection').resolves()
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// unsuccessful | no argument', () => {
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(425);
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(425)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful | invalid argument', () => {
|
||||
return cmdFn({command: {arg: '1,2,3,4,5'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(425);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { 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'}})
|
||||
.then(() => {
|
||||
const [ip, port] = ActiveConnector.prototype.setupConnection.args[0];
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
expect(ip).to.equal('192.168.0.100');
|
||||
expect(port).to.equal(35286);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { 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)
|
||||
expect(ip).to.equal('192.168.0.100')
|
||||
expect(port).to.equal(35286)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,65 +1,60 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'PROT';
|
||||
const CMD = 'PROT'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let sandbox
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve(),
|
||||
server: {}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
}
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// unsuccessful', () => {
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(202);
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(202)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful - no bufferSize', () => {
|
||||
mockClient.server._tls = {};
|
||||
mockClient.secure = true;
|
||||
mockClient.server._tls = {}
|
||||
mockClient.secure = true
|
||||
|
||||
return cmdFn({command: {arg: 'P'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(503);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'P' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(503)
|
||||
})
|
||||
})
|
||||
|
||||
it('// successful', () => {
|
||||
mockClient.bufferSize = 0;
|
||||
mockClient.secure = true;
|
||||
mockClient.bufferSize = 0
|
||||
mockClient.secure = true
|
||||
|
||||
return cmdFn({command: {arg: 'p'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'p' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful - unsupported', () => {
|
||||
mockClient.secure = true;
|
||||
return cmdFn({command: {arg: 'C'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(536);
|
||||
});
|
||||
});
|
||||
mockClient.secure = true
|
||||
return cmdFn({ command: { arg: 'C' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(536)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful - unknown', () => {
|
||||
mockClient.secure = true;
|
||||
return cmdFn({command: {arg: 'QQ'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504);
|
||||
});
|
||||
});
|
||||
});
|
||||
mockClient.secure = true
|
||||
return cmdFn({ command: { arg: 'QQ' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,75 +1,74 @@
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const bunyan = require('bunyan')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'PWD';
|
||||
const CMD = 'PWD'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
let sandbox
|
||||
let log = bunyan.createLogger({ name: CMD })
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
fs: {currentDirectory: () => {}}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
fs: { currentDirectory: () => {} }
|
||||
}
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.stub(mockClient, 'reply').resolves();
|
||||
sandbox.stub(mockClient.fs, 'currentDirectory').resolves();
|
||||
});
|
||||
sandbox.stub(mockClient, 'reply').resolves()
|
||||
sandbox.stub(mockClient.fs, 'currentDirectory').resolves()
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = {reply: () => {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
const badMockClient = { reply: () => {} }
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(
|
||||
badMockClient
|
||||
)
|
||||
sandbox.stub(badMockClient, 'reply').resolves()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
return badCmdFn().then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550)
|
||||
})
|
||||
})
|
||||
|
||||
it('fails on no fs currentDirectory command', () => {
|
||||
const badMockClient = {reply: () => {}, fs: {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
const badMockClient = { reply: () => {}, fs: {} }
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(
|
||||
badMockClient
|
||||
)
|
||||
sandbox.stub(badMockClient, 'reply').resolves()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
});
|
||||
});
|
||||
return badCmdFn().then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn({log, command: {arg: 'test', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(257);
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { arg: 'test', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(257)
|
||||
})
|
||||
})
|
||||
|
||||
it('// successful', () => {
|
||||
mockClient.fs.currentDirectory.restore();
|
||||
sandbox.stub(mockClient.fs, 'currentDirectory').resolves('/test');
|
||||
mockClient.fs.currentDirectory.restore()
|
||||
sandbox.stub(mockClient.fs, 'currentDirectory').resolves('/test')
|
||||
|
||||
return cmdFn({log, command: {arg: 'test', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(257);
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { arg: 'test', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(257)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful', () => {
|
||||
mockClient.fs.currentDirectory.restore();
|
||||
sandbox.stub(mockClient.fs, 'currentDirectory').rejects(new Error('Bad'));
|
||||
mockClient.fs.currentDirectory.restore()
|
||||
sandbox.stub(mockClient.fs, 'currentDirectory').rejects(new Error('Bad'))
|
||||
|
||||
return cmdFn({log, command: {arg: 'bad', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { arg: 'bad', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'QUIT';
|
||||
const CMD = 'QUIT'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let sandbox
|
||||
const mockClient = {
|
||||
close: () => {}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
}
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.stub(mockClient, 'close').resolves();
|
||||
});
|
||||
sandbox.stub(mockClient, 'close').resolves()
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.close.callCount).to.equal(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.close.callCount).to.equal(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,58 +1,53 @@
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird');
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
const Promise = require('bluebird')
|
||||
|
||||
const CMD = 'REST';
|
||||
const CMD = 'REST'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let sandbox
|
||||
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.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// unsuccessful', () => {
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501);
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501)
|
||||
})
|
||||
})
|
||||
|
||||
it('-1 // unsuccessful', () => {
|
||||
return cmdFn({command: {arg: '-1', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: '-1', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501)
|
||||
})
|
||||
})
|
||||
|
||||
it('bad // unsuccessful', () => {
|
||||
return cmdFn({command: {arg: 'bad', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'bad', directive: CMD } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501)
|
||||
})
|
||||
})
|
||||
|
||||
it('1 // successful', () => {
|
||||
return cmdFn({command: {arg: '1', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.restByteCount).to.equal(1);
|
||||
expect(mockClient.reply.args[0][0]).to.equal(350);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: '1', directive: CMD } }).then(() => {
|
||||
expect(mockClient.restByteCount).to.equal(1)
|
||||
expect(mockClient.reply.args[0][0]).to.equal(350)
|
||||
})
|
||||
})
|
||||
|
||||
it('0 // successful', () => {
|
||||
return cmdFn({command: {arg: '0', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.restByteCount).to.equal(0);
|
||||
expect(mockClient.reply.args[0][0]).to.equal(350);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: '0', directive: CMD } }).then(() => {
|
||||
expect(mockClient.restByteCount).to.equal(0)
|
||||
expect(mockClient.reply.args[0][0]).to.equal(350)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
const Promise = require('bluebird');
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const EventEmitter = require('events');
|
||||
const Promise = require('bluebird')
|
||||
const bunyan = require('bunyan')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
const EventEmitter = require('events')
|
||||
|
||||
const CMD = 'RETR';
|
||||
const CMD = 'RETR'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
let emitter;
|
||||
let sandbox
|
||||
let log = bunyan.createLogger({ name: CMD })
|
||||
let emitter
|
||||
const mockClient = {
|
||||
commandSocket: {
|
||||
pause: () => {},
|
||||
@@ -16,84 +16,79 @@ describe(CMD, function () {
|
||||
},
|
||||
reply: () => Promise.resolve(),
|
||||
connector: {
|
||||
waitForConnection: () => Promise.resolve({
|
||||
resume: () => {}
|
||||
}),
|
||||
waitForConnection: () =>
|
||||
Promise.resolve({
|
||||
resume: () => {}
|
||||
}),
|
||||
end: () => Promise.resolve({})
|
||||
}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
}
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
mockClient.fs = {
|
||||
read: () => {}
|
||||
};
|
||||
}
|
||||
|
||||
emitter = new EventEmitter();
|
||||
mockClient.emit = emitter.emit.bind(emitter);
|
||||
mockClient.on = emitter.on.bind(emitter);
|
||||
mockClient.once = emitter.once.bind(emitter);
|
||||
emitter = new EventEmitter()
|
||||
mockClient.emit = emitter.emit.bind(emitter)
|
||||
mockClient.on = emitter.on.bind(emitter)
|
||||
mockClient.once = emitter.once.bind(emitter)
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
afterEach(() => sandbox.restore());
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
})
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
delete mockClient.fs
|
||||
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
mockClient.fs = {}
|
||||
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful | connector times out', () => {
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').callsFake(function () {
|
||||
return Promise.reject(new Promise.TimeoutError());
|
||||
});
|
||||
return Promise.reject(new Promise.TimeoutError())
|
||||
})
|
||||
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(425);
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { arg: 'test.txt' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(425)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful | connector errors out', () => {
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').callsFake(function () {
|
||||
return Promise.reject(new Error('test'));
|
||||
});
|
||||
return Promise.reject(new Error('test'))
|
||||
})
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(551);
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { arg: 'test.txt' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(551)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful | emits error event', () => {
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').callsFake(function () {
|
||||
return Promise.reject(new Error('test'));
|
||||
});
|
||||
return Promise.reject(new Error('test'))
|
||||
})
|
||||
|
||||
let errorEmitted = false;
|
||||
let errorEmitted = false
|
||||
emitter.once('RETR', (err) => {
|
||||
errorEmitted = !!err;
|
||||
});
|
||||
errorEmitted = !!err
|
||||
})
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'}})
|
||||
.then(() => {
|
||||
expect(errorEmitted).to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { arg: 'test.txt' } }).then(() => {
|
||||
expect(errorEmitted).to.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,62 +1,58 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'RNFR';
|
||||
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);
|
||||
let sandbox
|
||||
const mockLog = { error: () => {} }
|
||||
const mockClient = { reply: () => Promise.resolve() }
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
mockClient.renameFrom = 'test';
|
||||
mockClient.renameFrom = 'test'
|
||||
mockClient.fs = {
|
||||
get: () => Promise.resolve()
|
||||
};
|
||||
}
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
sandbox.spy(mockClient.fs, 'get');
|
||||
});
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
sandbox.spy(mockClient.fs, 'get')
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
delete mockClient.fs
|
||||
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
mockClient.fs = {}
|
||||
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402)
|
||||
})
|
||||
})
|
||||
|
||||
it('test // unsuccessful | file get fails', () => {
|
||||
mockClient.fs.get.restore();
|
||||
sandbox.stub(mockClient.fs, 'get').rejects(new Error('test'));
|
||||
mockClient.fs.get.restore()
|
||||
sandbox.stub(mockClient.fs, 'get').rejects(new Error('test'))
|
||||
|
||||
return cmdFn({log: mockLog, command: {arg: 'test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
return cmdFn({ log: mockLog, command: { arg: 'test' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550)
|
||||
})
|
||||
})
|
||||
|
||||
it('test // successful', () => {
|
||||
return cmdFn({log: mockLog, command: {arg: 'test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.fs.get.args[0][0]).to.equal('test');
|
||||
expect(mockClient.reply.args[0][0]).to.equal(350);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ log: mockLog, command: { arg: 'test' } }).then(() => {
|
||||
expect(mockClient.fs.get.args[0][0]).to.equal('test')
|
||||
expect(mockClient.reply.args[0][0]).to.equal(350)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,77 +1,72 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const EventEmitter = require('events');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
const EventEmitter = require('events')
|
||||
|
||||
const CMD = 'RNTO';
|
||||
const CMD = 'RNTO'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockLog = {error: () => {}};
|
||||
let emitter;
|
||||
const mockClient = {reply: () => Promise.resolve()};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
let sandbox
|
||||
const mockLog = { error: () => {} }
|
||||
let emitter
|
||||
const mockClient = { reply: () => Promise.resolve() }
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
mockClient.renameFrom = 'test';
|
||||
mockClient.renameFrom = 'test'
|
||||
mockClient.fs = {
|
||||
get: () => Promise.resolve(),
|
||||
rename: () => Promise.resolve()
|
||||
};
|
||||
}
|
||||
|
||||
emitter = new EventEmitter();
|
||||
mockClient.emit = emitter.emit.bind(emitter);
|
||||
emitter = new EventEmitter()
|
||||
mockClient.emit = emitter.emit.bind(emitter)
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
sandbox.spy(mockClient.fs, 'rename');
|
||||
});
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
sandbox.spy(mockClient.fs, 'rename')
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// unsuccessful | no renameFrom set', () => {
|
||||
delete mockClient.renameFrom;
|
||||
delete mockClient.renameFrom
|
||||
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(503);
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(503)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
delete mockClient.fs
|
||||
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
mockClient.fs = {}
|
||||
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402)
|
||||
})
|
||||
})
|
||||
|
||||
it('new // unsuccessful | rename fails', () => {
|
||||
mockClient.fs.rename.restore();
|
||||
sandbox.stub(mockClient.fs, 'rename').rejects(new Error('test'));
|
||||
mockClient.fs.rename.restore()
|
||||
sandbox.stub(mockClient.fs, 'rename').rejects(new Error('test'))
|
||||
|
||||
return cmdFn({log: mockLog, command: {arg: 'new'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
return cmdFn({ log: mockLog, command: { arg: 'new' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550)
|
||||
})
|
||||
})
|
||||
|
||||
it('new // successful', () => {
|
||||
return cmdFn({command: {arg: 'new'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(250);
|
||||
expect(mockClient.fs.rename.args[0]).to.eql(['test', 'new']);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'new' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(250)
|
||||
expect(mockClient.fs.rename.args[0]).to.eql(['test', 'new'])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,69 +1,69 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'CHMOD';
|
||||
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);
|
||||
let sandbox
|
||||
const mockLog = { error: () => {} }
|
||||
const mockClient = { reply: () => Promise.resolve() }
|
||||
const cmdFn = require(`../../../../src/commands/registration/site/${CMD.toLowerCase()}`).bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
mockClient.fs = {
|
||||
chmod: () => Promise.resolve()
|
||||
};
|
||||
}
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
sandbox.spy(mockClient.fs, 'chmod');
|
||||
});
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
sandbox.spy(mockClient.fs, 'chmod')
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// unsuccessful | no file system', (done) => {
|
||||
delete mockClient.fs;
|
||||
delete mockClient.fs
|
||||
|
||||
cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550)
|
||||
done()
|
||||
})
|
||||
.catch(done)
|
||||
})
|
||||
|
||||
it('// unsuccessful | file system does not have functions', (done) => {
|
||||
mockClient.fs = {};
|
||||
mockClient.fs = {}
|
||||
|
||||
cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402)
|
||||
done()
|
||||
})
|
||||
.catch(done)
|
||||
})
|
||||
|
||||
it('777 test // unsuccessful | file chmod fails', (done) => {
|
||||
mockClient.fs.chmod.restore();
|
||||
sandbox.stub(mockClient.fs, 'chmod').rejects(new Error('test'));
|
||||
mockClient.fs.chmod.restore()
|
||||
sandbox.stub(mockClient.fs, 'chmod').rejects(new Error('test'))
|
||||
|
||||
cmdFn({log: mockLog, command: {arg: '777 test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(500);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
cmdFn({ log: mockLog, command: { arg: '777 test' } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(500)
|
||||
done()
|
||||
})
|
||||
.catch(done)
|
||||
})
|
||||
|
||||
it('777 test // successful', (done) => {
|
||||
cmdFn({log: mockLog, command: {arg: '777 test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.fs.chmod.args[0]).to.eql(['test', 511]);
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
cmdFn({ log: mockLog, command: { arg: '777 test' } })
|
||||
.then(() => {
|
||||
expect(mockClient.fs.chmod.args[0]).to.eql(['test', 511])
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200)
|
||||
done()
|
||||
})
|
||||
.catch(done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,52 +1,49 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const bunyan = require('bunyan');
|
||||
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');
|
||||
const siteRegistry = require('../../../../src/commands/registration/site/registry')
|
||||
const FtpCommands = require('../../../../src/commands')
|
||||
|
||||
const CMD = 'SITE';
|
||||
const CMD = 'SITE'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const log = bunyan.createLogger({name: 'site-test'});
|
||||
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.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.stub(mockClient, 'reply').resolves();
|
||||
});
|
||||
sandbox.stub(mockClient, 'reply').resolves()
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// unsuccessful', () => {
|
||||
return cmdFn({log})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(502);
|
||||
});
|
||||
});
|
||||
return cmdFn({ log }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(502)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful', () => {
|
||||
return cmdFn({log, command: {arg: 'BAD'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(502);
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { arg: 'BAD' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(502)
|
||||
})
|
||||
})
|
||||
|
||||
it('// successful', () => {
|
||||
sandbox.stub(siteRegistry.CHMOD, 'handler').resolves();
|
||||
sandbox.stub(siteRegistry.CHMOD, 'handler').resolves()
|
||||
|
||||
return cmdFn({log, command: {arg: 'CHMOD test'}})
|
||||
.then(() => {
|
||||
const {command} = siteRegistry.CHMOD.handler.args[0][0];
|
||||
expect(command.directive).to.equal('CHMOD');
|
||||
expect(command.arg).to.equal('test');
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { arg: 'CHMOD test' } }).then(() => {
|
||||
const { command } = siteRegistry.CHMOD.handler.args[0][0]
|
||||
expect(command.directive).to.equal('CHMOD')
|
||||
expect(command.arg).to.equal('test')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,58 +1,54 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'SIZE';
|
||||
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);
|
||||
let sandbox
|
||||
const mockLog = { error: () => {} }
|
||||
const mockClient = { reply: () => Promise.resolve() }
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
mockClient.fs = {
|
||||
get: () => Promise.resolve({size: 1})
|
||||
};
|
||||
get: () => Promise.resolve({ size: 1 })
|
||||
}
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
delete mockClient.fs
|
||||
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
mockClient.fs = {}
|
||||
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful | file get fails', () => {
|
||||
sandbox.stub(mockClient.fs, 'get').rejects(new Error('test'));
|
||||
sandbox.stub(mockClient.fs, 'get').rejects(new Error('test'))
|
||||
|
||||
return cmdFn({log: mockLog, command: {arg: 'test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
return cmdFn({ log: mockLog, command: { arg: 'test' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550)
|
||||
})
|
||||
})
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn({command: {arg: 'test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(213);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'test' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(213)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,61 +1,57 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'STAT';
|
||||
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);
|
||||
let sandbox
|
||||
const mockLog = { error: () => {} }
|
||||
const mockClient = { reply: () => Promise.resolve() }
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
mockClient.fs = {
|
||||
get: () => Promise.resolve({}),
|
||||
list: () => Promise.resolve([])
|
||||
};
|
||||
}
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(211);
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(211)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
delete mockClient.fs
|
||||
|
||||
return cmdFn({command: {arg: 'test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'test' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
mockClient.fs = {}
|
||||
|
||||
return cmdFn({command: {arg: 'test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'test' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful | file get fails', () => {
|
||||
sandbox.stub(mockClient.fs, 'get').rejects(new Error('test'));
|
||||
sandbox.stub(mockClient.fs, 'get').rejects(new Error('test'))
|
||||
|
||||
return cmdFn({log: mockLog, command: {arg: 'test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(450);
|
||||
});
|
||||
});
|
||||
return cmdFn({ log: mockLog, command: { arg: 'test' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(450)
|
||||
})
|
||||
})
|
||||
|
||||
it('// successful | file', () => {
|
||||
sandbox.stub(mockClient.fs, 'get').returns({
|
||||
@@ -75,33 +71,34 @@ describe(CMD, function () {
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => false
|
||||
});
|
||||
})
|
||||
|
||||
return cmdFn({command: {arg: 'test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(212);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'test' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(212)
|
||||
})
|
||||
})
|
||||
|
||||
it('// successful | directory', () => {
|
||||
sandbox.stub(mockClient.fs, 'list').returns([{
|
||||
name: 'test_file',
|
||||
dev: 2114,
|
||||
ino: 48064969,
|
||||
mode: 33188,
|
||||
nlink: 1,
|
||||
uid: 85,
|
||||
gid: 100,
|
||||
rdev: 0,
|
||||
size: 527,
|
||||
blksize: 4096,
|
||||
blocks: 8,
|
||||
atime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
mtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => false
|
||||
}]);
|
||||
sandbox.stub(mockClient.fs, 'list').returns([
|
||||
{
|
||||
name: 'test_file',
|
||||
dev: 2114,
|
||||
ino: 48064969,
|
||||
mode: 33188,
|
||||
nlink: 1,
|
||||
uid: 85,
|
||||
gid: 100,
|
||||
rdev: 0,
|
||||
size: 527,
|
||||
blksize: 4096,
|
||||
blocks: 8,
|
||||
atime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
mtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => false
|
||||
}
|
||||
])
|
||||
|
||||
sandbox.stub(mockClient.fs, 'get').returns({
|
||||
name: 'test_directory',
|
||||
@@ -120,11 +117,10 @@ describe(CMD, function () {
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => true
|
||||
});
|
||||
})
|
||||
|
||||
return cmdFn({command: {arg: 'test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(213);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'test' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(213)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
const Promise = require('bluebird');
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const EventEmitter = require('events');
|
||||
const Promise = require('bluebird')
|
||||
const bunyan = require('bunyan')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
const EventEmitter = require('events')
|
||||
|
||||
const CMD = 'STOR';
|
||||
const CMD = 'STOR'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
let emitter;
|
||||
let sandbox
|
||||
let log = bunyan.createLogger({ name: CMD })
|
||||
let emitter
|
||||
const mockClient = {
|
||||
commandSocket: {
|
||||
pause: () => {},
|
||||
@@ -16,83 +16,79 @@ describe(CMD, function () {
|
||||
},
|
||||
reply: () => Promise.resolve(),
|
||||
connector: {
|
||||
waitForConnection: () => Promise.resolve({
|
||||
resume: () => {}
|
||||
}),
|
||||
waitForConnection: () =>
|
||||
Promise.resolve({
|
||||
resume: () => {}
|
||||
}),
|
||||
end: () => Promise.resolve({})
|
||||
}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
}
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
mockClient.fs = {
|
||||
write: () => {}
|
||||
};
|
||||
}
|
||||
|
||||
emitter = new EventEmitter();
|
||||
mockClient.emit = emitter.emit.bind(emitter);
|
||||
mockClient.on = emitter.on.bind(emitter);
|
||||
mockClient.once = emitter.once.bind(emitter);
|
||||
emitter = new EventEmitter()
|
||||
mockClient.emit = emitter.emit.bind(emitter)
|
||||
mockClient.on = emitter.on.bind(emitter)
|
||||
mockClient.once = emitter.once.bind(emitter)
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
afterEach(() => sandbox.restore());
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
})
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
delete mockClient.fs
|
||||
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
mockClient.fs = {}
|
||||
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful | connector times out', () => {
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').callsFake(function () {
|
||||
return Promise.reject(new Promise.TimeoutError());
|
||||
});
|
||||
return Promise.reject(new Promise.TimeoutError())
|
||||
})
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(425);
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { arg: 'test.txt' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(425)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful | connector errors out', () => {
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').callsFake(function () {
|
||||
return Promise.reject(new Error('test'));
|
||||
});
|
||||
return Promise.reject(new Error('test'))
|
||||
})
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { arg: 'test.txt' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful | emits error event', () => {
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').callsFake(function () {
|
||||
return Promise.reject(new Error('test'));
|
||||
});
|
||||
return Promise.reject(new Error('test'))
|
||||
})
|
||||
|
||||
let errorEmitted = false;
|
||||
let errorEmitted = false
|
||||
emitter.once('STOR', (err) => {
|
||||
errorEmitted = !!err;
|
||||
});
|
||||
errorEmitted = !!err
|
||||
})
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'}})
|
||||
.then(() => {
|
||||
expect(errorEmitted).to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ log, command: { arg: 'test.txt' } }).then(() => {
|
||||
expect(errorEmitted).to.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,75 +1,71 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const stor = require('../../../src/commands/registration/stor');
|
||||
const stor = require('../../../src/commands/registration/stor')
|
||||
|
||||
const CMD = 'STOU';
|
||||
const CMD = 'STOU'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let sandbox
|
||||
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.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
mockClient.fs = {
|
||||
get: () => Promise.resolve(),
|
||||
getUniqueName: () => Promise.resolve('4')
|
||||
};
|
||||
}
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
sandbox.spy(mockClient.fs, 'get');
|
||||
sandbox.spy(mockClient.fs, 'getUniqueName');
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
sandbox.spy(mockClient.fs, 'get')
|
||||
sandbox.spy(mockClient.fs, 'getUniqueName')
|
||||
|
||||
sandbox.stub(stor.handler, 'call').resolves({});
|
||||
});
|
||||
sandbox.stub(stor.handler, 'call').resolves({})
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
delete mockClient.fs
|
||||
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
mockClient.fs = {}
|
||||
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402)
|
||||
})
|
||||
})
|
||||
|
||||
it('// successful | given name is unique', () => {
|
||||
mockClient.fs.get.restore();
|
||||
sandbox.stub(mockClient.fs, 'get').rejects({});
|
||||
mockClient.fs.get.restore()
|
||||
sandbox.stub(mockClient.fs, 'get').rejects({})
|
||||
|
||||
return cmdFn({command: {arg: 'good'}})
|
||||
.then(() => {
|
||||
const call = stor.handler.call.args[0][1];
|
||||
expect(call).to.have.property('command');
|
||||
expect(call.command).to.have.property('arg');
|
||||
expect(call.command.arg).to.eql('good');
|
||||
expect(mockClient.fs.getUniqueName.callCount).to.equal(0);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'good' } }).then(() => {
|
||||
const call = stor.handler.call.args[0][1]
|
||||
expect(call).to.have.property('command')
|
||||
expect(call.command).to.have.property('arg')
|
||||
expect(call.command.arg).to.eql('good')
|
||||
expect(mockClient.fs.getUniqueName.callCount).to.equal(0)
|
||||
})
|
||||
})
|
||||
|
||||
it('// successful | generates unique name', () => {
|
||||
return cmdFn({command: {arg: 'bad'}})
|
||||
.then(() => {
|
||||
const call = stor.handler.call.args[0][1];
|
||||
expect(call).to.have.property('command');
|
||||
expect(call.command).to.have.property('arg');
|
||||
expect(call.command.arg).to.eql('4');
|
||||
expect(mockClient.fs.getUniqueName.callCount).to.equal(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'bad' } }).then(() => {
|
||||
const call = stor.handler.call.args[0][1]
|
||||
expect(call).to.have.property('command')
|
||||
expect(call.command).to.have.property('arg')
|
||||
expect(call.command.arg).to.eql('4')
|
||||
expect(mockClient.fs.getUniqueName.callCount).to.equal(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,35 +1,33 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'STRU';
|
||||
const CMD = 'STRU'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let sandbox
|
||||
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.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn({command: {arg: 'F'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'F' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200)
|
||||
})
|
||||
})
|
||||
|
||||
it('// unsuccessful', () => {
|
||||
return cmdFn({command: {arg: 'X'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'X' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'SYST';
|
||||
const CMD = 'SYST'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let sandbox
|
||||
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.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(215);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn().then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(215)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,54 +1,50 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird')
|
||||
const { expect } = require('chai')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const CMD = 'TYPE';
|
||||
const CMD = 'TYPE'
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let sandbox
|
||||
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.bind(mockClient)
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise);
|
||||
sandbox = sinon.sandbox.create().usingPromise(Promise)
|
||||
|
||||
mockClient.transferType = null;
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
mockClient.transferType = null
|
||||
sandbox.spy(mockClient, 'reply')
|
||||
})
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
sandbox.restore()
|
||||
})
|
||||
|
||||
it('A // successful', () => {
|
||||
return cmdFn({command: {arg: 'A'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
expect(mockClient.transferType).to.equal('ascii');
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'A' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200)
|
||||
expect(mockClient.transferType).to.equal('ascii')
|
||||
})
|
||||
})
|
||||
|
||||
it('I // successful', () => {
|
||||
return cmdFn({command: {arg: 'I'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
expect(mockClient.transferType).to.equal('binary');
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'I' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200)
|
||||
expect(mockClient.transferType).to.equal('binary')
|
||||
})
|
||||
})
|
||||
|
||||
it('L // successful', () => {
|
||||
return cmdFn({command: {arg: 'L'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
expect(mockClient.transferType).to.equal('binary');
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'L' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200)
|
||||
expect(mockClient.transferType).to.equal('binary')
|
||||
})
|
||||
})
|
||||
|
||||
it('X // successful', () => {
|
||||
return cmdFn({command: {arg: 'X'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501);
|
||||
expect(mockClient.transferType).to.equal(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
return cmdFn({ command: { arg: 'X' } }).then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501)
|
||||
expect(mockClient.transferType).to.equal(null)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user