Compare commits
92 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a794f1e5b3 | ||
|
|
1b5d22a3ca | ||
|
|
4205caf7ac | ||
|
|
a468d4ffd0 | ||
|
|
40b08893ac | ||
|
|
8a2454ceea | ||
|
|
0c7cc4fe6e | ||
|
|
6ea6baceb0 | ||
|
|
b07e0189ee | ||
|
|
ec30a5a4f3 | ||
|
|
6020409979 | ||
|
|
c60606971a | ||
|
|
bd41b31821 | ||
|
|
ce1c526c41 | ||
|
|
d822101a07 | ||
|
|
47c8eedd3b | ||
|
|
6c08cc2aed | ||
|
|
e2a5c78b0a | ||
|
|
2cadac3f7e | ||
|
|
2255be9acd | ||
|
|
d22c911a36 | ||
|
|
5dabbc251b | ||
|
|
ef89577627 | ||
|
|
8fbe750086 | ||
|
|
3b33508f44 | ||
|
|
23368b04b9 | ||
|
|
876a061e92 | ||
|
|
65b1fd27a0 | ||
|
|
286c1063fa | ||
|
|
e87c36d7ff | ||
|
|
de0aafad2f | ||
|
|
4f80e11745 | ||
|
|
6bbd905379 | ||
|
|
de50f55457 | ||
|
|
32cdedd163 | ||
|
|
6c2c1a87dc | ||
|
|
9e83143690 | ||
|
|
0238529edf | ||
|
|
d0c204eb81 | ||
|
|
cdebe9a464 | ||
|
|
eeb8f9ab4d | ||
|
|
60d06c21c8 | ||
|
|
8609b1d02e | ||
|
|
80b05215ff | ||
|
|
37f0a15549 | ||
|
|
1ba67034b1 | ||
|
|
0a331c5998 | ||
|
|
a7103ded7e | ||
|
|
d787d4cab6 | ||
|
|
154cd5a5d7 | ||
|
|
5fc59b50b1 | ||
|
|
043c97c80f | ||
|
|
772fe5ca06 | ||
|
|
e272802525 | ||
|
|
7589322abc | ||
|
|
fae5564041 | ||
|
|
e9b4a6385d | ||
|
|
71621aae4f | ||
|
|
0eaa0f8743 | ||
|
|
8828a4ea09 | ||
|
|
b33659320f | ||
|
|
6a6b949d3b | ||
|
|
283be85db3 | ||
|
|
e555ce9230 | ||
|
|
e6575808f1 | ||
|
|
a5e58a106e | ||
|
|
ed086e576a | ||
|
|
31f0f3b0dc | ||
|
|
d763820c86 | ||
|
|
f3183314cc | ||
|
|
dde7b36c46 | ||
|
|
00af9e7e61 | ||
|
|
99a885cd44 | ||
|
|
443051d753 | ||
|
|
27ecc4d835 | ||
|
|
c8526be1f4 | ||
|
|
e0b11ff480 | ||
|
|
58b9d8db9d | ||
|
|
fa121ba0fd | ||
|
|
2e02dc20ad | ||
|
|
8aeb6976d2 | ||
|
|
84a68ae03c | ||
|
|
9dfc80b99d | ||
|
|
090e3d8105 | ||
|
|
c3b0dbf5b0 | ||
|
|
69a5133936 | ||
|
|
5394908a6b | ||
|
|
3e7bd5bcf9 | ||
|
|
175b422c5f | ||
|
|
b2a9851204 | ||
|
|
977dd1579a | ||
|
|
176b2b7ca8 |
140
.circleci/config.yml
Normal file
140
.circleci/config.yml
Normal file
@@ -0,0 +1,140 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build_node_8:
|
||||
docker:
|
||||
- image: circleci/node:8
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: npm-install-node-8-{{ checksum "package.json" }}
|
||||
- run:
|
||||
name: Install
|
||||
command: npm install
|
||||
- save_cache:
|
||||
key: npm-install-node-8-{{ checksum "package.json" }}
|
||||
paths:
|
||||
- node_modules
|
||||
|
||||
build_node_6:
|
||||
docker:
|
||||
- image: circleci/node:6
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: npm-install-node-6-{{ checksum "package.json" }}
|
||||
- run:
|
||||
name: Install
|
||||
command: npm install
|
||||
- save_cache:
|
||||
key: npm-install-node-6-{{ checksum "package.json" }}
|
||||
paths:
|
||||
- node_modules
|
||||
|
||||
lint:
|
||||
docker:
|
||||
- image: circleci/node:8
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: npm-install-node-8-{{ checksum "package.json" }}
|
||||
- run:
|
||||
name: Lint
|
||||
command: npm run verify:js
|
||||
|
||||
test_node_8:
|
||||
docker:
|
||||
- image: circleci/node:8
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: npm-install-node-8-{{ checksum "package.json" }}
|
||||
- run:
|
||||
name: Test Node 8
|
||||
command: npm run test:coverage
|
||||
when: always
|
||||
- store_test_results:
|
||||
path: reports
|
||||
- store_artifacts:
|
||||
path: reports/coverage
|
||||
prefix: coverage
|
||||
|
||||
test_node_6:
|
||||
docker:
|
||||
- image: circleci/node:6
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: npm-install-node-6-{{ checksum "package.json" }}
|
||||
- run:
|
||||
name: Test Node 6
|
||||
command: npm run test:coverage
|
||||
when: always
|
||||
- store_test_results:
|
||||
path: reports
|
||||
- store_artifacts:
|
||||
path: reports/coverage
|
||||
prefix: coverage
|
||||
|
||||
release:
|
||||
docker:
|
||||
- image: circleci/node:8
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: npm-install-node-6-{{ checksum "package.json" }}
|
||||
- run:
|
||||
name: Update NPM
|
||||
command: |
|
||||
npm install npm@5
|
||||
npm install semantic-release@11
|
||||
- deploy:
|
||||
name: Semantic Release
|
||||
command: |
|
||||
npm run semantic-release || true
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
test_and_tag:
|
||||
jobs:
|
||||
- build_node_8:
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
- build_node_6:
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
- lint:
|
||||
requires:
|
||||
- build_node_8
|
||||
- test_node_6:
|
||||
requires:
|
||||
- build_node_6
|
||||
- test_node_8:
|
||||
requires:
|
||||
- build_node_8
|
||||
- release:
|
||||
requires:
|
||||
- lint
|
||||
- test_node_6
|
||||
- test_node_8
|
||||
|
||||
build_and_test:
|
||||
jobs:
|
||||
- build_node_8:
|
||||
filters:
|
||||
branches:
|
||||
ignore: master
|
||||
- build_node_6:
|
||||
filters:
|
||||
branches:
|
||||
ignore: master
|
||||
- lint:
|
||||
requires:
|
||||
- build_node_8
|
||||
- test_node_6:
|
||||
requires:
|
||||
- build_node_6
|
||||
- test_node_8:
|
||||
requires:
|
||||
- build_node_8
|
||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
package-lock.json binary
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,3 +3,5 @@ node_modules/
|
||||
dist/
|
||||
reports/
|
||||
npm-debug.log
|
||||
.nyc_output/
|
||||
test_tmp/
|
||||
24
.nycrc
Normal file
24
.nycrc
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"check-coverage": true,
|
||||
"per-file": false,
|
||||
"lines": 90,
|
||||
"statements": 90,
|
||||
"functions": 85,
|
||||
"branches": 75,
|
||||
"include": [
|
||||
"src/**/*.js"
|
||||
],
|
||||
"exclude": [
|
||||
"test/**/*.spec.js"
|
||||
],
|
||||
"reporter": [
|
||||
"lcovonly",
|
||||
"html",
|
||||
"text",
|
||||
"cobertura",
|
||||
"json"
|
||||
],
|
||||
"cache": true,
|
||||
"all": true,
|
||||
"report-dir": "./reports/coverage/"
|
||||
}
|
||||
25
.travis.yml
25
.travis.yml
@@ -1,25 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "6"
|
||||
- "node"
|
||||
|
||||
env:
|
||||
FTP_URL: ftp://127.0.0.1:8880
|
||||
PASV_RANGE: 8881
|
||||
|
||||
install: npm install
|
||||
|
||||
script:
|
||||
- npm run verify:js
|
||||
- npm run test:coverage
|
||||
|
||||
after_script:
|
||||
- npm run upload-coverage
|
||||
|
||||
deploy:
|
||||
skip_cleanup: true
|
||||
provider: script
|
||||
script: npm run semantic-release
|
||||
on:
|
||||
branch: master
|
||||
node: "6"
|
||||
@@ -136,6 +136,9 @@ Command | Description
|
||||
Command | Description
|
||||
:------ | :----------
|
||||
<pre>npm run verify</pre> | Verify code style and syntax<ul><li>Verifies source *and test code* aginst customisable rules (unlike Webpack loaders)</li></ul>
|
||||
<pre>npm run verify:js</pre> | Verify Javascript code style and syntax
|
||||
<pre>npm run verify:js:fix</pre> | Verify Javascript code style and syntax and fix any errors that can be fixed automatically
|
||||
<pre>npm run verify:js:watch</pre> | Verify Javascript code style and syntax and watch files for changes
|
||||
<pre>npm run verify:watch</pre> | Runs verify task whenever JS or CSS code is changed
|
||||
|
||||
|
||||
|
||||
22
LICENSE
22
LICENSE
@@ -1,9 +1,21 @@
|
||||
ftp-srv Copyright (c) 2017 Tyler Stewart
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
Copyright (c) 2017 Tyler Stewart
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
76
README.md
76
README.md
@@ -1,12 +1,27 @@
|
||||
[](https://github.com/trs/ftp-srv)
|
||||
<p align="center">
|
||||
<a href="https://github.com/trs/ftp-srv">
|
||||
<img alt="ftp-srv" src="logo.png" width="600px" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<!--[RM_DESCRIPTION]-->
|
||||
> Modern, extensible FTP Server
|
||||
|
||||
<!--[]-->
|
||||
<p align="center">
|
||||
Modern, extensible FTP Server
|
||||
</p>
|
||||
|
||||
[](https://badge.fury.io/js/ftp-srv) [](https://travis-ci.org/trs/ftp-srv)
|
||||
[](https://coveralls.io/github/trs/ftp-srv?branch=coveralls) [](https://github.com/semantic-release/semantic-release) [](http://commitizen.github.io/cz-cli/)
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/package/ftp-srv">
|
||||
<img alt="npm" src="https://img.shields.io/npm/dm/ftp-srv.svg?style=for-the-badge" />
|
||||
</a>
|
||||
|
||||
<a href="https://circleci.com/gh/trs/ftp-srv">
|
||||
<img alt="npm" src="https://img.shields.io/circleci/project/github/trs/ftp-srv.svg?style=for-the-badge" />
|
||||
</a>
|
||||
|
||||
<a href="https://coveralls.io/github/trs/ftp-srv?branch=master">
|
||||
<img alt="npm" src="https://img.shields.io/coveralls/github/trs/ftp-srv.svg?style=for-the-badge" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
@@ -28,6 +43,7 @@
|
||||
- Extensible [file systems](#file-system) per connection
|
||||
- Passive and active transfers
|
||||
- [Explicit](https://en.wikipedia.org/wiki/FTPS#Explicit) & [Implicit](https://en.wikipedia.org/wiki/FTPS#Implicit) TLS connections
|
||||
- Promise based API
|
||||
|
||||
## Install
|
||||
`npm install ftp-srv --save`
|
||||
@@ -37,8 +53,8 @@
|
||||
```js
|
||||
// Quick start
|
||||
|
||||
const FtpSvr = require('ftp-srv');
|
||||
const ftpServer = new FtpSvr('ftp://0.0.0.0:9876', { options ... });
|
||||
const FtpSrv = require('ftp-srv');
|
||||
const ftpServer = new FtpSrv('ftp://0.0.0.0:9876', { options ... });
|
||||
|
||||
ftpServer.on('login', (data, resolve, reject) => { ... });
|
||||
...
|
||||
@@ -105,11 +121,11 @@ A [bunyan logger](https://github.com/trentm/node-bunyan) instance. Created by de
|
||||
|
||||
## Events
|
||||
|
||||
The `FtpSvr` class extends the [node net.Server](https://nodejs.org/api/net.html#net_class_net_server). Some custom events can be resolved or rejected, such as `login`.
|
||||
The `FtpSrv` class extends the [node net.Server](https://nodejs.org/api/net.html#net_class_net_server). Some custom events can be resolved or rejected, such as `login`.
|
||||
|
||||
### `login`
|
||||
```js
|
||||
on('login', {connection, username, password}, resolve, reject) => { ... }
|
||||
on('login', ({connection, username, password}, resolve, reject) => { ... });
|
||||
```
|
||||
|
||||
Occurs when a client is attempting to login. Here you can resolve the login request by username and password.
|
||||
@@ -136,7 +152,7 @@ Occurs when a client is attempting to login. Here you can resolve the login requ
|
||||
|
||||
### `client-error`
|
||||
```js
|
||||
on('client-error', {connection, context, error}) => { ... }
|
||||
on('client-error', ({connection, context, error}) => { ... });
|
||||
```
|
||||
|
||||
Occurs when an error arises in the client connection.
|
||||
@@ -145,6 +161,26 @@ Occurs when an error arises in the client connection.
|
||||
`context` string of where the error occured
|
||||
`error` error object
|
||||
|
||||
### `RETR`
|
||||
```js
|
||||
on('RETR', (error, filePath) => { ... });
|
||||
```
|
||||
|
||||
Occurs when a file is downloaded.
|
||||
|
||||
`error` if successful, will be `null`
|
||||
`filePath` location to which file was downloaded
|
||||
|
||||
### `STOR`
|
||||
```js
|
||||
on('STOR', (error, fileName) => { ... });
|
||||
```
|
||||
|
||||
Occurs when a file is uploaded.
|
||||
|
||||
`error` if successful, will be `null`
|
||||
`fileName` name of the file that was downloaded
|
||||
|
||||
## Supported Commands
|
||||
|
||||
See the [command registry](src/commands/registration) for a list of all implemented FTP commands.
|
||||
@@ -192,13 +228,17 @@ __Used in:__ `CWD`, `CDUP`
|
||||
Returns a path to a newly created directory
|
||||
__Used in:__ `MKD`
|
||||
|
||||
#### [`write(fileName, {append = false})`](src/fs.js#L68)
|
||||
#### [`write(fileName, {append, start})`](src/fs.js#L68)
|
||||
Returns a writable stream
|
||||
Options: `append` if true, append to existing file
|
||||
Options:
|
||||
`append` if true, append to existing file
|
||||
`start` if set, specifies the byte offset to write to
|
||||
__Used in:__ `STOR`, `APPE`
|
||||
|
||||
#### [`read(fileName)`](src/fs.js#L75)
|
||||
#### [`read(fileName, {start})`](src/fs.js#L75)
|
||||
Returns a readable stream
|
||||
Options:
|
||||
`start` if set, specifies the byte offset to read from
|
||||
__Used in:__ `RETR`
|
||||
|
||||
#### [`delete(path)`](src/fs.js#L87)
|
||||
@@ -206,11 +246,11 @@ Delete a file or directory
|
||||
__Used in:__ `DELE`
|
||||
|
||||
#### [`rename(from, to)`](src/fs.js#L102)
|
||||
Rename a file or directory
|
||||
Renames a file or directory
|
||||
__Used in:__ `RNFR`, `RNTO`
|
||||
|
||||
#### [`chmod(path)`](src/fs.js#L108)
|
||||
Modify a file or directory's permissions
|
||||
Modifies a file or directory's permissions
|
||||
__Used in:__ `SITE CHMOD`
|
||||
|
||||
#### [`getUniqueName()`](src/fs.js#L113)
|
||||
@@ -231,3 +271,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
This software is licensed under the MIT Licence. See [LICENSE](LICENSE).
|
||||
|
||||
<!--[]-->
|
||||
|
||||
## References
|
||||
|
||||
- [https://cr.yp.to/ftp.html](https://cr.yp.to/ftp.html)
|
||||
|
||||
@@ -15,12 +15,7 @@ module.exports = {
|
||||
{value: 'WIP', name: 'WIP: Work in progress'}
|
||||
],
|
||||
|
||||
scopes: [
|
||||
{name: 'accounts'},
|
||||
{name: 'admin'},
|
||||
{name: 'exampleScope'},
|
||||
{name: 'changeMe'}
|
||||
],
|
||||
scopes: [],
|
||||
|
||||
// it needs to match the value for field type. Eg.: 'fix'
|
||||
/*
|
||||
@@ -39,5 +34,5 @@ module.exports = {
|
||||
allowBreakingChanges: ['feat', 'fix'],
|
||||
|
||||
// Appends the branch name to the footer of the commit. Useful for tracking commits after branches have been merged
|
||||
appendBranchNameToCommitMessage: true
|
||||
appendBranchNameToCommitMessage: false
|
||||
};
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
// Use JS to support loading of threshold data from external file
|
||||
var coverageConfig = {
|
||||
instrumentation: {
|
||||
root: 'src/',
|
||||
excludes: ['errors.js']
|
||||
},
|
||||
check: require('./thresholds.json'),
|
||||
reporting: {
|
||||
print: 'both',
|
||||
dir: 'reports/coverage/',
|
||||
reports: [
|
||||
'cobertura',
|
||||
'html',
|
||||
'lcovonly',
|
||||
'html',
|
||||
'json'
|
||||
],
|
||||
'report-config': {
|
||||
cobertura: {
|
||||
file: 'cobertura/coverage.xml'
|
||||
},
|
||||
json: {
|
||||
file: 'json/coverage.json'
|
||||
},
|
||||
lcovonly: {
|
||||
file: 'lcov/lcov.info'
|
||||
},
|
||||
text: {
|
||||
file: null
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = coverageConfig;
|
||||
@@ -1,3 +1,5 @@
|
||||
test/**/*.spec.js
|
||||
--reporter mocha-pretty-bunyan-nyan
|
||||
--reporter mocha-multi-reporters
|
||||
--reporter-options configFile=config/testUnit/reporters.json
|
||||
--ui bdd
|
||||
--bail
|
||||
6
config/testUnit/reporters.json
Normal file
6
config/testUnit/reporters.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"reporterEnabled": "mocha-pretty-bunyan-nyan",
|
||||
"mochaJunitReporterReporterOptions": {
|
||||
"mochaFile": "reports/junit.xml"
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"global": {
|
||||
"statements": 90,
|
||||
"branches": 80,
|
||||
"functions": 90,
|
||||
"lines": 90
|
||||
},
|
||||
"each": {
|
||||
"statements": 70,
|
||||
"branches": 40,
|
||||
"functions": 60,
|
||||
"lines": 70
|
||||
}
|
||||
}
|
||||
@@ -1,60 +1,162 @@
|
||||
# START_CONFIT_GENERATED_CONTENT
|
||||
confit:
|
||||
extends: &confit-extends
|
||||
- plugin:node/recommended
|
||||
|
||||
plugins: &confit-plugins
|
||||
- node
|
||||
|
||||
env: &confit-env
|
||||
commonjs: true # For Webpack, CommonJS
|
||||
node: true
|
||||
mocha: true
|
||||
es6: true
|
||||
|
||||
globals: &confit-globals {}
|
||||
parser: &confit-parser espree
|
||||
|
||||
parserOptions: &confit-parserOptions
|
||||
ecmaVersion: 6
|
||||
sourceType: module
|
||||
ecmaFeatures:
|
||||
globalReturn: false
|
||||
impliedStrict: true
|
||||
jsx: false
|
||||
|
||||
# END_CONFIT_GENERATED_CONTENT
|
||||
|
||||
# Customise this section to meet your needs...
|
||||
|
||||
extends: *confit-extends
|
||||
# Uncomment this next line if you need to add more items to the array, and remove the "*confit-extends" from the line above
|
||||
# <<: *confit-extends
|
||||
|
||||
plugins: *confit-plugins
|
||||
# Uncomment this next line if you need to add more items to the array, and remove the "*confit-plugins" from the line above
|
||||
# <<: *confit-extends
|
||||
|
||||
env:
|
||||
<<: *confit-env
|
||||
|
||||
globals:
|
||||
<<: *confit-globals
|
||||
|
||||
parser: *confit-parser
|
||||
|
||||
parserOptions:
|
||||
<<: *confit-parserOptions
|
||||
|
||||
rules:
|
||||
max-len:
|
||||
- warn
|
||||
- 200 # Line Length
|
||||
node/no-unpublished-require:
|
||||
- 2
|
||||
- allowModules:
|
||||
- chai
|
||||
- dotenv
|
||||
- ftp
|
||||
- sinon
|
||||
- sinon-as-promised
|
||||
{
|
||||
"extends": "eslint:recommended",
|
||||
"env": {
|
||||
"node": true,
|
||||
"mocha": true,
|
||||
"es6": true
|
||||
},
|
||||
"plugins": [
|
||||
"mocha",
|
||||
"node"
|
||||
],
|
||||
"rules": {
|
||||
"mocha/no-exclusive-tests": 2,
|
||||
"no-warning-comments": [
|
||||
1,
|
||||
{
|
||||
"terms": ["todo", "fixme", "xxx"],
|
||||
"location": "start"
|
||||
},
|
||||
],
|
||||
"object-curly-spacing": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"array-bracket-spacing": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"brace-style": [
|
||||
2,
|
||||
"1tbs"
|
||||
],
|
||||
"consistent-return": 0,
|
||||
"indent": [
|
||||
"error",
|
||||
2,
|
||||
{
|
||||
"SwitchCase": 1,
|
||||
"MemberExpression": "off"
|
||||
}
|
||||
],
|
||||
"no-multiple-empty-lines": [
|
||||
2,
|
||||
{
|
||||
"max": 2
|
||||
}
|
||||
],
|
||||
"no-use-before-define": [
|
||||
2,
|
||||
"nofunc"
|
||||
],
|
||||
"one-var": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"quote-props": [
|
||||
2,
|
||||
"as-needed"
|
||||
],
|
||||
"quotes": [
|
||||
2,
|
||||
"single"
|
||||
],
|
||||
"keyword-spacing": 2,
|
||||
"space-before-function-paren": [
|
||||
2,
|
||||
{
|
||||
"anonymous": "always",
|
||||
"named": "never"
|
||||
}
|
||||
],
|
||||
"space-in-parens": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"strict": [
|
||||
2,
|
||||
"global"
|
||||
],
|
||||
"curly": [
|
||||
2,
|
||||
"multi-line"
|
||||
],
|
||||
"eol-last": 2,
|
||||
"key-spacing": [
|
||||
2,
|
||||
{
|
||||
"beforeColon": false,
|
||||
"afterColon": true
|
||||
}
|
||||
],
|
||||
"no-eval": 2,
|
||||
"no-with": 2,
|
||||
"space-infix-ops": 2,
|
||||
"dot-notation": [
|
||||
2,
|
||||
{
|
||||
"allowKeywords": true
|
||||
}
|
||||
],
|
||||
"eqeqeq": 2,
|
||||
"no-alert": 2,
|
||||
"no-caller": 2,
|
||||
"no-extend-native": 2,
|
||||
"no-extra-bind": 2,
|
||||
"no-implied-eval": 2,
|
||||
"no-iterator": 2,
|
||||
"no-label-var": 2,
|
||||
"no-labels": 2,
|
||||
"no-lone-blocks": 2,
|
||||
"no-loop-func": 2,
|
||||
"no-multi-spaces": 2,
|
||||
"no-multi-str": 2,
|
||||
"no-native-reassign": 2,
|
||||
"no-new": 2,
|
||||
"no-new-func": 2,
|
||||
"no-new-wrappers": 2,
|
||||
"no-octal-escape": 2,
|
||||
"no-proto": 2,
|
||||
"no-return-assign": 2,
|
||||
"no-script-url": 2,
|
||||
"no-sequences": 2,
|
||||
"no-unused-expressions": 2,
|
||||
"yoda": 2,
|
||||
"no-shadow": 2,
|
||||
"no-shadow-restricted-names": 2,
|
||||
"no-undef-init": 2,
|
||||
"no-console": 1,
|
||||
"camelcase": [
|
||||
0,
|
||||
{
|
||||
"properties": "never"
|
||||
}
|
||||
],
|
||||
"comma-spacing": 2,
|
||||
"comma-dangle": 1,
|
||||
"new-cap": 2,
|
||||
"new-parens": 2,
|
||||
"arrow-parens": [2, "as-needed"],
|
||||
"no-array-constructor": 2,
|
||||
"array-callback-return": 1,
|
||||
"no-extra-parens": 2,
|
||||
"no-new-object": 2,
|
||||
"no-spaced-func": 2,
|
||||
"no-trailing-spaces": 2,
|
||||
"no-underscore-dangle": 0,
|
||||
"no-fallthrough": 0,
|
||||
"semi": 2,
|
||||
"semi-spacing": [
|
||||
2,
|
||||
{
|
||||
"before": false,
|
||||
"after": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"parserOptions": {
|
||||
"emcaVersion": 6,
|
||||
"sourceType": "module",
|
||||
"impliedStrict": true
|
||||
}
|
||||
}
|
||||
|
||||
13
confit.yml
13
confit.yml
@@ -1,6 +1,6 @@
|
||||
generator-confit:
|
||||
app:
|
||||
_version: f02196cc5cb7941ca46ec46d23bd6aef0dfcaca0
|
||||
_version: 462ecd915fd9db1aef6a37c2b5ce8b58b80c18ba
|
||||
buildProfile: Latest
|
||||
copyrightOwner: Tyler Stewart
|
||||
license: MIT
|
||||
@@ -8,7 +8,7 @@ generator-confit:
|
||||
publicRepository: true
|
||||
repositoryType: GitHub
|
||||
paths:
|
||||
_version: 7f33e41600b34cd6867478d8f2b3d6b2bbd42508
|
||||
_version: 780b129e0c7e5cab7e29c4f185bcf78524593a33
|
||||
config:
|
||||
configDir: config/
|
||||
input:
|
||||
@@ -18,22 +18,23 @@ generator-confit:
|
||||
prodDir: dist/
|
||||
reportDir: reports/
|
||||
buildJS:
|
||||
_version: df428a706d926204228c5d9ebdbd7b49908926d9
|
||||
_version: ead8ce4280b07d696aff499a5fca1a933727582f
|
||||
framework: []
|
||||
frameworkScripts: []
|
||||
outputFormat: ES6
|
||||
sourceFormat: ES6
|
||||
entryPoint:
|
||||
_version: de20402bf85c703080ef6daf21e35325a3b9d604
|
||||
_version: 39082c3df887fbc08744dfd088c25465e7a2e3a4
|
||||
entryPoints:
|
||||
main:
|
||||
- src/index.js
|
||||
testUnit:
|
||||
_version: 4472a6d59b434226f463992d3c1914c77a6a115d
|
||||
_version: 30eee42a88ee42cce4f1ae48fe0cbe81647d189a
|
||||
testDependencies: []
|
||||
testFramework: mocha
|
||||
verify:
|
||||
_version: 30ae86c5022840a01fc08833e238a82c683fa1c7
|
||||
jsCodingStandard: eslint
|
||||
jsCodingStandard: none
|
||||
documentation:
|
||||
_version: b1658da3278b16d1982212f5e8bc05348af20e0b
|
||||
generateDocs: false
|
||||
|
||||
123
ftp-srv.d.ts
vendored
Normal file
123
ftp-srv.d.ts
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
import * as tls from 'tls'
|
||||
import { Stats } from 'fs'
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
export class FileSystem {
|
||||
|
||||
readonly connection: FtpConnection;
|
||||
readonly root: string;
|
||||
readonly cwd: string;
|
||||
|
||||
constructor(connection: FtpConnection, {root, cwd}?: {
|
||||
root: any;
|
||||
cwd: any;
|
||||
});
|
||||
|
||||
currentDirectory(): string;
|
||||
|
||||
get(fileName: string): Promise<any>;
|
||||
|
||||
list(path?: string): Promise<any>;
|
||||
|
||||
chdir(path?: string): Promise<string>;
|
||||
|
||||
write(fileName: string, {append, start}?: {
|
||||
append?: boolean;
|
||||
start?: any;
|
||||
}): any;
|
||||
|
||||
read(fileName: string, {start}?: {
|
||||
start?: any;
|
||||
}): Promise<any>;
|
||||
|
||||
delete(path: string): Promise<any>;
|
||||
|
||||
mkdir(path: string): Promise<any>;
|
||||
|
||||
rename(from: string, to: string): Promise<any>;
|
||||
|
||||
chmod(path: string, mode: string): Promise<any>;
|
||||
|
||||
getUniqueName(): string;
|
||||
}
|
||||
|
||||
export class FtpConnection {
|
||||
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 {
|
||||
pasv_range?: number | string,
|
||||
greeting?: string | string[],
|
||||
tls?: tls.SecureContext | false,
|
||||
anonymous?: boolean,
|
||||
blacklist?: Array<string>,
|
||||
whitelist?: Array<string>,
|
||||
file_format?: (stat: Stats) => string | Promise<string> | "ls" | "ep",
|
||||
log?: any
|
||||
}
|
||||
|
||||
export class FtpServer {
|
||||
constructor(url: string, options?: FtpServerOptions);
|
||||
|
||||
readonly isTLS: boolean;
|
||||
|
||||
listen(): any;
|
||||
|
||||
emitPromise(action: any, ...data: any[]): Promise<any>;
|
||||
|
||||
emit(action: any, ...data: any[]): void;
|
||||
|
||||
setupTLS(_tls: boolean): boolean | {
|
||||
cert: string;
|
||||
key: string;
|
||||
ca: string
|
||||
};
|
||||
|
||||
setupGreeting(greet: string): string[];
|
||||
|
||||
setupFeaturesMessage(): string;
|
||||
|
||||
disconnectClient(id: string): Promise<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): EventEmitter;
|
||||
|
||||
on(event: "client-error", listener: (
|
||||
data: {
|
||||
connection: FtpConnection,
|
||||
context: string,
|
||||
error: Error,
|
||||
}
|
||||
) => void): EventEmitter;
|
||||
}
|
||||
|
||||
export {FtpServer as FtpSrv};
|
||||
export default FtpServer;
|
||||
BIN
logo.png
BIN
logo.png
Binary file not shown.
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 22 KiB |
@@ -1,17 +1,23 @@
|
||||
/*
|
||||
Send Button by Bruno Bosse from the Noun Project
|
||||
https://thenounproject.com/brunobosse/collection/basics/?i=1054386
|
||||
*/
|
||||
const puppeteer = require('puppeteer');
|
||||
const logoPath = `file://${process.cwd()}/logo/logo.html`;
|
||||
|
||||
const fs = require('fs');
|
||||
const htmlConvert = require('html-convert');
|
||||
|
||||
const convert = htmlConvert();
|
||||
let ws = fs.createWriteStream('logo.png');
|
||||
let rs = convert('logo/logo.html', {
|
||||
width: 350,
|
||||
height: 76
|
||||
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
|
||||
})
|
||||
.then(() => page.screenshot({
|
||||
path: 'logo.png',
|
||||
omitBackground: true
|
||||
}));
|
||||
})
|
||||
.then(() => browser.close());
|
||||
});
|
||||
|
||||
rs.pipe(ws);
|
||||
ws.on('finish', () => process.exit());
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
<svg width="566" height="580" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<g>
|
||||
<title>Layer 1</title>
|
||||
<g display="none" id="svg_1">
|
||||
<g display="inline" id="svg_2">
|
||||
<circle cx="337.851" cy="245.093" r="68.103" id="svg_3"/>
|
||||
<path d="m283.545,24.007c-145.682,0 -263.781,118.099 -263.781,263.781s118.099,263.781 263.781,263.781s263.781,-118.099 263.781,-263.781s-118.099,-263.781 -263.781,-263.781zm135.007,301.787c-35.898,35.898 -89.692,42.92 -132.482,20.934l-76.709,76.709c-13.651,13.651 -35.783,13.651 -49.434,0c-13.651,-13.651 -13.651,-35.784 0,-49.435l76.777,-76.777c-21.923,-42.853 -15.06,-96.699 20.761,-132.52c44.483,-44.483 116.448,-44.64 160.931,-0.157c44.482,44.485 44.639,116.763 0.156,161.246z" id="svg_4"/>
|
||||
</g>
|
||||
</g>
|
||||
<g display="none" id="svg_5">
|
||||
<g display="inline" id="svg_6">
|
||||
<path d="m282,211.676c-7.801,0 -13,6.324 -13,14.125l0,66.199l-40.682,0c-7.975,0 -14.318,5.978 -14.318,13.953l0,0.597c0,7.975 6.343,14.449 14.318,14.449l54.641,0c0.053,0 -0.495,-0.012 -0.442,-0.013c0.053,0.001 -0.495,0.004 -0.442,0.004c7.801,0 12.925,-6.324 12.925,-14.125l0,-0.315l0,-0.597l0,-80.152c0,-7.801 -5.199,-14.125 -13,-14.125z" id="svg_7"/>
|
||||
<path d="m283.545,24.007c-145.682,0 -263.781,118.099 -263.781,263.781s118.099,263.781 263.781,263.781s263.781,-118.099 263.781,-263.781s-118.099,-263.781 -263.781,-263.781zm-139.125,208.04c-0.89,-1.277 -1.569,-2.707 -2.003,-4.244c-3.279,-7.649 -5.111,-16.066 -5.111,-24.915c0,-34.997 28.371,-63.368 63.368,-63.368c14.59,0 28.023,4.937 38.735,13.224c1.019,0.639 1.951,1.4 2.775,2.268c2.431,2.56 3.932,6.012 3.932,9.82c0,5.696 -3.344,10.6 -8.169,12.891c-0.831,0.395 -1.704,0.709 -2.615,0.938c-27.13,10.06 -50.303,28.252 -66.567,51.62c-0.558,1.116 -1.257,2.145 -2.077,3.069c-2.601,2.933 -6.387,4.792 -10.616,4.792c-4.828,0.001 -9.086,-2.414 -11.652,-6.095zm257.1,194.907c-13.91,13.91 -36.463,13.91 -50.373,0l-5.58,-5.58c-18.184,10.084 -39.105,15.833 -61.371,15.833c-22.105,0 -42.885,-5.664 -60.977,-15.613l-5.36,5.36c-13.91,13.91 -36.463,13.91 -50.373,0c-13.91,-13.91 -13.91,-36.464 0,-50.374l5.424,-5.424c-9.849,-18.023 -15.449,-38.698 -15.449,-60.683c0,-69.995 56.741,-126.735 126.735,-126.735s126.735,56.741 126.735,126.735c0,21.823 -5.518,42.358 -15.232,60.286l5.821,5.821c13.91,13.91 13.91,36.464 0,50.374zm24.752,-197.077c-0.402,1.103 -0.924,2.149 -1.573,3.104c-2.536,3.727 -6.81,6.175 -11.658,6.175c-4.483,0 -8.469,-2.099 -11.05,-5.363c-0.508,-0.642 -0.968,-1.323 -1.36,-2.049c-16.153,-23.843 -39.432,-42.457 -66.784,-52.788c-1.048,-0.252 -2.048,-0.618 -2.991,-1.088c-4.671,-2.325 -7.888,-7.133 -7.888,-12.705c0,-3.488 1.263,-6.677 3.349,-9.148c1.025,-1.215 2.254,-2.245 3.628,-3.061c10.753,-8.412 24.288,-13.434 39,-13.434c34.997,0 63.368,28.371 63.368,63.368c-0.001,9.657 -2.176,18.8 -6.041,26.989z" id="svg_8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g display="none" id="svg_9">
|
||||
<path d="m283.545,24.007c-145.682,0 -263.781,118.099 -263.781,263.781s118.099,263.781 263.781,263.781s263.781,-118.099 263.781,-263.781s-118.099,-263.781 -263.781,-263.781zm139.455,375.302c0,7.493 -6.315,13.691 -13.808,13.691l-91.192,0l0,-35.34l0,-20.446l0,-2.871c0.038,-7.37 -6.111,-12.343 -15,-12.343s-39,0 -39,0c-3,-0.005 -2.241,0.161 -2.373,0.161c-7.435,0 -14.625,7.165 -14.625,14.6c0,0.127 -0.002,1.239 -0.002,1.239l0,19.66l0,35.34l-89.837,0l-0.494,0c-7.381,0 -13.669,-6.272 -13.669,-13.685l0,-121.53c0,-2.827 1.062,-5.443 2.568,-7.596c0.888,-1.27 2.164,-2.375 3.431,-3.265l124.012,-124.268l2.154,-2.157c2.255,-1.791 5.106,-2.867 8.209,-2.867c3.004,0 5.772,1.014 7.991,2.702l2.557,2.548l124.987,125.215c-0.054,0.045 -0.047,0.091 -0.101,0.136c2.505,2.415 4.191,5.799 4.191,9.554l0,121.522l0.001,0z" id="svg_10"/>
|
||||
</g>
|
||||
<g display="none" id="svg_11">
|
||||
<g display="inline" id="svg_12">
|
||||
<path d="m345.15,271.851c1.183,-1.297 2.547,-2.706 3.842,-4.247c1.421,-1.692 2.659,-3.545 3.417,-5.595c0.433,-1.119 0.713,-2.249 0.713,-3.433c0,-0.07 -0.121,-14.058 -0.121,-14.058l0,-40.854c0,-18.056 -14.646,-31.979 -31.875,-31.979c-2.232,0 -4.372,0.235 -6.66,0.724c-0.07,0.013 -0.098,0.032 -0.168,0.045c-9.206,1.965 -18.404,3.611 -27.845,3.643c-10.388,0.038 -20.532,-1.519 -30.667,-3.688c-2.282,-0.489 -4.222,-0.724 -6.453,-0.724c-17.229,0 -31.331,13.923 -31.331,31.979l0,4.447l0,29.426l0,8.558c0,3.935 -0.94,7.942 -0.673,11.865c0.097,1.434 -0.228,2.931 0.286,4.05c0.791,2.056 2.002,3.895 3.397,5.595c2.021,2.463 4.429,4.628 6.529,6.701c3.923,3.878 8.138,7.475 12.48,10.87c8.742,6.828 18.004,12.937 26.886,19.575c4.857,3.63 11.295,5.67 18.034,5.828c6.739,-0.159 13.176,-2.203 18.033,-5.833c9.734,-7.273 20.006,-13.637 29.409,-21.113c4.536,-3.607 8.857,-7.498 12.767,-11.782zm-85.15,-37.557c0,7.909 -7.091,14.317 -15,14.317c-7.909,0 -15,-6.408 -15,-14.317l0,-14.782c0,-7.909 7.091,-14.317 15,-14.317c7.909,0 15,6.408 15,14.317l0,14.782zm59.5,14.318c-7.909,0 -14.5,-6.408 -14.5,-14.317l0,-14.782c0,-7.909 6.591,-14.317 14.5,-14.317c7.909,0 14.5,6.408 14.5,14.317l0,14.782c0,7.908 -6.591,14.317 -14.5,14.317z" id="svg_13"/>
|
||||
<path d="m283.545,24.007c-145.682,0 -263.781,118.099 -263.781,263.781s118.099,263.781 263.781,263.781s263.781,-118.099 263.781,-263.781s-118.099,-263.781 -263.781,-263.781zm108.455,408.03c0,5.283 -4.98,7.963 -10.263,7.963l-103.555,0l-8.55,0l-82.179,0c-5.283,0 -10.453,-2.68 -10.453,-7.963l0,-37.968c0,-11.276 4.59,-23.322 12.77,-35.808c6.836,-10.433 16.918,-21.024 28.32,-30.633c7.486,-6.309 15.463,-11.977 23.502,-16.746c-0.944,-0.673 -1.889,-1.334 -2.831,-2.01c0.055,-0.032 0.106,-0.043 0.16,-0.074c-11.73,-8.489 -19.987,-15.692 -26.141,-21.834c-3.346,-3.388 -5.872,-6.221 -7.879,-9.237c-0.481,-0.51 -0.971,-1.029 -1.433,-1.559c-0.279,-0.321 -0.594,-0.643 -0.863,-0.971c-1.794,-2.187 -3.431,-4.554 -4.449,-7.199c-0.661,-1.441 -1.199,-3.366 -1.325,-5.212c-0.124,-1.817 -0.442,-3.648 -0.429,-5.483c0.024,-3.264 -0.402,-6.543 -0.402,-9.785l0,-18.16l0.549,0.01c0.01,-0.561 0.539,-1.144 0.457,-1.737c-0.82,-5.964 -5.862,-13.494 -7.177,-21.512c-0.104,-0.638 -0.188,-1.279 -0.242,-1.923l0.026,-0.01c-0.401,-4.131 -0.385,-8.308 0.145,-12.457c6.765,-52.927 46.886,-86.808 95.363,-86.808c47.53,0 87.052,32.316 94.942,83.737c0.787,5.139 0.773,10.342 0.025,15.428l0.347,0.109c-0.122,1.478 -0.237,2.94 -0.589,4.384c-1.724,7.058 -5.343,13.637 -6.311,18.973c-0.061,0.336 0.465,0.671 0.465,0.998l0,16.943c0,0 -0.605,6.586 -0.593,11.809c0.008,3.41 -0.606,6.241 -0.606,6.277c0,1.524 -0.42,2.977 -0.977,4.418c-0.975,2.637 -2.594,5.022 -4.423,7.199c-0.278,0.331 -0.559,0.65 -0.841,0.971c-0.657,0.749 -1.313,1.476 -1.963,2.176c-0.905,1.274 -1.887,2.489 -2.972,3.831c-5.301,6.006 -12.847,13.149 -24.136,21.791c-3.328,2.397 -6.672,4.742 -10.056,7.082c8.343,4.984 16.347,10.724 23.081,16.449c0.799,0.68 1.687,1.383 2.49,2.097c19.13,16.999 38.996,43.368 38.996,68.923l0,33.521z" id="svg_14"/>
|
||||
</g>
|
||||
</g>
|
||||
<g display="none" id="svg_15">
|
||||
<g display="inline" id="svg_16">
|
||||
<path d="m312.499,160.826c-9.98,2.131 -19.499,5.134 -28.499,8.871l0,0.309c0,-0.051 -0.324,-0.099 -0.449,-0.15c-13.49,5.7 -25.679,13.103 -35.916,21.847c6.85,0.617 12.365,6.309 12.365,13.318l0,13.871c0,7.422 -6.578,13.438 -14,13.438s-14,-6.017 -14,-13.438l0,-12.16c-5,7.508 -10,15.638 -14,24.243l0,10.724c0,11.767 40.019,38.51 46.415,42.736c1.735,0.958 3.005,1.504 3.005,1.504c4.505,2.922 10.252,4.531 16.122,4.582c0.101,-0.005 0.459,-0.014 0.459,-0.014l0,0.023c6,-0.006 13.014,-1.989 17.751,-5.526c0,0 47.249,-30.509 47.249,-43.305l0,-51.547c-0.001,-19.131 -17.791,-33.327 -36.502,-29.326zm15.501,58.072c0,7.422 -6.078,13.438 -13.5,13.438c-7.422,0 -13.5,-6.017 -13.5,-13.438l0,-13.871c0,-7.422 6.078,-13.438 13.5,-13.438c7.416,0 13.5,6.017 13.5,13.438l0,13.871z" id="svg_17"/>
|
||||
<path d="m283.545,24.007c-145.682,0 -263.781,118.099 -263.781,263.781s118.099,263.781 263.781,263.781s263.781,-118.099 263.781,-263.781s-118.099,-263.781 -263.781,-263.781zm105.455,311.424c-9,-3.568 -18.511,-5.577 -28.63,-5.577c-3.612,0 -7.204,0.26 -10.68,0.748c0.319,0.28 0.666,0.55 0.985,0.834c18.856,16.755 38.325,42.746 38.325,67.934l0,33.04c0,5.207 -4.685,8.59 -9.892,8.59l-102.07,0l-8.427,0l-81.001,0c-5.207,0 -9.609,-3.382 -9.609,-8.59l0,-37.423c0,-11.115 4.2,-22.991 12.263,-35.297c6.493,-9.909 15.656,-19.963 26.374,-29.156c-3.329,-0.447 -6.682,-0.68 -10.135,-0.68c-10.119,0 -19.502,2.01 -28.502,5.577l0,-124.877c0,-58.13 47.049,-105.257 105.179,-105.257c0.091,0 0.147,0.001 0.266,0.003c0.074,0 0.175,-0.003 0.249,-0.003c58.13,0 105.306,47.127 105.306,105.257l0,124.877l-0.001,0z" id="svg_18"/>
|
||||
</g>
|
||||
</g>
|
||||
<g display="none" id="svg_19">
|
||||
<g display="inline" id="svg_20">
|
||||
<path d="m349.599,245.278l-79.122,40.948c-0.082,0.043 -0.156,0.096 -0.237,0.141c-5.525,2.007 -10.592,5.215 -14.873,9.495c-15.891,15.892 -15.891,41.749 0,57.642c7.698,7.698 17.933,11.938 28.82,11.938s21.122,-4.24 28.82,-11.938c4.63,-4.63 7.901,-10.109 9.832,-15.938l40.798,-78.203c2.104,-4.033 1.351,-8.964 -1.86,-12.185c-3.209,-3.22 -8.138,-3.988 -12.178,-1.9z" id="svg_21"/>
|
||||
<path d="m283.545,24.007c-145.682,0 -263.781,118.099 -263.781,263.781s118.099,263.781 263.781,263.781s263.781,-118.099 263.781,-263.781s-118.099,-263.781 -263.781,-263.781zm157.069,312.993l15.415,0c-1.535,20 -6.792,40.567 -15.092,58.692c-0.006,0.018 -0.01,-0.01 -0.017,0.008c-0.088,0.193 -0.829,1.228 -0.961,1.506c-2.081,3.524 -5.906,4.794 -10.295,4.794l-24.507,0l-267.066,0c-4.166,0 -7.495,-1.264 -9.675,-5.366c-0.216,-0.455 -0.974,-1.57 -0.981,-1.59c-8.851,-19.329 -14.24,-40.044 -15.346,-64.044l15.672,0c5.76,0 10.428,-3.741 10.428,-9.5c0,-5.759 -4.669,-9.5 -10.428,-9.5l-15.463,0c2.802,-38 19.118,-75.51 44.351,-103.289l11.527,11.723c2.036,2.036 4.741,3.054 7.41,3.054c2.669,0 5.41,-1.018 7.446,-3.054c4.073,-4.073 4.217,-10.676 0.144,-14.749l-11.042,-11.331c28.262,-24.456 64.866,-39.891 104.866,-41.796l0,15.959c0,5.759 3.74,10.428 9.5,10.428s9.5,-4.669 9.5,-10.428l0,-15.707c39,2.862 76.041,19.222 103.786,44.5l-10.537,10.895c-4.073,4.073 -4.073,10.688 0,14.761c2.036,2.036 4.705,3.079 7.374,3.079c2.669,0 5.338,-0.969 7.374,-3.004l10.688,-10.588c24.41,28.294 39.792,62.547 41.637,104.547l-15.708,0c-5.76,0 -10.428,4.241 -10.428,10c0,5.759 4.668,10 10.428,10z" id="svg_22"/>
|
||||
</g>
|
||||
</g>
|
||||
<g display="none" id="svg_23">
|
||||
<g display="inline" id="svg_24">
|
||||
<path d="m343.258,200.171c-9.089,0.001 -18.289,2.508 -26.521,7.757c-23.007,14.673 -29.763,45.218 -15.09,68.225c9.423,14.775 25.389,22.847 41.704,22.847c9.089,0 18.29,-2.508 26.521,-7.757c23.007,-14.673 29.762,-45.218 15.09,-68.224c-9.423,-14.777 -25.389,-22.849 -41.704,-22.848zm34.669,57.087c-2.05,9.266 -7.655,17.181 -15.657,22.284c-5.724,3.651 -12.455,5.58 -19.199,5.58c-5.983,0 -11.071,-1.48 -17.071,-4.187l0,-40.485c0,-6.296 5.664,-12.45 11.959,-12.45l34.328,0c0.332,2 0.629,1.465 0.94,1.954c5.103,8.001 6.75,18.037 4.7,27.304z" id="svg_25"/>
|
||||
<path d="m283.545,24.007c-145.682,0 -263.781,118.099 -263.781,263.781s118.099,263.781 263.781,263.781s263.781,-118.099 263.781,-263.781s-118.099,-263.781 -263.781,-263.781zm-78.545,383.993l-82,0l0,-77.35c0,-6.296 5.466,-10.65 11.762,-10.65l59.472,0c6.295,0 10.766,4.355 10.766,10.65l0,77.35zm104,0l-84,0l0,-228.013c0,-6.296 4.569,-10.987 10.865,-10.987l54.982,0c-21.431,13 -35.968,35.007 -41.526,59.974c-5.613,25.215 -0.753,50.864 13.108,72.597c11.19,17.547 25.571,30.66 46.571,38.006l0,68.423zm17,0l0,-63.149c6,0.938 11.241,1.433 16.863,1.433c4.727,0 9.725,-0.35 14.392,-1.044l37.24,58.161c1.098,1.722 2.327,3.599 3.647,4.599l-72.142,0zm115.213,-4.086c-4.226,2.695 -8.946,3.982 -13.613,3.982c-8.373,0 -16.569,-4.144 -21.405,-11.727l-42.358,-66.416c-6.831,1.782 -13.783,2.653 -20.694,2.653c-27.275,0 -53.884,-13.574 -69.652,-38.297c-24.541,-38.48 -13.102,-89.658 25.378,-114.199c13.747,-8.767 29.081,-12.948 44.238,-12.948c27.271,0 53.959,13.542 69.733,38.275c19.805,31.054 16.076,70.435 -6.202,97.302l42.32,66.357c7.531,11.809 4.063,27.488 -7.745,35.018z" id="svg_26"/>
|
||||
</g>
|
||||
</g>
|
||||
<g display="none" id="svg_27">
|
||||
<g display="inline" id="svg_28">
|
||||
<path d="m283.545,24.007c-145.682,0 -263.781,118.099 -263.781,263.781s118.099,263.781 263.781,263.781s263.781,-118.099 263.781,-263.781s-118.099,-263.781 -263.781,-263.781zm179.255,300.845c-1.873,8.407 -10.159,13.275 -18.415,10.817l-22.428,-6.677c-2.855,9.611 -6.676,18.805 -11.367,27.462l20.632,11.166c7.575,4.1 9.987,13.398 5.36,20.662c0,0 -10.312,16.191 -23.125,29.004c-12.869,12.869 -29.193,23.244 -29.193,23.244c-7.269,4.621 -16.571,2.203 -20.67,-5.372l-11.112,-20.532c-8.656,4.702 -17.851,8.533 -27.463,11.398l6.674,22.419c2.458,8.255 -2.411,16.535 -10.82,18.401c0,0 -18.741,4.157 -36.861,4.157c-18.199,0 -37.079,-4.206 -37.079,-4.206c-8.407,-1.873 -13.275,-10.16 -10.817,-18.415l6.637,-22.292c-9.623,-2.852 -18.828,-6.674 -27.495,-11.366l-11.091,20.495c-4.1,7.575 -13.398,9.987 -20.662,5.36c0,0 -16.191,-10.312 -29.004,-23.126c-12.869,-12.869 -23.245,-29.193 -23.245,-29.193c-4.62,-7.269 -2.203,-16.57 5.372,-20.67l20.393,-11.036c-4.704,-8.666 -8.536,-17.873 -11.4,-27.496l-22.278,6.632c-8.255,2.458 -16.535,-2.412 -18.4,-10.82c0,0 -4.157,-18.74 -4.157,-36.861c0,-18.199 4.206,-37.079 4.206,-37.079c1.873,-8.407 10.159,-13.275 18.415,-10.817l22.212,6.613c2.863,-9.624 6.695,-18.831 11.399,-27.497l-20.448,-11.066c-7.575,-4.1 -9.987,-13.398 -5.36,-20.662c0,0 10.312,-16.191 23.125,-29.004c12.869,-12.869 29.193,-23.245 29.193,-23.245c7.269,-4.62 16.571,-2.203 20.67,5.372l11.057,20.431c8.667,-4.693 17.874,-8.515 27.497,-11.367l-6.653,-22.348c-2.458,-8.255 2.412,-16.535 10.82,-18.401c0,0 18.741,-4.157 36.861,-4.157c18.199,0 37.079,4.206 37.079,4.206c8.407,1.873 13.275,10.16 10.817,18.415l-6.653,22.348c9.612,2.866 18.808,6.697 27.464,11.4l11.14,-20.585c4.1,-7.575 13.398,-9.987 20.662,-5.36c0,0 16.191,10.312 29.004,23.126c12.869,12.869 23.244,29.193 23.244,29.193c4.62,7.269 2.203,16.57 -5.372,20.67l-20.571,11.133c4.69,8.657 8.512,17.852 11.366,27.463l22.49,-6.695c8.255,-2.458 16.535,2.412 18.4,10.82c0,0 4.157,18.74 4.157,36.861c-0.001,18.197 -4.207,37.077 -4.207,37.077z" id="svg_29"/>
|
||||
<circle cx="283.779" cy="287.886" r="37.594" id="svg_30"/>
|
||||
</g>
|
||||
</g>
|
||||
<g display="none" id="svg_31">
|
||||
<g display="inline" id="svg_32">
|
||||
<rect x="326.113" y="201.398" transform="matrix(-0.7071,-0.7071,0.7071,-0.7071,472.8639,611.4978) " width="73.929" height="12.835" id="svg_33"/>
|
||||
<polygon points="170.708,383.917 187.339,400.547 225.134,390.423 180.832,346.122 " id="svg_34"/>
|
||||
<path d="m413.02,192.538c-0.016,-0.268 -0.051,-0.534 -0.072,-0.801c-0.049,-0.602 -0.095,-1.205 -0.173,-1.804c-0.037,-0.284 -0.094,-0.565 -0.138,-0.848c-0.089,-0.58 -0.174,-1.161 -0.291,-1.737c-0.062,-0.305 -0.146,-0.606 -0.215,-0.909c-0.126,-0.55 -0.246,-1.102 -0.397,-1.647c-0.09,-0.325 -0.205,-0.645 -0.304,-0.968c-0.159,-0.518 -0.31,-1.037 -0.492,-1.548c-0.123,-0.346 -0.272,-0.685 -0.406,-1.028c-0.188,-0.48 -0.365,-0.964 -0.574,-1.438c-0.162,-0.367 -0.35,-0.724 -0.525,-1.087c-0.211,-0.44 -0.41,-0.885 -0.64,-1.318c-0.206,-0.388 -0.441,-0.764 -0.661,-1.146c-0.229,-0.396 -0.444,-0.798 -0.688,-1.187c-0.259,-0.413 -0.549,-0.81 -0.826,-1.214c-0.237,-0.345 -0.458,-0.698 -0.708,-1.037c-0.344,-0.466 -0.721,-0.915 -1.089,-1.368c-0.213,-0.262 -0.409,-0.532 -0.63,-0.789c-0.604,-0.703 -1.239,-1.388 -1.905,-2.054l-0.004,0.004c-0.028,-0.03 -0.053,-0.062 -0.083,-0.091c-1.556,-1.556 -4.079,-1.556 -5.634,0c-1.556,1.556 -1.556,4.078 0,5.634c0.042,0.042 0.086,0.078 0.129,0.117c0.172,0.173 0.327,0.356 0.494,0.532c7.992,8.41 9.548,19.621 6.063,30.056c-0.915,1.556 -0.709,3.588 0.626,4.924c1.585,1.585 4.154,1.585 5.739,0c0.537,-0.537 0.888,-1.188 1.06,-1.874c0.08,-0.212 0.141,-0.429 0.218,-0.642c0.211,-0.588 0.421,-1.175 0.601,-1.771c0.072,-0.238 0.128,-0.48 0.195,-0.719c0.17,-0.607 0.339,-1.215 0.478,-1.829c0.054,-0.239 0.091,-0.48 0.141,-0.719c0.127,-0.619 0.253,-1.238 0.348,-1.862c0.036,-0.239 0.056,-0.479 0.088,-0.719c0.083,-0.627 0.166,-1.254 0.217,-1.884c0.02,-0.243 0.022,-0.487 0.037,-0.731c0.039,-0.627 0.077,-1.254 0.084,-1.882c0.003,-0.255 -0.012,-0.509 -0.015,-0.764c-0.007,-0.618 -0.012,-1.236 -0.048,-1.852z" id="svg_35"/>
|
||||
<path d="m283.545,24.007c-145.682,0 -263.781,118.099 -263.781,263.781s118.099,263.781 263.781,263.781s263.781,-118.099 263.781,-263.781s-118.099,-263.781 -263.781,-263.781zm129.63,207.768l-3.086,3.086l-19.966,19.966l-143.453,143.453c-1.009,1.736 -2.674,3.036 -4.666,3.569l-54.971,14.725c-0.002,0 -0.003,0.001 -0.004,0.001l-31.304,8.386c-2.657,0.712 -5.493,-0.048 -7.438,-1.993c-1.945,-1.945 -2.705,-4.781 -1.993,-7.438l8.386,-31.304l14.292,-53.352c0.046,-1.907 0.791,-3.801 2.247,-5.257l144.847,-144.847l19.966,-19.966l3.086,-3.086c20.418,-20.418 53.64,-20.418 74.057,0c20.417,20.418 20.417,53.639 0,74.057z" id="svg_36"/>
|
||||
</g>
|
||||
</g>
|
||||
<g display="none" id="svg_37">
|
||||
<path d="m283.545,24.007c-145.682,0 -263.781,118.099 -263.781,263.781s118.099,263.781 263.781,263.781s263.781,-118.099 263.781,-263.781s-118.099,-263.781 -263.781,-263.781zm-62.545,379.174c0,11.498 -9.321,20.819 -20.819,20.819l-29.363,0c-11.497,0 -20.818,-9.321 -20.818,-20.819l0,-29.363c0,-11.498 9.321,-20.819 20.819,-20.819l29.363,0c11.498,0 20.819,9.321 20.819,20.819l0,29.363l-0.001,0zm0,-100c0,11.498 -9.321,20.819 -20.819,20.819l-29.363,0c-11.497,0 -20.818,-9.321 -20.818,-20.819l0,-29.363c0,-11.498 9.321,-20.819 20.819,-20.819l29.363,0c11.498,0 20.819,9.321 20.819,20.819l0,29.363l-0.001,0zm0,-102c0,11.498 -9.321,20.819 -20.819,20.819l-29.363,0c-11.497,0 -20.818,-9.321 -20.818,-20.819l0,-29.363c0,-11.498 9.321,-20.819 20.819,-20.819l29.363,0c11.498,0 20.819,9.321 20.819,20.819l0,29.363l-0.001,0zm212,186.032c0,11.48 -9.307,20.787 -20.787,20.787l-138.426,0c-11.48,0 -20.787,-9.307 -20.787,-20.787l0,-0.425c0,-11.481 9.307,-20.787 20.787,-20.787l138.425,0c11.481,0 20.787,9.307 20.787,20.787l0,0.425l0.001,0zm0,-98c0,11.48 -9.307,20.787 -20.787,20.787l-138.426,0c-11.48,0 -20.787,-9.307 -20.787,-20.787l0,-0.425c0,-11.481 9.307,-20.787 20.787,-20.787l138.425,0c11.481,0 20.787,9.307 20.787,20.787l0,0.425l0.001,0zm0,-101c0,11.48 -9.307,20.787 -20.787,20.787l-138.426,0c-11.48,0 -20.787,-9.307 -20.787,-20.787l0,-0.425c0,-11.481 9.307,-20.787 20.787,-20.787l138.425,0c11.481,0 20.787,9.307 20.787,20.787l0,0.425l0.001,0z" id="svg_38"/>
|
||||
</g>
|
||||
<g display="none" id="svg_39">
|
||||
<g display="inline" id="svg_40">
|
||||
<rect x="187" y="256" width="192" height="125" id="svg_41"/>
|
||||
<path d="m283.545,24.007c-145.682,0 -263.781,118.099 -263.781,263.781s118.099,263.781 263.781,263.781s263.781,-118.099 263.781,-263.781s-118.099,-263.781 -263.781,-263.781zm39.455,133.723l0,-19.607c0,-11.106 9.394,-20.109 20.5,-20.109s20.5,9.003 20.5,20.109l0,19.607l0,20.612c0,11.106 -9.394,20.109 -20.5,20.109s-20.5,-9.003 -20.5,-20.109l0,-20.612zm-119,0l0,-19.607c0,-11.106 8.394,-20.109 19.5,-20.109s19.5,9.003 19.5,20.109l0,19.607l0,20.612c0,11.106 -8.394,20.109 -19.5,20.109s-19.5,-9.003 -19.5,-20.109l0,-20.612zm217,242.478c0,11.019 -9.318,19.792 -20.337,19.792l-233.587,0c-11.018,0 -21.076,-8.773 -21.076,-19.792l0,-222.527c0,-11.019 10.058,-18.681 21.076,-18.681l19.924,0l0,19.342c0,19.959 17.041,36.197 37,36.197s37,-16.238 37,-36.197l0,-19.342l47,0l0,19.342c0,19.959 15.541,36.197 35.5,36.197s35.5,-16.238 35.5,-36.197l0,-19.342l21.663,0c11.019,0 20.337,7.662 20.337,18.681l0,222.527z" id="svg_42"/>
|
||||
</g>
|
||||
</g>
|
||||
<g display="none" id="svg_43">
|
||||
<g display="inline" id="svg_44">
|
||||
<path d="m348,411.767c7.039,0 12,-5.706 12,-12.745l0,-175.1c0,-7.039 -4.961,-12.745 -12,-12.745s-12,5.706 -12,12.745l0,175.1c0,7.038 4.961,12.745 12,12.745z" id="svg_45"/>
|
||||
<path d="m222,411.767c7.039,0 14,-5.706 14,-12.745l0,-175.1c0,-7.039 -6.961,-12.745 -14,-12.745s-14,5.706 -14,12.745l0,175.1c0,7.038 6.961,12.745 14,12.745z" id="svg_46"/>
|
||||
<path d="m283.545,24.007c-145.682,0 -263.781,118.099 -263.781,263.781s118.099,263.781 263.781,263.781s263.781,-118.099 263.781,-263.781s-118.099,-263.781 -263.781,-263.781zm109.455,185.128l0,212.78c0,12.095 -10.073,23.085 -22.167,23.085l-173.233,0c-12.094,0 -21.6,-10.99 -21.6,-23.085l0,-212.78l0,-32.135l217,0l0,32.135zm0,-47.135l-217,0l0,-10.841c0,-12.095 9.505,-21.159 21.6,-21.159l55.634,0c0.497,-12 10.163,-21 21.942,-21l18.081,0c11.78,0 21.445,9 21.942,21l55.634,0c12.094,0 22.167,9.064 22.167,21.159l0,10.841z" id="svg_47"/>
|
||||
<path d="m284,411.767c7.039,0 13,-5.706 13,-12.745l0,-175.1c0,-7.039 -5.961,-12.745 -13,-12.745s-13,5.706 -13,12.745l0,175.1c0,7.038 5.961,12.745 13,12.745z" id="svg_48"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="svg_49">
|
||||
<g id="svg_50">
|
||||
<polygon points="196.661,391.813 223.84,340.524 381.46,229.666 196.625,326.192 " id="svg_51" fill="#03a9f4"/>
|
||||
<path d="m283.545,24.007c-145.682,0 -263.781,118.099 -263.781,263.781s118.099,263.781 263.781,263.781s263.781,-118.099 263.781,-263.781s-118.099,-263.781 -263.781,-263.781zm160.376,182.583l-117.664,174.425c-3.558,5.273 -10.504,7.04 -16.15,4.11l-47.144,-24.478l-64.897,56.276c-2.322,2.013 -5.247,3.062 -8.206,3.062c-1.752,0 -3.516,-0.368 -5.173,-1.119c-4.456,-2.023 -7.325,-6.456 -7.345,-11.349l-0.386,-91.883l-70.236,-36.656c-4.681,-2.443 -7.327,-7.562 -6.612,-12.794c0.715,-5.233 4.635,-9.454 9.801,-10.553l321.028,-68.291c4.992,-1.061 10.13,1.012 12.985,5.245c2.854,4.232 2.854,9.773 -0.001,14.005z" id="svg_52" fill="#03a9f4"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 21 KiB |
@@ -1,29 +1,44 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:400,700" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css?family=Overpass+Mono:700" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
width: 100vw;
|
||||
}
|
||||
h1 {
|
||||
display: inline-block;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-self: center;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
margin-left: -4px;
|
||||
padding: 0px;
|
||||
width: 75vw;
|
||||
font-size: 68px;
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
font-family: 'Overpass Mono', monospace;
|
||||
font-weight: bold;
|
||||
line-height: 0.8em;
|
||||
letter-spacing: -3px;
|
||||
color: #333;
|
||||
color: #fff;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-webkit-text-stroke: 1px #0063B1;
|
||||
text-shadow:
|
||||
1px 0px 0px #ccc, 0px 1px 0px #eee,
|
||||
2px 1px 0px #ccc, 1px 2px 0px #eee,
|
||||
3px 2px 0px #ccc, 2px 3px 0px #eee,
|
||||
4px 3px 0px #ccc;
|
||||
3px 3px 0 #0063B1,
|
||||
-1px -1px 0 #0063B1,
|
||||
1px -1px 0 #0063B1,
|
||||
-1px 1px 0 #0063B1,
|
||||
1px 1px 0 #0063B1;
|
||||
}
|
||||
h1 > span {
|
||||
display: block;
|
||||
@@ -31,17 +46,23 @@
|
||||
margin: 0;
|
||||
padding-top: 6px;
|
||||
}
|
||||
img {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
h1 > hr {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
margin-top: 22px;
|
||||
border: 1px solid #0063B1;
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>
|
||||
<span>ftp-srv</span>
|
||||
</h1>
|
||||
<img src="icon.svg" width="76px" height="76px" />
|
||||
<div>
|
||||
<h1>
|
||||
<span>ftp</span>
|
||||
<hr />
|
||||
<span>srv</span>
|
||||
</h1>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
5567
package-lock.json
generated
5567
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
58
package.json
58
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ftp-srv",
|
||||
"version": "0.0.0",
|
||||
"version": "0.0.0-development",
|
||||
"description": "Modern, extensible FTP Server",
|
||||
"keywords": [
|
||||
"ftp",
|
||||
@@ -8,14 +8,16 @@
|
||||
"ftp-srv",
|
||||
"ftp-svr",
|
||||
"ftpd",
|
||||
"server",
|
||||
"ftpserver"
|
||||
"ftpserver",
|
||||
"server"
|
||||
],
|
||||
"license": "MIT",
|
||||
"main": "ftp-srv.js",
|
||||
"files": [
|
||||
"src"
|
||||
"src",
|
||||
"ftp-srv.d.ts"
|
||||
],
|
||||
"main": "ftp-srv.js",
|
||||
"types": "./ftp-srv.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/trs/ftp-srv"
|
||||
@@ -27,20 +29,23 @@
|
||||
"commitmsg": "cz-customizable-ghooks",
|
||||
"dev": "cross-env NODE_ENV=development npm run verify:watch",
|
||||
"prepush": "npm-run-all verify test:coverage --silent",
|
||||
"semantic-release": "semantic-release pre && npm publish && semantic-release post",
|
||||
"semantic-release": "semantic-release",
|
||||
"start": "npm run dev",
|
||||
"test": "npm run test:unit",
|
||||
"test:check-coverage": "cross-env NODE_ENV=test istanbul check-coverage reports/coverage/coverage.json --config config/testUnit/istanbul.js",
|
||||
"test:check-coverage": "nyc check-coverage",
|
||||
"test:coverage": "npm-run-all test:unit:once test:check-coverage --silent",
|
||||
"test:unit": "chokidar 'src/**/*.js' 'test/**/*.js' -c 'npm run test:unit:once' --initial --silent",
|
||||
"test:unit:once": "cross-env NODE_ENV=test istanbul cover --config config/testUnit/istanbul.js _mocha -- --opts config/testUnit/mocha.opts",
|
||||
"upload-coverage": "cat reports/coverage/lcov/lcov.info | ./node_modules/coveralls/bin/coveralls.js",
|
||||
"test:unit": "cross-env NODE_ENV=test nyc mocha --opts config/testUnit/mocha.opts -w",
|
||||
"test:unit:once": "cross-env NODE_ENV=test nyc mocha --opts config/testUnit/mocha.opts",
|
||||
"upload-coverage": "cat reports/coverage/lcov.info | coveralls",
|
||||
"verify": "npm run verify:js --silent",
|
||||
"verify:js": "eslint -c config/verify/.eslintrc \"src/**/*.js\" \"test/**/*.js\" \"config/**/*.js\" && echo ✅ verify:js success",
|
||||
"verify:js:fix": "eslint --fix -c config/verify/.eslintrc \"src/**/*.js\" \"test/**/*.js\" \"config/**/*.js\" && echo ✅ verify:js:fix success",
|
||||
"verify:js:watch": "chokidar 'src/**/*.js' 'test/**/*.js' 'config/**/*.js' -c 'npm run verify:js:fix' --initial --silent",
|
||||
"verify:watch": "npm run verify:js:watch --silent"
|
||||
},
|
||||
"release": {
|
||||
"verifyConditions": "condition-circle"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "node_modules/cz-customizable"
|
||||
@@ -50,33 +55,38 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"bunyan": "^1.8.10",
|
||||
"bluebird": "^3.5.1",
|
||||
"bunyan": "^1.8.12",
|
||||
"lodash": "^4.17.4",
|
||||
"moment": "^2.18.1",
|
||||
"uuid": "^3.0.1",
|
||||
"when": "^3.7.8"
|
||||
"moment": "^2.19.1",
|
||||
"uuid": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@icetee/ftp": "^0.3.15",
|
||||
"chai": "^4.0.2",
|
||||
"chokidar-cli": "1.2.0",
|
||||
"condition-circle": "^1.6.0",
|
||||
"coveralls": "2.13.1",
|
||||
"cross-env": "5.0.1",
|
||||
"cz-customizable": "5.0.0",
|
||||
"cross-env": "3.1.4",
|
||||
"cz-customizable": "5.2.0",
|
||||
"cz-customizable-ghooks": "1.5.0",
|
||||
"dotenv": "^4.0.0",
|
||||
"eslint": "3.19.0",
|
||||
"eslint": "4.5.0",
|
||||
"eslint-config-google": "0.8.0",
|
||||
"eslint-plugin-node": "5.0.0",
|
||||
"ftp": "^0.3.10",
|
||||
"html-convert": "^2.1.7",
|
||||
"husky": "0.13.4",
|
||||
"eslint-friendly-formatter": "3.0.0",
|
||||
"eslint-plugin-mocha": "^4.11.0",
|
||||
"eslint-plugin-node": "5.1.1",
|
||||
"husky": "0.13.3",
|
||||
"istanbul": "0.4.5",
|
||||
"mocha": "3.4.2",
|
||||
"mocha": "3.5.0",
|
||||
"mocha-junit-reporter": "1.13.0",
|
||||
"mocha-multi-reporters": "1.1.5",
|
||||
"mocha-pretty-bunyan-nyan": "^1.0.4",
|
||||
"npm-run-all": "4.0.2",
|
||||
"nyc": "11.1.0",
|
||||
"rimraf": "2.6.1",
|
||||
"semantic-release": "^6.3.6",
|
||||
"sinon": "^2.3.2"
|
||||
"semantic-release": "^11.0.2",
|
||||
"sinon": "^2.3.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.x",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const _ = require('lodash');
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
|
||||
const REGISTRY = require('./registry');
|
||||
|
||||
@@ -12,10 +12,18 @@ class FtpCommands {
|
||||
}
|
||||
|
||||
parse(message) {
|
||||
const [directive, ...args] = message.replace(/"/g, '').split(' ');
|
||||
const strippedMessage = message.replace(/"/g, '');
|
||||
const [directive, ...args] = strippedMessage.split(' ');
|
||||
const params = args.reduce(({arg, flags}, param) => {
|
||||
if (/^-{1,2}[a-zA-Z0-9_]+/.test(param)) flags.push(param);
|
||||
else arg.push(param);
|
||||
return {arg, flags};
|
||||
}, {arg: [], flags: []});
|
||||
|
||||
const command = {
|
||||
directive: _.chain(directive).trim().toUpper().value(),
|
||||
arg: _.compact(args).join(' ') || null,
|
||||
arg: params.arg.length ? params.arg.join(' ') : null,
|
||||
flags: params.flags,
|
||||
raw: message
|
||||
};
|
||||
return command;
|
||||
@@ -54,7 +62,7 @@ class FtpCommands {
|
||||
}
|
||||
|
||||
const handler = commandRegister.handler.bind(this.connection);
|
||||
return when.try(handler, { log, command, previous_command: this.previousCommand })
|
||||
return Promise.resolve(handler({log, command, previous_command: this.previousCommand}))
|
||||
.finally(() => {
|
||||
this.previousCommand = _.clone(command);
|
||||
});
|
||||
|
||||
@@ -4,10 +4,10 @@ module.exports = {
|
||||
return this.connector.waitForConnection()
|
||||
.then(socket => {
|
||||
return this.reply(426, {socket})
|
||||
.then(() => this.connector.end());
|
||||
.then(() => this.connector.end())
|
||||
.then(() => this.reply(226));
|
||||
})
|
||||
.catch(() => {})
|
||||
.then(() => this.reply(226));
|
||||
.catch(() => this.reply(225));
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Abort an active file transfer'
|
||||
|
||||
@@ -20,7 +20,8 @@ module.exports = {
|
||||
};
|
||||
|
||||
function handleTLS() {
|
||||
if (!this.server._tls) return this.reply(504);
|
||||
if (!this.server._tls) return this.reply(502);
|
||||
if (this.secure) return this.reply(202);
|
||||
|
||||
return this.reply(234)
|
||||
.then(() => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const escapePath = require('../../helpers/escape-path');
|
||||
|
||||
module.exports = {
|
||||
@@ -7,7 +7,7 @@ module.exports = {
|
||||
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 when.try(this.fs.chdir.bind(this.fs), command.arg)
|
||||
return Promise.resolve(this.fs.chdir(command.arg))
|
||||
.then(cwd => {
|
||||
const path = cwd ? `"${escapePath(cwd)}"` : undefined;
|
||||
return this.reply(250, path);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
|
||||
module.exports = {
|
||||
directive: 'DELE',
|
||||
@@ -6,7 +6,7 @@ module.exports = {
|
||||
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 when.try(this.fs.delete.bind(this.fs), command.arg)
|
||||
return Promise.resolve(this.fs.delete(command.arg))
|
||||
.then(() => {
|
||||
return this.reply(250);
|
||||
})
|
||||
|
||||
@@ -9,11 +9,11 @@ const FAMILY = {
|
||||
module.exports = {
|
||||
directive: 'EPRT',
|
||||
handler: function ({command} = {}) {
|
||||
this.connector = new ActiveConnector(this);
|
||||
const [protocol, ip, port] = _.compact(command.arg.split('|'));
|
||||
const [, protocol, ip, port] = _.chain(command).get('arg', '').split('|').value();
|
||||
const family = FAMILY[protocol];
|
||||
if (!family) return this.reply(502, 'Unknown network 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));
|
||||
},
|
||||
|
||||
@@ -9,8 +9,12 @@ module.exports = {
|
||||
const feat = _.get(registry[cmd], 'flags.feat', null);
|
||||
if (feat) return _.concat(feats, feat);
|
||||
return feats;
|
||||
}, [])
|
||||
.map(feat => ` ${feat}`);
|
||||
}, ['UTF8'])
|
||||
.sort()
|
||||
.map(feat => ({
|
||||
message: ` ${feat}`,
|
||||
raw: true
|
||||
}));
|
||||
return features.length
|
||||
? this.reply(211, 'Extensions supported', ...features, 'End')
|
||||
: this.reply(211, 'No features');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const _ = require('lodash');
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const getFileStat = require('../../helpers/file-stat');
|
||||
|
||||
// http://cr.yp.to/ftp/list.html
|
||||
@@ -13,15 +13,11 @@ module.exports = {
|
||||
|
||||
const simple = command.directive === 'NLST';
|
||||
|
||||
let dataSocket;
|
||||
const path = command.arg || '.';
|
||||
return this.connector.waitForConnection()
|
||||
.then(socket => {
|
||||
this.commandSocket.pause();
|
||||
dataSocket = socket;
|
||||
})
|
||||
.then(() => when.try(this.fs.get.bind(this.fs), path))
|
||||
.then(stat => stat.isDirectory() ? when.try(this.fs.list.bind(this.fs), path) : [stat])
|
||||
.tap(() => this.commandSocket.pause())
|
||||
.then(() => Promise.resolve(this.fs.get(path)))
|
||||
.then(stat => stat.isDirectory() ? Promise.resolve(this.fs.list(path)) : [stat])
|
||||
.then(files => {
|
||||
const getFileMessage = file => {
|
||||
if (simple) return file.name;
|
||||
@@ -33,7 +29,7 @@ module.exports = {
|
||||
return {
|
||||
raw: true,
|
||||
message,
|
||||
socket: dataSocket
|
||||
socket: this.connector.socket
|
||||
};
|
||||
});
|
||||
return this.reply(150)
|
||||
@@ -41,10 +37,8 @@ module.exports = {
|
||||
if (fileList.length) return this.reply({}, ...fileList);
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return this.reply(226, 'Transfer OK');
|
||||
})
|
||||
.catch(when.TimeoutError, err => {
|
||||
.then(() => this.reply(226))
|
||||
.catch(Promise.TimeoutError, err => {
|
||||
log.error(err);
|
||||
return this.reply(425, 'No connection established');
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const moment = require('moment');
|
||||
|
||||
module.exports = {
|
||||
@@ -7,7 +7,7 @@ module.exports = {
|
||||
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 when.try(this.fs.get.bind(this.fs), command.arg)
|
||||
return Promise.resolve(this.fs.get(command.arg))
|
||||
.then(fileStat => {
|
||||
const modificationTime = moment.utc(fileStat.mtime).format('YYYYMMDDHHmmss.SSS');
|
||||
return this.reply(213, modificationTime);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const escapePath = require('../../helpers/escape-path');
|
||||
|
||||
module.exports = {
|
||||
@@ -7,7 +7,7 @@ module.exports = {
|
||||
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 when.try(this.fs.mkdir.bind(this.fs), command.arg)
|
||||
return Promise.resolve(this.fs.mkdir(command.arg))
|
||||
.then(dir => {
|
||||
const path = dir ? `"${escapePath(dir)}"` : undefined;
|
||||
return this.reply(257, path);
|
||||
|
||||
@@ -1,8 +1,39 @@
|
||||
const _ = require('lodash');
|
||||
|
||||
const OPTIONS = {
|
||||
UTF8: utf8,
|
||||
'UTF-8': utf8
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
directive: 'OPTS',
|
||||
handler: function () {
|
||||
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);
|
||||
|
||||
if (!OPTIONS.hasOwnProperty(option)) return this.reply(500);
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
const encoding = getEncoding();
|
||||
if (!encoding) return this.reply(501, 'Unknown setting for option');
|
||||
|
||||
this.encoding = encoding;
|
||||
if (this.transferType !== 'binary') this.transferType = this.encoding;
|
||||
|
||||
return this.reply(200, `UTF8 encoding ${_.toLower(setting)}`);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ module.exports = {
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Protection Buffer Size',
|
||||
flags: {
|
||||
no_auth: true
|
||||
no_auth: true,
|
||||
feat: 'PBSZ'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@ module.exports = {
|
||||
directive: 'PORT',
|
||||
handler: function ({command} = {}) {
|
||||
this.connector = new ActiveConnector(this);
|
||||
|
||||
const rawConnection = _.get(command, 'arg', '').split(',');
|
||||
if (rawConnection.length !== 6) return this.reply(425);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ module.exports = {
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Data Channel Protection Level',
|
||||
flags: {
|
||||
no_auth: true
|
||||
no_auth: true,
|
||||
feat: 'PROT'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const escapePath = require('../../helpers/escape-path');
|
||||
|
||||
module.exports = {
|
||||
@@ -7,7 +7,7 @@ module.exports = {
|
||||
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 when.try(this.fs.currentDirectory.bind(this.fs))
|
||||
return Promise.resolve(this.fs.currentDirectory())
|
||||
.then(cwd => {
|
||||
const path = cwd ? `"${escapePath(cwd)}"` : undefined;
|
||||
return this.reply(257, path);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module.exports = {
|
||||
directive: 'QUIT',
|
||||
handler: function () {
|
||||
return this.close(221);
|
||||
return this.close(221, 'Client called QUIT');
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Disconnect',
|
||||
|
||||
16
src/commands/registration/rest.js
Normal file
16
src/commands/registration/rest.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const _ = require('lodash');
|
||||
|
||||
module.exports = {
|
||||
directive: 'REST',
|
||||
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');
|
||||
|
||||
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,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
|
||||
module.exports = {
|
||||
directive: 'RETR',
|
||||
@@ -6,29 +6,45 @@ module.exports = {
|
||||
if (!this.fs) return this.reply(550, 'File system not instantiated');
|
||||
if (!this.fs.read) return this.reply(402, 'Not supported by file system');
|
||||
|
||||
let dataSocket;
|
||||
return this.connector.waitForConnection()
|
||||
.then(socket => {
|
||||
this.commandSocket.pause();
|
||||
dataSocket = socket;
|
||||
})
|
||||
.then(() => when.try(this.fs.read.bind(this.fs), command.arg))
|
||||
.then(stream => {
|
||||
return when.promise((resolve, reject) => {
|
||||
dataSocket.on('error', err => stream.emit('error', err));
|
||||
const filePath = command.arg;
|
||||
|
||||
stream.on('data', data => dataSocket.write(data, this.encoding));
|
||||
stream.on('end', () => resolve(this.reply(226)));
|
||||
stream.on('error', err => reject(err));
|
||||
this.reply(150).then(() => dataSocket.resume());
|
||||
return this.connector.waitForConnection()
|
||||
.tap(() => this.commandSocket.pause())
|
||||
.then(() => Promise.resolve(this.fs.read(filePath, {start: this.restByteCount})))
|
||||
.then(stream => {
|
||||
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, this.transferType, () => stream && stream.resume());
|
||||
}
|
||||
});
|
||||
stream.once('end', () => resolve());
|
||||
stream.once('error', destroyConnection(this.connector.socket, reject));
|
||||
|
||||
this.connector.socket.once('error', destroyConnection(stream, reject));
|
||||
});
|
||||
|
||||
this.restByteCount = 0;
|
||||
|
||||
return this.reply(150).then(() => stream.resume() && this.connector.socket.resume())
|
||||
.then(() => eventsPromise)
|
||||
.tap(() => this.emit('RETR', null, filePath))
|
||||
.finally(() => stream.destroy && stream.destroy());
|
||||
})
|
||||
.catch(when.TimeoutError, err => {
|
||||
.then(() => this.reply(226))
|
||||
.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);
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const dele = require('./dele').handler;
|
||||
const {handler: dele} = require('./dele');
|
||||
|
||||
module.exports = {
|
||||
directive: ['RMD', 'XRMD'],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
|
||||
module.exports = {
|
||||
directive: 'RNFR',
|
||||
@@ -7,7 +7,7 @@ module.exports = {
|
||||
if (!this.fs.get) return this.reply(402, 'Not supported by file system');
|
||||
|
||||
const fileName = command.arg;
|
||||
return when.try(this.fs.get.bind(this.fs), fileName)
|
||||
return Promise.resolve(this.fs.get(fileName))
|
||||
.then(() => {
|
||||
this.renameFrom = fileName;
|
||||
return this.reply(350);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
|
||||
module.exports = {
|
||||
directive: 'RNTO',
|
||||
@@ -11,7 +11,7 @@ module.exports = {
|
||||
const from = this.renameFrom;
|
||||
const to = command.arg;
|
||||
|
||||
return when.try(this.fs.rename.bind(this.fs), from, to)
|
||||
return Promise.resolve(this.fs.rename(from, to))
|
||||
.then(() => {
|
||||
return this.reply(250);
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
|
||||
module.exports = function ({log, command} = {}) {
|
||||
if (!this.fs) return this.reply(550, 'File system not instantiated');
|
||||
@@ -6,7 +6,7 @@ module.exports = function ({log, command} = {}) {
|
||||
|
||||
const [mode, ...fileNameParts] = command.arg.split(' ');
|
||||
const fileName = fileNameParts.join(' ');
|
||||
return when.try(this.fs.chmod.bind(this.fs), fileName, parseInt(mode, 8))
|
||||
return Promise.resolve(this.fs.chmod(fileName, parseInt(mode, 8)))
|
||||
.then(() => {
|
||||
return this.reply(200);
|
||||
})
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const _ = require('lodash');
|
||||
|
||||
const registry = require('./registry');
|
||||
|
||||
module.exports = {
|
||||
directive: 'SITE',
|
||||
handler: function ({log, command} = {}) {
|
||||
const registry = require('./registry');
|
||||
const subCommand = this.commands.parse(command.arg);
|
||||
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);
|
||||
|
||||
const handler = registry[subCommand.directive].handler.bind(this);
|
||||
return when.try(handler, { log: subLog, command: subCommand });
|
||||
return Promise.resolve(handler({log: subLog, command: subCommand}));
|
||||
},
|
||||
syntax: '{{cmd}} <subVerb> [...<subParams>]',
|
||||
description: 'Sends site specific commands to remote server'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
|
||||
module.exports = {
|
||||
directive: 'SIZE',
|
||||
@@ -6,7 +6,7 @@ module.exports = {
|
||||
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 when.try(this.fs.get.bind(this.fs), command.arg)
|
||||
return Promise.resolve(this.fs.get(command.arg))
|
||||
.then(fileStat => {
|
||||
return this.reply(213, {message: fileStat.size});
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const _ = require('lodash');
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const getFileStat = require('../../helpers/file-stat');
|
||||
|
||||
module.exports = {
|
||||
@@ -11,26 +11,27 @@ module.exports = {
|
||||
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 when.try(this.fs.get.bind(this.fs), path)
|
||||
return Promise.resolve(this.fs.get(path))
|
||||
.then(stat => {
|
||||
if (stat.isDirectory()) {
|
||||
if (!this.fs.list) return this.reply(402, 'Not supported by file system');
|
||||
|
||||
return when.try(this.fs.list.bind(this.fs), path)
|
||||
.then(files => {
|
||||
const fileList = files.map(file => {
|
||||
const message = getFileStat(file, _.get(this, 'server.options.file_format', 'ls'));
|
||||
return {
|
||||
raw: true,
|
||||
message
|
||||
};
|
||||
});
|
||||
return this.reply(213, 'Status begin', ...fileList, 'Status end');
|
||||
});
|
||||
} else {
|
||||
return this.reply(212, getFileStat(stat, _.get(this, 'server.options.file_format', 'ls')));
|
||||
return Promise.resolve(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);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
|
||||
module.exports = {
|
||||
directive: 'STOR',
|
||||
@@ -9,34 +9,50 @@ module.exports = {
|
||||
const append = command.directive === 'APPE';
|
||||
const fileName = command.arg;
|
||||
|
||||
let dataSocket;
|
||||
return this.connector.waitForConnection()
|
||||
.then(socket => {
|
||||
this.commandSocket.pause();
|
||||
dataSocket = socket;
|
||||
})
|
||||
.then(() => when.try(this.fs.write.bind(this.fs), fileName, {append}))
|
||||
.tap(() => this.commandSocket.pause())
|
||||
.then(() => Promise.resolve(this.fs.write(fileName, {append, start: this.restByteCount})))
|
||||
.then(stream => {
|
||||
return when.promise((resolve, reject) => {
|
||||
stream.once('error', err => dataSocket.emit('error', err));
|
||||
stream.once('finish', () => resolve(this.reply(226, fileName)));
|
||||
const destroyConnection = (connection, reject) => err => {
|
||||
if (connection) connection.destroy(err);
|
||||
reject(err);
|
||||
};
|
||||
|
||||
// Emit `close` if stream has a close listener, otherwise emit `finish` with the end() method
|
||||
// It is assumed that the `close` handler will call the end() method
|
||||
dataSocket.once('end', () => stream.listenerCount('close') ? stream.emit('close') : stream.end());
|
||||
dataSocket.once('error', err => reject(err));
|
||||
dataSocket.on('data', data => stream.write(data, this.encoding));
|
||||
const streamPromise = new Promise((resolve, reject) => {
|
||||
stream.once('error', destroyConnection(this.connector.socket, reject));
|
||||
stream.once('finish', () => resolve());
|
||||
});
|
||||
|
||||
this.reply(150).then(() => dataSocket.resume());
|
||||
})
|
||||
.finally(() => when.try(stream.destroy.bind(stream)));
|
||||
const socketPromise = new Promise((resolve, reject) => {
|
||||
this.connector.socket.on('data', data => {
|
||||
if (this.connector.socket) this.connector.socket.pause();
|
||||
if (stream) {
|
||||
stream.write(data, this.transferType, () => this.connector.socket && this.connector.socket.resume());
|
||||
}
|
||||
});
|
||||
this.connector.socket.once('end', () => {
|
||||
if (stream.listenerCount('close')) stream.emit('close');
|
||||
else stream.end();
|
||||
resolve();
|
||||
});
|
||||
this.connector.socket.once('error', destroyConnection(stream, reject));
|
||||
});
|
||||
|
||||
this.restByteCount = 0;
|
||||
|
||||
return this.reply(150).then(() => this.connector.socket.resume())
|
||||
.then(() => Promise.join(streamPromise, socketPromise))
|
||||
.tap(() => this.emit('STOR', null, fileName))
|
||||
.finally(() => stream.destroy && stream.destroy());
|
||||
})
|
||||
.catch(when.TimeoutError, err => {
|
||||
.then(() => this.reply(226, fileName))
|
||||
.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);
|
||||
})
|
||||
.finally(() => {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
const when = require('when');
|
||||
|
||||
const stor = require('./stor').handler;
|
||||
const Promise = require('bluebird');
|
||||
const {handler: stor} = require('./stor');
|
||||
|
||||
module.exports = {
|
||||
directive: 'STOU',
|
||||
@@ -9,10 +8,10 @@ module.exports = {
|
||||
if (!this.fs.get || !this.fs.getUniqueName) return this.reply(402, 'Not supported by file system');
|
||||
|
||||
const fileName = args.command.arg;
|
||||
return when.try(() => {
|
||||
return when.try(this.fs.get.bind(this.fs), fileName)
|
||||
.then(() => when.try(this.fs.getUniqueName.bind(this.fs)))
|
||||
.catch(() => when.resolve(fileName));
|
||||
return Promise.try(() => {
|
||||
return Promise.resolve(this.fs.get(fileName))
|
||||
.then(() => Promise.resolve(this.fs.getUniqueName()))
|
||||
.catch(() => Promise.resolve(fileName));
|
||||
})
|
||||
.then(name => {
|
||||
args.command.arg = name;
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
const _ = require('lodash');
|
||||
|
||||
const ENCODING_TYPES = {
|
||||
A: 'utf8',
|
||||
I: 'binary',
|
||||
L: 'binary'
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
directive: 'TYPE',
|
||||
handler: function ({command} = {}) {
|
||||
const encoding = _.upperCase(command.arg);
|
||||
if (!ENCODING_TYPES.hasOwnProperty(encoding)) return this.reply(501);
|
||||
|
||||
this.encoding = ENCODING_TYPES[encoding];
|
||||
return this.reply(200);
|
||||
if (/^A[0-9]?$/i.test(command.arg)) {
|
||||
this.transferType = 'ascii';
|
||||
} else if (/^L[0-9]?$/i.test(command.arg) || /^I$/i.test(command.arg)) {
|
||||
this.transferType = 'binary';
|
||||
} else {
|
||||
return this.reply(501);
|
||||
}
|
||||
return this.reply(200, `Switch to "${this.transferType}" transfer mode.`);
|
||||
},
|
||||
syntax: '{{cmd}} <mode>',
|
||||
description: 'Set the transfer mode, binary (I) or utf8 (A)'
|
||||
description: 'Set the transfer mode, binary (I) or ascii (A)',
|
||||
flags: {
|
||||
feat: 'TYPE A,I,L'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -21,6 +21,7 @@ const commands = [
|
||||
require('./registration/port'),
|
||||
require('./registration/pwd'),
|
||||
require('./registration/quit'),
|
||||
require('./registration/rest'),
|
||||
require('./registration/retr'),
|
||||
require('./registration/rmd'),
|
||||
require('./registration/rnfr'),
|
||||
@@ -35,7 +36,9 @@ const commands = [
|
||||
require('./registration/type'),
|
||||
require('./registration/user'),
|
||||
require('./registration/pbsz'),
|
||||
require('./registration/prot')
|
||||
require('./registration/prot'),
|
||||
require('./registration/eprt'),
|
||||
require('./registration/epsv')
|
||||
];
|
||||
|
||||
const registry = commands.reduce((result, cmd) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const _ = require('lodash');
|
||||
const uuid = require('uuid');
|
||||
const when = require('when');
|
||||
const sequence = require('when/sequence');
|
||||
const Promise = require('bluebird');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
const BaseConnector = require('./connector/base');
|
||||
const FileSystem = require('./fs');
|
||||
@@ -9,14 +9,18 @@ const Commands = require('./commands');
|
||||
const errors = require('./errors');
|
||||
const DEFAULT_MESSAGE = require('./messages');
|
||||
|
||||
class FtpConnection {
|
||||
class FtpConnection extends EventEmitter {
|
||||
constructor(server, options) {
|
||||
super();
|
||||
this.server = server;
|
||||
this.id = uuid.v4();
|
||||
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);
|
||||
|
||||
@@ -30,35 +34,49 @@ class FtpConnection {
|
||||
this.commandSocket.on('close', () => {
|
||||
if (this.connector) this.connector.end();
|
||||
if (this.commandSocket && !this.commandSocket.destroyed) this.commandSocket.destroy();
|
||||
this.removeAllListeners();
|
||||
});
|
||||
}
|
||||
|
||||
_handleData(data) {
|
||||
const messages = _.compact(data.toString('utf8').split('\r\n'));
|
||||
const messages = _.compact(data.toString(this.encoding).split('\r\n'));
|
||||
this.log.trace(messages);
|
||||
return sequence(messages.map(message => this.commands.handle.bind(this.commands, message)));
|
||||
return Promise.mapSeries(messages, message => this.commands.handle(message));
|
||||
}
|
||||
|
||||
get ip() {
|
||||
try {
|
||||
return this.dataSocket ? this.dataSocket.remoteAddress : this.commandSocket.remoteAddress;
|
||||
return this.commandSocket ? this.commandSocket.remoteAddress : undefined;
|
||||
} catch (ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
get restByteCount() {
|
||||
return this._restByteCount > 0 ? this._restByteCount : undefined;
|
||||
}
|
||||
set restByteCount(rbc) {
|
||||
this._restByteCount = rbc;
|
||||
}
|
||||
|
||||
get secure() {
|
||||
return this.server.isTLS || this._secure;
|
||||
}
|
||||
set secure(sec) {
|
||||
this._secure = sec;
|
||||
}
|
||||
|
||||
close(code = 421, message = 'Closing connection') {
|
||||
return when
|
||||
.resolve(code)
|
||||
return Promise.resolve(code)
|
||||
.then(_code => _code && this.reply(_code, message))
|
||||
.then(() => this.commandSocket && this.commandSocket.end());
|
||||
}
|
||||
|
||||
login(username, password) {
|
||||
return when.try(() => {
|
||||
return Promise.try(() => {
|
||||
const loginListeners = this.server.listeners('login');
|
||||
if (!loginListeners || !loginListeners.length) {
|
||||
if (!this.server.options.anoymous) 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});
|
||||
}
|
||||
@@ -76,8 +94,8 @@ class FtpConnection {
|
||||
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 when.map(letters, promise => {
|
||||
return when(promise)
|
||||
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
|
||||
@@ -85,8 +103,12 @@ class FtpConnection {
|
||||
if (!letter.socket) letter.socket = options.socket ? options.socket : this.commandSocket;
|
||||
if (!letter.message) letter.message = DEFAULT_MESSAGE[options.code] || 'No information';
|
||||
if (!letter.encoding) letter.encoding = this.encoding;
|
||||
return when(letter.message) // allow passing in a promise as a message
|
||||
return Promise.resolve(letter.message) // allow passing in a promise as a message
|
||||
.then(message => {
|
||||
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;
|
||||
return letter;
|
||||
});
|
||||
@@ -94,16 +116,11 @@ class FtpConnection {
|
||||
});
|
||||
};
|
||||
|
||||
const processLetter = (letter, index) => {
|
||||
return when.promise((resolve, reject) => {
|
||||
const seperator = !options.hasOwnProperty('eol') ?
|
||||
letters.length - 1 === index ? ' ' : '-' :
|
||||
options.eol ? ' ' : '-';
|
||||
const packet = !letter.raw ? _.compact([letter.code || options.code, letter.message]).join(seperator) : letter.message;
|
||||
|
||||
const processLetter = letter => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (letter.socket && letter.socket.writable) {
|
||||
this.log.trace({port: letter.socket.address().port, packet}, 'Reply');
|
||||
letter.socket.write(packet + '\r\n', letter.encoding, err => {
|
||||
this.log.trace({port: letter.socket.address().port, encoding: letter.encoding, message: letter.message}, 'Reply');
|
||||
letter.socket.write(letter.message + '\r\n', letter.encoding, err => {
|
||||
if (err) {
|
||||
this.log.error(err);
|
||||
return reject(err);
|
||||
@@ -115,7 +132,9 @@ class FtpConnection {
|
||||
};
|
||||
|
||||
return satisfyParameters()
|
||||
.then(satisfiedLetters => sequence(satisfiedLetters.map((letter, index) => processLetter.bind(this, letter, index))))
|
||||
.then(satisfiedLetters => Promise.mapSeries(satisfiedLetters, (letter, index) => {
|
||||
return processLetter(letter, index);
|
||||
}))
|
||||
.catch(err => {
|
||||
this.log.error(err);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const {Socket} = require('net');
|
||||
const tls = require('tls');
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const Connector = require('./base');
|
||||
|
||||
class Active extends Connector {
|
||||
@@ -10,25 +10,27 @@ class Active extends Connector {
|
||||
}
|
||||
|
||||
waitForConnection({timeout = 5000, delay = 250} = {}) {
|
||||
return when.iterate(
|
||||
() => {},
|
||||
() => this.dataSocket && this.dataSocket.connected,
|
||||
() => when().delay(delay)
|
||||
).timeout(timeout)
|
||||
.then(() => this.dataSocket);
|
||||
const checkSocket = () => {
|
||||
if (this.dataSocket && this.dataSocket.connected) {
|
||||
return Promise.resolve(this.dataSocket);
|
||||
}
|
||||
return Promise.resolve().delay(delay)
|
||||
.then(() => checkSocket());
|
||||
};
|
||||
|
||||
return checkSocket().timeout(timeout);
|
||||
}
|
||||
|
||||
setupConnection(host, port, family = 4) {
|
||||
const closeExistingServer = () => this.dataSocket ?
|
||||
when(this.dataSocket.destroy()) :
|
||||
when.resolve();
|
||||
const closeExistingServer = () => Promise.resolve(
|
||||
this.dataSocket ? this.dataSocket.destroy() : undefined);
|
||||
|
||||
return closeExistingServer()
|
||||
.then(() => {
|
||||
this.dataSocket = new Socket();
|
||||
this.dataSocket.setEncoding(this.encoding);
|
||||
this.dataSocket.on('error', err => this.server.emit('client-error', {connection: this.connection, context: 'dataSocket', error: err}));
|
||||
this.dataSocket.connect({ host, port, family }, () => {
|
||||
this.dataSocket.setEncoding(this.connection.transferType);
|
||||
this.dataSocket.on('error', err => this.server && this.server.emit('client-error', {connection: this.connection, context: 'dataSocket', error: err}));
|
||||
this.dataSocket.connect({host, port, family}, () => {
|
||||
this.dataSocket.pause();
|
||||
|
||||
if (this.connection.secure) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const errors = require('../errors');
|
||||
|
||||
class Connector {
|
||||
@@ -14,20 +14,34 @@ class Connector {
|
||||
return this.connection.log;
|
||||
}
|
||||
|
||||
get socket() {
|
||||
return this.dataSocket;
|
||||
}
|
||||
|
||||
get server() {
|
||||
return this.connection.server;
|
||||
}
|
||||
|
||||
waitForConnection() {
|
||||
return when.reject(new errors.ConnectorError('No connector setup, send PASV or PORT'));
|
||||
return Promise.reject(new errors.ConnectorError('No connector setup, send PASV or PORT'));
|
||||
}
|
||||
|
||||
end() {
|
||||
if (this.dataSocket) this.dataSocket.end();
|
||||
if (this.dataServer) this.dataServer.close();
|
||||
this.dataSocket = null;
|
||||
this.dataServer = null;
|
||||
this.type = false;
|
||||
const closeDataSocket = new Promise(resolve => {
|
||||
if (this.dataSocket) this.dataSocket.end();
|
||||
else resolve();
|
||||
});
|
||||
const closeDataServer = new Promise(resolve => {
|
||||
if (this.dataServer) this.dataServer.close(() => resolve());
|
||||
else resolve();
|
||||
});
|
||||
|
||||
return Promise.all([closeDataSocket, closeDataServer])
|
||||
.then(() => {
|
||||
this.dataSocket = null;
|
||||
this.dataServer = null;
|
||||
this.type = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
module.exports = Connector;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const net = require('net');
|
||||
const tls = require('tls');
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
|
||||
const Connector = require('./base');
|
||||
const findPort = require('../helpers/find-port');
|
||||
@@ -13,19 +13,23 @@ class Passive extends Connector {
|
||||
}
|
||||
|
||||
waitForConnection({timeout = 5000, delay = 250} = {}) {
|
||||
if (!this.dataServer) return when.reject(new errors.ConnectorError('Passive server not setup'));
|
||||
return when.iterate(
|
||||
() => {},
|
||||
() => this.dataServer && this.dataServer.listening && this.dataSocket && this.dataSocket.connected,
|
||||
() => when().delay(delay)
|
||||
).timeout(timeout)
|
||||
.then(() => this.dataSocket);
|
||||
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().delay(delay)
|
||||
.then(() => checkSocket());
|
||||
};
|
||||
|
||||
return checkSocket().timeout(timeout);
|
||||
}
|
||||
|
||||
setupServer() {
|
||||
const closeExistingServer = () => this.dataServer ?
|
||||
when.promise(resolve => this.dataServer.close(() => resolve())) :
|
||||
when.resolve();
|
||||
new Promise(resolve => this.dataServer.close(() => resolve())) :
|
||||
Promise.resolve();
|
||||
|
||||
return closeExistingServer()
|
||||
.then(() => this.getPort())
|
||||
@@ -41,7 +45,7 @@ class Passive extends Connector {
|
||||
return this.connection.reply(550, 'Remote addresses do not match')
|
||||
.finally(() => this.connection.close());
|
||||
}
|
||||
this.log.debug({port}, 'Passive connection fulfilled.');
|
||||
this.log.trace({port, remoteAddress: socket.remoteAddress}, 'Passive connection fulfilled.');
|
||||
|
||||
if (this.connection.secure) {
|
||||
const secureContext = tls.createSecureContext(this.server._tls);
|
||||
@@ -54,28 +58,28 @@ class Passive extends Connector {
|
||||
this.dataSocket = socket;
|
||||
}
|
||||
this.dataSocket.connected = true;
|
||||
this.dataSocket.setEncoding(this.connection.encoding);
|
||||
this.dataSocket.on('error', err => this.server.emit('client-error', {connection: this.connection, context: 'dataSocket', error: err}));
|
||||
this.dataSocket.setEncoding(this.connection.transferType);
|
||||
this.dataSocket.on('error', err => this.server && this.server.emit('client-error', {connection: this.connection, context: 'dataSocket', error: err}));
|
||||
this.dataSocket.on('close', () => {
|
||||
this.log.debug('Passive connection closed');
|
||||
this.log.trace('Passive connection closed');
|
||||
this.end();
|
||||
});
|
||||
};
|
||||
|
||||
this.dataSocket = null;
|
||||
this.dataServer = net.createServer({ pauseOnConnect: true }, connectionHandler);
|
||||
this.dataServer = net.createServer({pauseOnConnect: true}, connectionHandler);
|
||||
this.dataServer.maxConnections = 1;
|
||||
this.dataServer.on('error', err => this.server.emit('client-error', {connection: this.connection, context: 'dataServer', error: err}));
|
||||
this.dataServer.on('error', err => this.server && this.server.emit('client-error', {connection: this.connection, context: 'dataServer', error: err}));
|
||||
this.dataServer.on('close', () => {
|
||||
this.log.debug('Passive server closed');
|
||||
this.log.trace('Passive server closed');
|
||||
this.dataServer = null;
|
||||
});
|
||||
|
||||
return when.promise((resolve, reject) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.dataServer.listen(port, err => {
|
||||
if (err) reject(err);
|
||||
else {
|
||||
this.log.info({port}, 'Passive connection listening');
|
||||
this.log.debug({port}, 'Passive connection listening');
|
||||
resolve(this.dataServer);
|
||||
}
|
||||
});
|
||||
|
||||
44
src/fs.js
44
src/fs.js
@@ -1,14 +1,12 @@
|
||||
const _ = require('lodash');
|
||||
const nodePath = require('path');
|
||||
const uuid = require('uuid');
|
||||
const when = require('when');
|
||||
const whenNode = require('when/node');
|
||||
const syncFs = require('fs');
|
||||
const fs = whenNode.liftAll(syncFs);
|
||||
const Promise = require('bluebird');
|
||||
const fs = Promise.promisifyAll(require('fs'));
|
||||
const errors = require('./errors');
|
||||
|
||||
class FileSystem {
|
||||
constructor(connection, { root, cwd } = {}) {
|
||||
constructor(connection, {root, cwd} = {}) {
|
||||
this.connection = connection;
|
||||
this.cwd = cwd || nodePath.sep;
|
||||
this.root = root || process.cwd();
|
||||
@@ -32,19 +30,19 @@ class FileSystem {
|
||||
|
||||
get(fileName) {
|
||||
const {fsPath} = this._resolvePath(fileName);
|
||||
return fs.stat(fsPath)
|
||||
return fs.statAsync(fsPath)
|
||||
.then(stat => _.set(stat, 'name', fileName));
|
||||
}
|
||||
|
||||
list(path = '.') {
|
||||
const {fsPath} = this._resolvePath(path);
|
||||
return fs.readdir(fsPath)
|
||||
return fs.readdirAsync(fsPath)
|
||||
.then(fileNames => {
|
||||
return when.map(fileNames, fileName => {
|
||||
return Promise.map(fileNames, fileName => {
|
||||
const filePath = nodePath.join(fsPath, fileName);
|
||||
return fs.access(filePath, syncFs.constants.F_OK)
|
||||
return fs.accessAsync(filePath, fs.constants.F_OK)
|
||||
.then(() => {
|
||||
return fs.stat(filePath)
|
||||
return fs.statAsync(filePath)
|
||||
.then(stat => _.set(stat, 'name', fileName));
|
||||
})
|
||||
.catch(() => null);
|
||||
@@ -55,7 +53,7 @@ class FileSystem {
|
||||
|
||||
chdir(path = '.') {
|
||||
const {fsPath, serverPath} = this._resolvePath(path);
|
||||
return fs.stat(fsPath)
|
||||
return fs.statAsync(fsPath)
|
||||
.tap(stat => {
|
||||
if (!stat.isDirectory()) throw new errors.FileSystemError('Not a valid directory');
|
||||
})
|
||||
@@ -65,50 +63,50 @@ class FileSystem {
|
||||
});
|
||||
}
|
||||
|
||||
write(fileName, {append = false} = {}) {
|
||||
write(fileName, {append = false, start = undefined} = {}) {
|
||||
const {fsPath} = this._resolvePath(fileName);
|
||||
const stream = syncFs.createWriteStream(fsPath, {flags: !append ? 'w+' : 'a+'});
|
||||
stream.once('error', () => fs.unlink(fsPath));
|
||||
const stream = fs.createWriteStream(fsPath, {flags: !append ? 'w+' : 'a+', start});
|
||||
stream.once('error', () => fs.unlinkAsync(fsPath));
|
||||
stream.once('close', () => stream.end());
|
||||
return stream;
|
||||
}
|
||||
|
||||
read(fileName) {
|
||||
read(fileName, {start = undefined} = {}) {
|
||||
const {fsPath} = this._resolvePath(fileName);
|
||||
return fs.stat(fsPath)
|
||||
return fs.statAsync(fsPath)
|
||||
.tap(stat => {
|
||||
if (stat.isDirectory()) throw new errors.FileSystemError('Cannot read a directory');
|
||||
})
|
||||
.then(() => {
|
||||
const stream = syncFs.createReadStream(fsPath, {flags: 'r'});
|
||||
const stream = fs.createReadStream(fsPath, {flags: 'r', start});
|
||||
return stream;
|
||||
});
|
||||
}
|
||||
|
||||
delete(path) {
|
||||
const {fsPath} = this._resolvePath(path);
|
||||
return fs.stat(fsPath)
|
||||
return fs.statAsync(fsPath)
|
||||
.then(stat => {
|
||||
if (stat.isDirectory()) return fs.rmdir(fsPath);
|
||||
else return fs.unlink(fsPath);
|
||||
if (stat.isDirectory()) return fs.rmdirAsync(fsPath);
|
||||
else return fs.unlinkAsync(fsPath);
|
||||
});
|
||||
}
|
||||
|
||||
mkdir(path) {
|
||||
const {fsPath} = this._resolvePath(path);
|
||||
return fs.mkdir(fsPath)
|
||||
return fs.mkdirAsync(fsPath)
|
||||
.then(() => fsPath);
|
||||
}
|
||||
|
||||
rename(from, to) {
|
||||
const {fsPath: fromPath} = this._resolvePath(from);
|
||||
const {fsPath: toPath} = this._resolvePath(to);
|
||||
return fs.rename(fromPath, toPath);
|
||||
return fs.renameAsync(fromPath, toPath);
|
||||
}
|
||||
|
||||
chmod(path, mode) {
|
||||
const {fsPath} = this._resolvePath(path);
|
||||
return fs.chmod(fsPath, mode);
|
||||
return fs.chmodAsync(fsPath, mode);
|
||||
}
|
||||
|
||||
getUniqueName() {
|
||||
|
||||
@@ -18,7 +18,8 @@ module.exports = function (fileStat, format = 'ls') {
|
||||
function ls(fileStat) {
|
||||
const now = moment.utc();
|
||||
const mtime = moment.utc(new Date(fileStat.mtime));
|
||||
const dateFormat = now.diff(mtime, 'months') < 6 ? 'MMM DD HH:mm' : 'MMM DD YYYY';
|
||||
const timeDiff = now.diff(mtime, 'months');
|
||||
const dateFormat = timeDiff < 6 ? 'MMM DD HH:mm' : 'MMM DD YYYY';
|
||||
|
||||
return [
|
||||
fileStat.mode ? [
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
const net = require('net');
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const errors = require('../errors');
|
||||
|
||||
module.exports = function (min = 1, max = undefined) {
|
||||
return when.promise((resolve, reject) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let checkPort = min;
|
||||
let portCheckServer = net.createServer();
|
||||
portCheckServer.maxConnections = 0;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
const http = require('http');
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const errors = require('../errors');
|
||||
|
||||
const IP_WEBSITE = 'http://api.ipify.org/';
|
||||
|
||||
module.exports = function (hostname) {
|
||||
return when.promise((resolve, reject) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!hostname || hostname === '0.0.0.0') {
|
||||
let ip = '';
|
||||
http.get(IP_WEBSITE, response => {
|
||||
|
||||
57
src/index.js
57
src/index.js
@@ -1,16 +1,18 @@
|
||||
const _ = require('lodash');
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const nodeUrl = require('url');
|
||||
const buyan = require('bunyan');
|
||||
const net = require('net');
|
||||
const tls = require('tls');
|
||||
const fs = require('fs');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
const Connection = require('./connection');
|
||||
const resolveHost = require('./helpers/resolve-host');
|
||||
|
||||
class FtpServer {
|
||||
class FtpServer extends EventEmitter {
|
||||
constructor(url, options = {}) {
|
||||
super();
|
||||
this.options = _.merge({
|
||||
log: buyan.createLogger({name: 'ftp-srv'}),
|
||||
anonymous: false,
|
||||
@@ -43,21 +45,14 @@ class FtpServer {
|
||||
return connection.reply(220, ...greeting, features)
|
||||
.finally(() => socket.resume());
|
||||
};
|
||||
const serverOptions = _.assign(this.isTLS ? this._tls : {}, { pauseOnConnect: true });
|
||||
const serverOptions = _.assign(this.isTLS ? this._tls : {}, {pauseOnConnect: true});
|
||||
|
||||
this.server = (this.isTLS ? tls : net).createServer(serverOptions, serverConnectionHandler);
|
||||
this.server.on('error', err => this.log.error(err, '[Event] error'));
|
||||
if (this.isTLS) {
|
||||
this.server.on('tlsClientError', err => this.log.error(err, '[Event] tlsClientError'));
|
||||
}
|
||||
this.on = this.server.on.bind(this.server);
|
||||
this.once = this.server.once.bind(this.server);
|
||||
this.listeners = this.server.listeners.bind(this.server);
|
||||
|
||||
process.on('SIGTERM', () => this.close());
|
||||
process.on('SIGINT', () => this.close());
|
||||
process.on('SIGBREAK', () => this.close());
|
||||
process.on('SIGHUP', () => this.close());
|
||||
process.on('SIGTERM', () => this.quit());
|
||||
process.on('SIGINT', () => this.quit());
|
||||
process.on('SIGQUIT', () => this.quit());
|
||||
}
|
||||
|
||||
get isTLS() {
|
||||
@@ -68,7 +63,7 @@ class FtpServer {
|
||||
return resolveHost(this.url.hostname)
|
||||
.then(hostname => {
|
||||
this.url.hostname = hostname;
|
||||
return when.promise((resolve, reject) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.server.listen(this.url.port, err => {
|
||||
if (err) return reject(err);
|
||||
this.log.info({
|
||||
@@ -76,25 +71,21 @@ class FtpServer {
|
||||
ip: this.url.hostname,
|
||||
port: this.url.port
|
||||
}, 'Listening');
|
||||
resolve();
|
||||
resolve('Listening');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
emitPromise(action, ...data) {
|
||||
const defer = when.defer();
|
||||
const params = _.concat(data, [defer.resolve, defer.reject]);
|
||||
this.server.emit(action, ...params);
|
||||
return defer.promise;
|
||||
}
|
||||
|
||||
emit(action, ...data) {
|
||||
this.server.emit(action, ...data);
|
||||
return new Promise((resolve, reject) => {
|
||||
const params = _.concat(data, [resolve, reject]);
|
||||
this.emit.call(this, action, ...params);
|
||||
});
|
||||
}
|
||||
|
||||
setupTLS(_tls) {
|
||||
if (!tls) return false;
|
||||
if (!_tls) return false;
|
||||
return _.assign({}, _tls, {
|
||||
cert: _tls.cert ? fs.readFileSync(_tls.cert) : undefined,
|
||||
key: _tls.key ? fs.readFileSync(_tls.key) : undefined,
|
||||
@@ -120,7 +111,7 @@ class FtpServer {
|
||||
}
|
||||
|
||||
disconnectClient(id) {
|
||||
return when.promise(resolve => {
|
||||
return new Promise(resolve => {
|
||||
const client = this.connections[id];
|
||||
if (!client) return resolve();
|
||||
delete this.connections[id];
|
||||
@@ -129,21 +120,27 @@ class FtpServer {
|
||||
} catch (err) {
|
||||
this.log.error(err, 'Error closing connection', {id});
|
||||
} finally {
|
||||
resolve();
|
||||
resolve('Disconnected');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
quit() {
|
||||
return this.close()
|
||||
.finally(() => process.exit(0));
|
||||
}
|
||||
|
||||
close() {
|
||||
this.log.info('Server closing...');
|
||||
this.server.maxConnections = 0;
|
||||
return when.map(Object.keys(this.connections), id => this.disconnectClient(id))
|
||||
.then(() => when.promise(resolve => {
|
||||
return Promise.map(Object.keys(this.connections), id => Promise.try(this.disconnectClient.bind(this, id)))
|
||||
.then(() => new Promise(resolve => {
|
||||
this.server.close(err => {
|
||||
if (err) this.log.error(err, 'Error closing server');
|
||||
resolve();
|
||||
resolve('Closed');
|
||||
});
|
||||
}));
|
||||
}))
|
||||
.then(() => this.removeAllListeners());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const {expect} = require('chai');
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const bunyan = require('bunyan');
|
||||
const sinon = require('sinon');
|
||||
|
||||
@@ -11,7 +11,7 @@ describe('FtpCommands', function () {
|
||||
let mockConnection = {
|
||||
authenticated: false,
|
||||
log: bunyan.createLogger({name: 'FtpCommands'}),
|
||||
reply: () => when.resolve({}),
|
||||
reply: () => Promise.resolve({}),
|
||||
server: {
|
||||
options: {
|
||||
blacklist: ['allo']
|
||||
@@ -53,6 +53,29 @@ describe('FtpCommands', function () {
|
||||
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"');
|
||||
});
|
||||
|
||||
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');
|
||||
expect(cmd.flags).to.deep.equal(['-l', '-A', '--zz88A']);
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
||||
describe('handle', function () {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
@@ -6,10 +6,10 @@ const CMD = 'ABOR';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockClient = {
|
||||
reply: () => when.resolve(),
|
||||
reply: () => Promise.resolve(),
|
||||
connector: {
|
||||
waitForConnection: () => when.resolve(),
|
||||
end: () => when.resolve()
|
||||
waitForConnection: () => Promise.resolve(),
|
||||
end: () => Promise.resolve()
|
||||
}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
@@ -33,7 +33,7 @@ describe(CMD, function () {
|
||||
.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(226);
|
||||
expect(mockClient.reply.args[0][0]).to.equal(225);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
@@ -6,7 +6,7 @@ const CMD = 'ALLO';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockClient = {
|
||||
reply: () => when.resolve()
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
@@ -6,7 +6,7 @@ const CMD = 'AUTH';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockClient = {
|
||||
reply: () => when.resolve(),
|
||||
reply: () => Promise.resolve(),
|
||||
server: {
|
||||
_tls: {}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('TLS // supported', () => {
|
||||
return cmdFn({command: { arg: 'TLS', directive: CMD}})
|
||||
return cmdFn({command: {arg: 'TLS', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(234);
|
||||
expect(mockClient.secure).to.equal(true);
|
||||
@@ -31,14 +31,14 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('SSL // not supported', () => {
|
||||
return cmdFn({command: { arg: 'SSL', directive: CMD}})
|
||||
return cmdFn({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}})
|
||||
return cmdFn({command: {arg: 'bad', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
@@ -8,9 +8,9 @@ describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
const mockClient = {
|
||||
reply: () => when.resolve(),
|
||||
reply: () => Promise.resolve(),
|
||||
fs: {
|
||||
chdir: () => when.resolve()
|
||||
chdir: () => Promise.resolve()
|
||||
}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
@@ -8,7 +8,7 @@ describe(CMD, function () {
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
fs: { chdir: () => {} }
|
||||
fs: {chdir: () => {}}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
@@ -24,7 +24,7 @@ describe(CMD, function () {
|
||||
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = { reply: () => {} };
|
||||
const badMockClient = {reply: () => {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
@@ -35,7 +35,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('fails on no fs chdir command', () => {
|
||||
const badMockClient = { reply: () => {}, fs: {} };
|
||||
const badMockClient = {reply: () => {}, fs: {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
@@ -47,7 +47,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('test // successful', () => {
|
||||
return cmdFn({log, command: { arg: 'test', directive: CMD}})
|
||||
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');
|
||||
@@ -58,7 +58,7 @@ describe(CMD, function () {
|
||||
mockClient.fs.chdir.restore();
|
||||
sandbox.stub(mockClient.fs, 'chdir').resolves('/test');
|
||||
|
||||
return cmdFn({log, command: { arg: 'test', directive: CMD}})
|
||||
return cmdFn({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');
|
||||
@@ -69,7 +69,7 @@ describe(CMD, function () {
|
||||
mockClient.fs.chdir.restore();
|
||||
sandbox.stub(mockClient.fs, 'chdir').rejects(new Error('Bad'));
|
||||
|
||||
return cmdFn({log, command: { arg: 'bad', directive: CMD}})
|
||||
return cmdFn({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');
|
||||
|
||||
@@ -8,7 +8,7 @@ describe(CMD, function () {
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
fs: { delete: () => {} }
|
||||
fs: {delete: () => {}}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
@@ -24,7 +24,7 @@ describe(CMD, function () {
|
||||
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = { reply: () => {} };
|
||||
const badMockClient = {reply: () => {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
@@ -35,7 +35,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('fails on no fs delete command', () => {
|
||||
const badMockClient = { reply: () => {}, fs: {} };
|
||||
const badMockClient = {reply: () => {}, fs: {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
@@ -47,7 +47,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('test // successful', () => {
|
||||
return cmdFn({log, command: { arg: 'test', directive: CMD}})
|
||||
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');
|
||||
@@ -58,7 +58,7 @@ describe(CMD, function () {
|
||||
mockClient.fs.delete.restore();
|
||||
sandbox.stub(mockClient.fs, 'delete').rejects(new Error('Bad'));
|
||||
|
||||
return cmdFn({log, command: { arg: 'bad', directive: CMD}})
|
||||
return cmdFn({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');
|
||||
|
||||
60
test/commands/registration/eprt.spec.js
Normal file
60
test/commands/registration/eprt.spec.js
Normal file
@@ -0,0 +1,60 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const ActiveConnector = require('../../../src/connector/active');
|
||||
|
||||
const CMD = 'EPRT';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
sandbox.stub(ActiveConnector.prototype, 'setupConnection').resolves();
|
||||
});
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('// unsuccessful | no argument', () => {
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
35
test/commands/registration/epsv.spec.js
Normal file
35
test/commands/registration/epsv.spec.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const PassiveConnector = require('../../../src/connector/passive');
|
||||
|
||||
const CMD = 'EPSV';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
sandbox.stub(mockClient, 'reply').resolves();
|
||||
sandbox.stub(PassiveConnector.prototype, 'setupServer').resolves({
|
||||
address: () => ({port: 12345})
|
||||
});
|
||||
});
|
||||
afterEach(() => {
|
||||
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|)');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
@@ -6,7 +6,7 @@ const CMD = 'HELP';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockClient = {
|
||||
reply: () => when.resolve()
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
@@ -20,28 +20,28 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn({command: { directive: CMD }})
|
||||
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}})
|
||||
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}})
|
||||
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}})
|
||||
return cmdFn({command: {arg: 'bad', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(502);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
@@ -14,7 +14,7 @@ describe(CMD, function () {
|
||||
get: () => {}
|
||||
},
|
||||
connector: {
|
||||
waitForConnection: () => when({}),
|
||||
waitForConnection: () => Promise.resolve({}),
|
||||
end: () => {}
|
||||
},
|
||||
commandSocket: {
|
||||
@@ -88,7 +88,7 @@ describe(CMD, function () {
|
||||
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = { reply: () => {} };
|
||||
const badMockClient = {reply: () => {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
@@ -99,7 +99,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('fails on no fs list command', () => {
|
||||
const badMockClient = { reply: () => {}, fs: {} };
|
||||
const badMockClient = {reply: () => {}, fs: {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
@@ -165,7 +165,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('. // unsuccessful (timeout)', () => {
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').returns(when.reject(new when.TimeoutError()));
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').returns(Promise.reject(new Promise.TimeoutError()));
|
||||
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
.then(() => {
|
||||
|
||||
@@ -8,7 +8,7 @@ describe(CMD, function () {
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
fs: { get: () => {} }
|
||||
fs: {get: () => {}}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
@@ -24,7 +24,7 @@ describe(CMD, function () {
|
||||
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = { reply: () => {} };
|
||||
const badMockClient = {reply: () => {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
@@ -35,7 +35,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('fails on no fs get command', () => {
|
||||
const badMockClient = { reply: () => {}, fs: {} };
|
||||
const badMockClient = {reply: () => {}, fs: {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ describe(CMD, function () {
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
fs: { mkdir: () => {} }
|
||||
fs: {mkdir: () => {}}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
@@ -24,7 +24,7 @@ describe(CMD, function () {
|
||||
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = { reply: () => {} };
|
||||
const badMockClient = {reply: () => {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
@@ -35,7 +35,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('fails on no fs mkdir command', () => {
|
||||
const badMockClient = { reply: () => {}, fs: {} };
|
||||
const badMockClient = {reply: () => {}, fs: {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
@@ -6,7 +6,7 @@ const CMD = 'MODE';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockClient = {
|
||||
reply: () => when.resolve()
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
@@ -14,7 +14,7 @@ describe(CMD, function () {
|
||||
list: () => {}
|
||||
},
|
||||
connector: {
|
||||
waitForConnection: () => when({}),
|
||||
waitForConnection: () => Promise.resolve({}),
|
||||
end: () => {}
|
||||
},
|
||||
commandSocket: {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
@@ -6,7 +6,7 @@ const CMD = 'NOOP';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockClient = {
|
||||
reply: () => when.resolve()
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
@@ -6,7 +6,7 @@ const CMD = 'OPTS';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockClient = {
|
||||
reply: () => when.resolve()
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
@@ -19,10 +19,40 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('// successful', () => {
|
||||
it('// unsuccessful', () => {
|
||||
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(500);
|
||||
});
|
||||
});
|
||||
|
||||
it('UTF8 BAD // unsuccessful', () => {
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ describe(CMD, function () {
|
||||
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);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
@@ -6,7 +6,7 @@ const CMD = 'PBSZ';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockClient = {
|
||||
reply: () => when.resolve(),
|
||||
reply: () => Promise.resolve(),
|
||||
server: {}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
@@ -8,7 +8,7 @@ const CMD = 'PORT';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockClient = {
|
||||
reply: () => when.resolve()
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
@@ -30,14 +30,14 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// unsuccessful | invalid argument', () => {
|
||||
return cmdFn({ command: { arg: '1,2,3,4,5' } })
|
||||
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' } })
|
||||
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);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
@@ -6,7 +6,7 @@ const CMD = 'PROT';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockClient = {
|
||||
reply: () => when.resolve(),
|
||||
reply: () => Promise.resolve(),
|
||||
server: {}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
@@ -8,7 +8,7 @@ describe(CMD, function () {
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
fs: { currentDirectory: () => {} }
|
||||
fs: {currentDirectory: () => {}}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
@@ -24,7 +24,7 @@ describe(CMD, function () {
|
||||
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = { reply: () => {} };
|
||||
const badMockClient = {reply: () => {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
@@ -35,7 +35,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('fails on no fs currentDirectory command', () => {
|
||||
const badMockClient = { reply: () => {}, fs: {} };
|
||||
const badMockClient = {reply: () => {}, fs: {}};
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
|
||||
@@ -47,7 +47,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn({log, command: { arg: 'test', directive: CMD}})
|
||||
return cmdFn({log, command: {arg: 'test', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(257);
|
||||
});
|
||||
|
||||
58
test/commands/registration/rest.spec.js
Normal file
58
test/commands/registration/rest.spec.js
Normal file
@@ -0,0 +1,58 @@
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const Promise = require('bluebird');
|
||||
|
||||
const CMD = 'REST';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('// unsuccessful', () => {
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
it('bad // unsuccessful', () => {
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,20 +1,22 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
const CMD = 'RETR';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
let emitter;
|
||||
const mockClient = {
|
||||
commandSocket: {
|
||||
pause: () => {},
|
||||
resume: () => {}
|
||||
},
|
||||
reply: () => when.resolve(),
|
||||
reply: () => Promise.resolve(),
|
||||
connector: {
|
||||
waitForConnection: () => when.resolve({
|
||||
waitForConnection: () => Promise.resolve({
|
||||
resume: () => {}
|
||||
}),
|
||||
end: () => {}
|
||||
@@ -29,6 +31,11 @@ describe(CMD, function () {
|
||||
read: () => {}
|
||||
};
|
||||
|
||||
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());
|
||||
@@ -53,10 +60,11 @@ describe(CMD, function () {
|
||||
|
||||
it('// unsuccessful | connector times out', () => {
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').callsFake(function () {
|
||||
return when.reject(new when.TimeoutError());
|
||||
return Promise.reject(new Promise.TimeoutError());
|
||||
});
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'} })
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(425);
|
||||
});
|
||||
@@ -64,12 +72,28 @@ describe(CMD, function () {
|
||||
|
||||
it('// unsuccessful | connector errors out', () => {
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').callsFake(function () {
|
||||
return when.reject(new Error('test'));
|
||||
return Promise.reject(new Error('test'));
|
||||
});
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'} })
|
||||
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'));
|
||||
});
|
||||
|
||||
let errorEmitted = false;
|
||||
emitter.once('RETR', err => {
|
||||
errorEmitted = !!err;
|
||||
});
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'}})
|
||||
.then(() => {
|
||||
expect(errorEmitted).to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const CMD = 'RNFR';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockLog = { error: () => {} };
|
||||
const mockClient = { reply: () => when.resolve() };
|
||||
const mockLog = {error: () => {}};
|
||||
const mockClient = {reply: () => Promise.resolve()};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -14,7 +14,7 @@ describe(CMD, function () {
|
||||
|
||||
mockClient.renameFrom = 'test';
|
||||
mockClient.fs = {
|
||||
get: () => when.resolve()
|
||||
get: () => Promise.resolve()
|
||||
};
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
@@ -46,14 +46,14 @@ describe(CMD, function () {
|
||||
mockClient.fs.get.restore();
|
||||
sandbox.stub(mockClient.fs, 'get').rejects(new Error('test'));
|
||||
|
||||
return cmdFn({ log: mockLog, command: { arg: 'test' } })
|
||||
return cmdFn({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' } })
|
||||
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,12 +1,12 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const CMD = 'RNTO';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockLog = { error: () => {} };
|
||||
const mockClient = { reply: () => when.resolve() };
|
||||
const mockLog = {error: () => {}};
|
||||
const mockClient = {reply: () => Promise.resolve()};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -14,8 +14,8 @@ describe(CMD, function () {
|
||||
|
||||
mockClient.renameFrom = 'test';
|
||||
mockClient.fs = {
|
||||
get: () => when.resolve(),
|
||||
rename: () => when.resolve()
|
||||
get: () => Promise.resolve(),
|
||||
rename: () => Promise.resolve()
|
||||
};
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
@@ -56,14 +56,14 @@ describe(CMD, function () {
|
||||
mockClient.fs.rename.restore();
|
||||
sandbox.stub(mockClient.fs, 'rename').rejects(new Error('test'));
|
||||
|
||||
return cmdFn({ log: mockLog, command: { arg: 'new' } })
|
||||
return cmdFn({log: mockLog, command: {arg: 'new'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
|
||||
it('new // successful', () => {
|
||||
return cmdFn({ command: { arg: '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,19 +1,19 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const CMD = 'CHMOD';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockLog = { error: () => {} };
|
||||
const mockClient = { reply: () => when.resolve() };
|
||||
const mockLog = {error: () => {}};
|
||||
const mockClient = {reply: () => Promise.resolve()};
|
||||
const cmdFn = require(`../../../../src/commands/registration/site/${CMD.toLowerCase()}`).bind(mockClient);
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
mockClient.fs = {
|
||||
chmod: () => when.resolve()
|
||||
chmod: () => Promise.resolve()
|
||||
};
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
@@ -49,7 +49,7 @@ describe(CMD, function () {
|
||||
mockClient.fs.chmod.restore();
|
||||
sandbox.stub(mockClient.fs, 'chmod').rejects(new Error('test'));
|
||||
|
||||
cmdFn({ log: mockLog, command: { arg: '777 test' } })
|
||||
cmdFn({log: mockLog, command: {arg: '777 test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(500);
|
||||
done();
|
||||
@@ -58,7 +58,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('777 test // successful', done => {
|
||||
cmdFn({ log: mockLog, command: { arg: '777 test' } })
|
||||
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);
|
||||
|
||||
52
test/commands/registration/site/site.spec.js
Normal file
52
test/commands/registration/site/site.spec.js
Normal file
@@ -0,0 +1,52 @@
|
||||
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 CMD = 'SITE';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const log = bunyan.createLogger({name: 'site-test'});
|
||||
const mockClient = {
|
||||
reply: () => Promise.resolve(),
|
||||
commands: new FtpCommands()
|
||||
};
|
||||
const cmdFn = require(`../../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
sandbox.stub(mockClient, 'reply').resolves();
|
||||
});
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('// unsuccessful', () => {
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
it('// successful', () => {
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,19 +1,19 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const CMD = 'SIZE';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockLog = { error: () => {} };
|
||||
const mockClient = { reply: () => when.resolve() };
|
||||
const mockLog = {error: () => {}};
|
||||
const mockClient = {reply: () => Promise.resolve()};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
mockClient.fs = {
|
||||
get: () => when.resolve({size: 1})
|
||||
get: () => Promise.resolve({size: 1})
|
||||
};
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
@@ -43,14 +43,14 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | file get fails', () => {
|
||||
sandbox.stub(mockClient.fs, 'get').rejects(new Error('test'));
|
||||
|
||||
return cmdFn({ log: mockLog, command: { arg: 'test' } })
|
||||
return cmdFn({log: mockLog, command: {arg: 'test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn({ command: { arg: 'test' } })
|
||||
return cmdFn({command: {arg: 'test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(213);
|
||||
});
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const CMD = 'STAT';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockLog = { error: () => {} };
|
||||
const mockClient = { reply: () => when.resolve() };
|
||||
const mockLog = {error: () => {}};
|
||||
const mockClient = {reply: () => Promise.resolve()};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
mockClient.fs = {
|
||||
get: () => when.resolve({}),
|
||||
list: () => when.resolve([])
|
||||
get: () => Promise.resolve({}),
|
||||
list: () => Promise.resolve([])
|
||||
};
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
@@ -33,7 +33,7 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
|
||||
return cmdFn({ command: { arg: 'test' } })
|
||||
return cmdFn({command: {arg: 'test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
@@ -42,7 +42,7 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
|
||||
return cmdFn({ command: { arg: 'test' } })
|
||||
return cmdFn({command: {arg: 'test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
@@ -51,7 +51,7 @@ describe(CMD, function () {
|
||||
it('// unsuccessful | file get fails', () => {
|
||||
sandbox.stub(mockClient.fs, 'get').rejects(new Error('test'));
|
||||
|
||||
return cmdFn({ log: mockLog, command: { arg: 'test' } })
|
||||
return cmdFn({log: mockLog, command: {arg: 'test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(450);
|
||||
});
|
||||
@@ -77,7 +77,7 @@ describe(CMD, function () {
|
||||
isDirectory: () => false
|
||||
});
|
||||
|
||||
return cmdFn({ command: { arg: 'test' } })
|
||||
return cmdFn({command: {arg: 'test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(212);
|
||||
});
|
||||
@@ -122,7 +122,7 @@ describe(CMD, function () {
|
||||
isDirectory: () => true
|
||||
});
|
||||
|
||||
return cmdFn({ command: { arg: 'test' } })
|
||||
return cmdFn({command: {arg: 'test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(213);
|
||||
});
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
const CMD = 'STOR';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
let emitter;
|
||||
const mockClient = {
|
||||
commandSocket: {
|
||||
pause: () => {},
|
||||
resume: () => {}
|
||||
},
|
||||
reply: () => when.resolve(),
|
||||
reply: () => Promise.resolve(),
|
||||
connector: {
|
||||
waitForConnection: () => when.resolve({
|
||||
waitForConnection: () => Promise.resolve({
|
||||
resume: () => {}
|
||||
}),
|
||||
end: () => {}
|
||||
@@ -29,6 +31,11 @@ describe(CMD, function () {
|
||||
write: () => {}
|
||||
};
|
||||
|
||||
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());
|
||||
@@ -53,10 +60,10 @@ describe(CMD, function () {
|
||||
|
||||
it('// unsuccessful | connector times out', () => {
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').callsFake(function () {
|
||||
return when.reject(new when.TimeoutError());
|
||||
return Promise.reject(new Promise.TimeoutError());
|
||||
});
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'} })
|
||||
return cmdFn({log, command: {arg: 'test.txt'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(425);
|
||||
});
|
||||
@@ -64,12 +71,28 @@ describe(CMD, function () {
|
||||
|
||||
it('// unsuccessful | connector errors out', () => {
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').callsFake(function () {
|
||||
return when.reject(new Error('test'));
|
||||
return Promise.reject(new Error('test'));
|
||||
});
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'} })
|
||||
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'));
|
||||
});
|
||||
|
||||
let errorEmitted = false;
|
||||
emitter.once('STOR', err => {
|
||||
errorEmitted = !!err;
|
||||
});
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'}})
|
||||
.then(() => {
|
||||
expect(errorEmitted).to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
@@ -8,7 +8,7 @@ const CMD = 'STOU';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockClient = {
|
||||
reply: () => when.resolve()
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
@@ -16,8 +16,8 @@ describe(CMD, function () {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
mockClient.fs = {
|
||||
get: () => when.resolve(),
|
||||
getUniqueName: () => when.resolve('4')
|
||||
get: () => Promise.resolve(),
|
||||
getUniqueName: () => Promise.resolve('4')
|
||||
};
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
@@ -52,7 +52,7 @@ describe(CMD, function () {
|
||||
mockClient.fs.get.restore();
|
||||
sandbox.stub(mockClient.fs, 'get').rejects({});
|
||||
|
||||
return cmdFn({ command: { arg: 'good' } })
|
||||
return cmdFn({command: {arg: 'good'}})
|
||||
.then(() => {
|
||||
const call = stor.handler.call.args[0][1];
|
||||
expect(call).to.have.property('command');
|
||||
@@ -63,7 +63,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// successful | generates unique name', () => {
|
||||
return cmdFn({ command: { arg: 'bad' } })
|
||||
return cmdFn({command: {arg: 'bad'}})
|
||||
.then(() => {
|
||||
const call = stor.handler.call.args[0][1];
|
||||
expect(call).to.have.property('command');
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
@@ -6,7 +6,7 @@ const CMD = 'STRU';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockClient = {
|
||||
reply: () => when.resolve()
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
@@ -20,14 +20,14 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('// successful', () => {
|
||||
return cmdFn({command: { arg: 'F' } })
|
||||
return cmdFn({command: {arg: 'F'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful', () => {
|
||||
return cmdFn({command: { arg: 'X' } })
|
||||
return cmdFn({command: {arg: 'X'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
@@ -6,7 +6,7 @@ const CMD = 'SYST';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockClient = {
|
||||
reply: () => when.resolve()
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
@@ -6,14 +6,14 @@ const CMD = 'TYPE';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockClient = {
|
||||
reply: () => when.resolve()
|
||||
reply: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
mockClient.encoding = null;
|
||||
mockClient.transferType = null;
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
afterEach(() => {
|
||||
@@ -21,34 +21,34 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('A // successful', () => {
|
||||
return cmdFn({ command: { arg: 'A' } })
|
||||
return cmdFn({command: {arg: 'A'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
expect(mockClient.encoding).to.equal('utf8');
|
||||
expect(mockClient.transferType).to.equal('ascii');
|
||||
});
|
||||
});
|
||||
|
||||
it('I // successful', () => {
|
||||
return cmdFn({ command: { arg: 'I' } })
|
||||
return cmdFn({command: {arg: 'I'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
expect(mockClient.encoding).to.equal('binary');
|
||||
expect(mockClient.transferType).to.equal('binary');
|
||||
});
|
||||
});
|
||||
|
||||
it('L // successful', () => {
|
||||
return cmdFn({ command: { arg: 'L' } })
|
||||
return cmdFn({command: {arg: 'L'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
expect(mockClient.encoding).to.equal('binary');
|
||||
expect(mockClient.transferType).to.equal('binary');
|
||||
});
|
||||
});
|
||||
|
||||
it('X // successful', () => {
|
||||
return cmdFn({ command: { arg: 'X' } })
|
||||
return cmdFn({command: {arg: 'X'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501);
|
||||
expect(mockClient.encoding).to.equal(null);
|
||||
expect(mockClient.transferType).to.equal(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
@@ -9,9 +9,9 @@ describe(CMD, function () {
|
||||
error: () => {}
|
||||
};
|
||||
const mockClient = {
|
||||
reply: () => when.resolve(),
|
||||
server: { options: {} },
|
||||
login: () => when.resolve()
|
||||
reply: () => Promise.resolve(),
|
||||
server: {options: {}},
|
||||
login: () => Promise.resolve()
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
@@ -29,7 +29,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('test // successful | prompt for password', () => {
|
||||
return cmdFn({ command: { arg: 'test' } })
|
||||
return cmdFn({command: {arg: 'test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(331);
|
||||
});
|
||||
@@ -38,7 +38,7 @@ describe(CMD, function () {
|
||||
it('test // successful | anonymous login', () => {
|
||||
mockClient.server.options = {anonymous: true};
|
||||
|
||||
return cmdFn({ command: { arg: 'anonymous' } })
|
||||
return cmdFn({command: {arg: 'anonymous'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(230);
|
||||
expect(mockClient.login.callCount).to.equal(1);
|
||||
@@ -46,7 +46,7 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
it('test // unsuccessful | no username provided', () => {
|
||||
return cmdFn({ command: { } })
|
||||
return cmdFn({command: { }})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501);
|
||||
expect(mockClient.login.callCount).to.equal(0);
|
||||
@@ -56,7 +56,7 @@ describe(CMD, function () {
|
||||
it('test // unsuccessful | already set username', () => {
|
||||
mockClient.username = 'test';
|
||||
|
||||
return cmdFn({ command: { arg: 'test' } })
|
||||
return cmdFn({command: {arg: 'test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(530);
|
||||
expect(mockClient.login.callCount).to.equal(0);
|
||||
@@ -66,7 +66,7 @@ describe(CMD, function () {
|
||||
it('test // successful | regular login if anonymous is true', () => {
|
||||
mockClient.server.options = {anonymous: true};
|
||||
|
||||
return cmdFn({ log: mockLog, command: { arg: 'test' } })
|
||||
return cmdFn({log: mockLog, command: {arg: 'test'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(331);
|
||||
expect(mockClient.login.callCount).to.equal(0);
|
||||
@@ -76,7 +76,7 @@ describe(CMD, function () {
|
||||
it('test // successful | anonymous login with set username', () => {
|
||||
mockClient.server.options = {anonymous: 'sillyrabbit'};
|
||||
|
||||
return cmdFn({ log: mockLog, command: { arg: 'sillyrabbit' } })
|
||||
return cmdFn({log: mockLog, command: {arg: 'sillyrabbit'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(230);
|
||||
expect(mockClient.login.callCount).to.equal(1);
|
||||
@@ -88,7 +88,7 @@ describe(CMD, function () {
|
||||
mockClient.login.restore();
|
||||
sandbox.stub(mockClient, 'login').rejects(new Error('test'));
|
||||
|
||||
return cmdFn({ log: mockLog, command: { arg: 'anonymous' } })
|
||||
return cmdFn({log: mockLog, command: {arg: 'anonymous'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(530);
|
||||
expect(mockClient.login.callCount).to.equal(1);
|
||||
@@ -98,7 +98,7 @@ describe(CMD, function () {
|
||||
it('test // successful | does not login if already authenticated', () => {
|
||||
mockClient.authenticated = true;
|
||||
|
||||
return cmdFn({ log: mockLog, command: { arg: 'sillyrabbit' } })
|
||||
return cmdFn({log: mockLog, command: {arg: 'sillyrabbit'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(230);
|
||||
expect(mockClient.login.callCount).to.equal(0);
|
||||
|
||||
@@ -66,7 +66,7 @@ describe('Connector - Active //', function () {
|
||||
|
||||
it('upgrades to a secure connection', function () {
|
||||
mockConnection.secure = true;
|
||||
mockConnection.server = { _tls: {} };
|
||||
mockConnection.server = {_tls: {}};
|
||||
|
||||
return active.setupConnection('127.0.0.1', PORT)
|
||||
.then(() => {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const when = require('when');
|
||||
const Promise = require('bluebird');
|
||||
const net = require('net');
|
||||
const bunyan = require('bunyan');
|
||||
|
||||
@@ -11,12 +11,12 @@ const PassiveConnector = require('../../src/connector/passive');
|
||||
describe('Connector - Passive //', function () {
|
||||
let passive;
|
||||
let mockConnection = {
|
||||
reply: () => when.resolve({}),
|
||||
close: () => when.resolve({}),
|
||||
reply: () => Promise.resolve({}),
|
||||
close: () => Promise.resolve({}),
|
||||
encoding: 'utf8',
|
||||
log: bunyan.createLogger({name: 'passive-test'}),
|
||||
commandSocket: {},
|
||||
server: { options: {} }
|
||||
server: {options: {}}
|
||||
};
|
||||
let sandbox;
|
||||
|
||||
@@ -64,7 +64,7 @@ describe('Connector - Passive //', function () {
|
||||
return passive.setupServer()
|
||||
.then(shouldNotResolve)
|
||||
.catch(err => {
|
||||
expect(err.name).to.equal('RangeError');
|
||||
expect(err).to.be.instanceOf(RangeError);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const moment = require('moment');
|
||||
|
||||
const fileStat = require('../../src/helpers/file-stat');
|
||||
const errors = require('../../src/errors');
|
||||
|
||||
describe('helpers // file-stat', function () {
|
||||
let sandbox;
|
||||
|
||||
before(function () {
|
||||
sandbox = sinon.sandbox.create();
|
||||
});
|
||||
afterEach(function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
const STAT = {
|
||||
name: 'test1',
|
||||
dev: 2114,
|
||||
@@ -44,6 +55,11 @@ describe('helpers // file-stat', function () {
|
||||
|
||||
describe('format - ls //', function () {
|
||||
it('formats correctly', () => {
|
||||
const momentStub = sandbox.stub(moment, 'utc').callThrough();
|
||||
momentStub.onFirstCall().callsFake(function () {
|
||||
return moment.utc(new Date('Sept 10 2016'));
|
||||
});
|
||||
|
||||
const format = fileStat(STAT, 'ls');
|
||||
expect(format).to.equal('-rwxrwxrwx 1 85 100 527 Oct 10 23:24 test1');
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user