Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
63777c0d74 | ||
|
|
9be8ffa60d | ||
|
|
b8cd6022e1 | ||
|
|
0618a3c675 | ||
|
|
3c533a5fbc | ||
|
|
3d0a58ca15 | ||
|
|
4b4c809af8 | ||
|
|
a234534de0 | ||
|
|
635fb35341 | ||
|
|
51a6448ac2 | ||
|
|
4d8a69615c | ||
|
|
ab5a2e9641 | ||
|
|
a7f25accd2 | ||
|
|
c49a361c36 | ||
|
|
d3d65aa5cf | ||
|
|
e53848f881 | ||
|
|
b5cf75b09f | ||
|
|
dd0a790519 | ||
|
|
e25ee55865 | ||
|
|
0433bb48cd | ||
|
|
350c2b3e81 | ||
|
|
887bf1fa58 | ||
|
|
70a62f1da1 | ||
|
|
3a1afdb694 | ||
|
|
10127b32e5 | ||
|
|
e1aad3e021 | ||
|
|
a254e6c5f3 | ||
|
|
c46d6086ea |
13
.travis.yml
13
.travis.yml
@@ -1,6 +1,7 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "6"
|
||||
# - "node"
|
||||
|
||||
env:
|
||||
FTP_URL: ftp://127.0.0.1:8880
|
||||
@@ -12,5 +13,13 @@ script:
|
||||
- npm run verify:js
|
||||
- npm run test:coverage
|
||||
|
||||
after_success:
|
||||
- if [ $TRAVIS_BRANCH = 'master' ]; then npm run semantic-release; fi
|
||||
after_script:
|
||||
- npm run upload-coverage
|
||||
|
||||
deploy:
|
||||
skip_cleanup: true
|
||||
provider: script
|
||||
script: npm run semantic-release
|
||||
on:
|
||||
branch: master
|
||||
node: "6"
|
||||
|
||||
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.
|
||||
|
||||
44
README.md
44
README.md
@@ -1,12 +1,13 @@
|
||||

|
||||
|
||||
[](https://badge.fury.io/js/ftp-srv) [](https://travis-ci.org/trs/ftp-srv) [](https://github.com/semantic-release/semantic-release) [](http://commitizen.github.io/cz-cli/)
|
||||
[](https://github.com/trs/ftp-srv)
|
||||
|
||||
<!--[RM_DESCRIPTION]-->
|
||||
> Modern, extensible FTP Server
|
||||
|
||||
<!--[]-->
|
||||
|
||||
[](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/)
|
||||
|
||||
---
|
||||
|
||||
- [Overview](#overview)
|
||||
@@ -149,11 +150,26 @@ Occurs when an error arises in the client connection.
|
||||
See the [command registry](src/commands/registration) for a list of all implemented FTP commands.
|
||||
|
||||
## File System
|
||||
The default file system can be overwritten to use your own implementation.
|
||||
The default [file system](src/fs.js) can be overwritten to use your own implementation.
|
||||
This can allow for virtual file systems, and more.
|
||||
Each connection can set it's own file system based on the user.
|
||||
|
||||
Custom file systems can implement the following variables depending on the developers needs.
|
||||
The default file system is exported and can be extended as needed:
|
||||
```js
|
||||
const {FtpSrv, FileSystem} = require('ftp-srv');
|
||||
|
||||
class MyFileSystem extends FileSystem {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
}
|
||||
|
||||
get(fileName) {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Custom file systems can implement the following variables depending on the developers needs:
|
||||
|
||||
### Methods
|
||||
#### [`currentDirectory()`](src/fs.js#L29)
|
||||
@@ -162,11 +178,11 @@ __Used in:__ `PWD`
|
||||
|
||||
#### [`get(fileName)`](src/fs.js#L33)
|
||||
Returns a file stat object of file or directory
|
||||
__Used in:__ `STAT`, `SIZE`, `RNFR`, `MDTM`
|
||||
__Used in:__ `LIST`, `NLST`, `STAT`, `SIZE`, `RNFR`, `MDTM`
|
||||
|
||||
#### [`list(path)`](src/fs.js#L39)
|
||||
Returns array of file and directory stat objects
|
||||
__Used in:__ `LIST`, `STAT`
|
||||
__Used in:__ `LIST`, `NLST`, `STAT`
|
||||
|
||||
#### [`chdir(path)`](src/fs.js#L56)
|
||||
Returns new directory relative to current directory
|
||||
@@ -176,13 +192,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)
|
||||
@@ -190,11 +210,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)
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -47,6 +47,7 @@ parserOptions:
|
||||
<<: *confit-parserOptions
|
||||
|
||||
rules:
|
||||
no-process-exit: 0
|
||||
max-len:
|
||||
- warn
|
||||
- 200 # Line Length
|
||||
|
||||
62
ftp-srv.d.ts
vendored
Normal file
62
ftp-srv.d.ts
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
declare class FileSystem {
|
||||
constructor(connection: any, {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;
|
||||
}
|
||||
|
||||
declare class FtpServer {
|
||||
constructor(url: string, options?: {});
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
declare const FtpSrv: FtpServer;
|
||||
export default FtpServer;
|
||||
6
ftp-srv.js
Normal file
6
ftp-srv.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const FtpSrv = require('./src');
|
||||
const FileSystem = require('./src/fs');
|
||||
|
||||
module.exports = FtpSrv;
|
||||
module.exports.FtpSrv = FtpSrv;
|
||||
module.exports.FileSystem = FileSystem;
|
||||
43
logo.html
43
logo.html
@@ -1,43 +0,0 @@
|
||||
<!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/icon?family=Material+Icons"
|
||||
rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
padding: 0px;
|
||||
}
|
||||
h1 {
|
||||
display: flex;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 73px;
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
font-weight: bold;
|
||||
line-height: 0.9em;
|
||||
letter-spacing: -3px;
|
||||
color: #444;
|
||||
text-shadow:
|
||||
1px 0px 1px #ccc, 0px 1px 1px #eee,
|
||||
2px 1px 1px #ccc, 1px 2px 1px #eee,
|
||||
3px 2px 1px #ccc, 2px 3px 1px #eee,
|
||||
4px 3px 1px #ccc, 3px 4px 1px #eee,
|
||||
5px 4px 1px #ccc, 4px 5px 1px #eee,
|
||||
6px 5px 1px #ccc, 5px 6px 1px #eee,
|
||||
7px 6px 1px #ccc;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
h1 > i {
|
||||
font-size: 42px !important;
|
||||
color: #03A9F4;
|
||||
align-self: center;
|
||||
padding-top: 15px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1><i class="material-icons">markunread_mailbox</i>ftp-srv</h1>
|
||||
</body>
|
||||
</html>
|
||||
BIN
logo.png
BIN
logo.png
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 104 KiB |
17
logo/generate.js
Normal file
17
logo/generate.js
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
Send Button by Bruno Bosse from the Noun Project
|
||||
https://thenounproject.com/brunobosse/collection/basics/?i=1054386
|
||||
*/
|
||||
|
||||
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
|
||||
});
|
||||
|
||||
rs.pipe(ws);
|
||||
ws.on('finish', () => process.exit());
|
||||
81
logo/icon.svg
Normal file
81
logo/icon.svg
Normal file
@@ -0,0 +1,81 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 21 KiB |
47
logo/logo.html
Normal file
47
logo/logo.html
Normal file
@@ -0,0 +1,47 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:400,700" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
h1 {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
margin-left: -4px;
|
||||
padding: 0px;
|
||||
font-size: 68px;
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
font-weight: bold;
|
||||
line-height: 0.8em;
|
||||
letter-spacing: -3px;
|
||||
color: #333;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
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;
|
||||
}
|
||||
h1 > span {
|
||||
display: block;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
padding-top: 6px;
|
||||
}
|
||||
img {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>
|
||||
<span>ftp-srv</span>
|
||||
</h1>
|
||||
<img src="icon.svg" width="76px" height="76px" />
|
||||
</body>
|
||||
</html>
|
||||
4330
package-lock.json
generated
Normal file
4330
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
41
package.json
41
package.json
@@ -8,10 +8,16 @@
|
||||
"ftp-srv",
|
||||
"ftp-svr",
|
||||
"ftpd",
|
||||
"server"
|
||||
"server",
|
||||
"ftpserver"
|
||||
],
|
||||
"license": "MIT",
|
||||
"main": "src/index.js",
|
||||
"main": "ftp-srv.js",
|
||||
"files": [
|
||||
"src",
|
||||
"ftp-srv.d.ts"
|
||||
],
|
||||
"types": "./ftp-srv.d.ts",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/trs/ftp-srv"
|
||||
@@ -46,32 +52,33 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"bunyan": "^1.8.9",
|
||||
"bunyan": "^1.8.10",
|
||||
"lodash": "^4.17.4",
|
||||
"moment": "^2.18.1",
|
||||
"uuid": "^3.0.1",
|
||||
"uuid": "^3.1.0",
|
||||
"when": "^3.7.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^3.5.0",
|
||||
"chai": "^4.0.2",
|
||||
"chokidar-cli": "1.2.0",
|
||||
"coveralls": "2.11.15",
|
||||
"cross-env": "3.1.4",
|
||||
"cz-customizable": "4.0.0",
|
||||
"coveralls": "2.13.1",
|
||||
"cross-env": "5.0.1",
|
||||
"cz-customizable": "5.0.0",
|
||||
"cz-customizable-ghooks": "1.5.0",
|
||||
"dotenv": "^4.0.0",
|
||||
"eslint": "3.14.1",
|
||||
"eslint-config-google": "0.7.1",
|
||||
"eslint-plugin-node": "3.0.5",
|
||||
"eslint": "3.19.0",
|
||||
"eslint-config-google": "0.8.0",
|
||||
"eslint-plugin-node": "5.0.0",
|
||||
"ftp": "^0.3.10",
|
||||
"husky": "0.13.1",
|
||||
"html-convert": "^2.1.7",
|
||||
"husky": "0.13.4",
|
||||
"istanbul": "0.4.5",
|
||||
"mocha": "3.2.0",
|
||||
"mocha": "3.4.2",
|
||||
"mocha-pretty-bunyan-nyan": "^1.0.4",
|
||||
"npm-run-all": "4.0.1",
|
||||
"rimraf": "2.5.4",
|
||||
"semantic-release": "^6.3.2",
|
||||
"sinon": "^2.1.0"
|
||||
"npm-run-all": "4.0.2",
|
||||
"rimraf": "2.6.1",
|
||||
"semantic-release": "^6.3.6",
|
||||
"sinon": "^2.3.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.x",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -8,18 +8,16 @@ module.exports = {
|
||||
directive: 'LIST',
|
||||
handler: function ({log, command} = {}) {
|
||||
if (!this.fs) return this.reply(550, 'File system not instantiated');
|
||||
if (!this.fs.get) return this.reply(402, 'Not supported by file system');
|
||||
if (!this.fs.list) return this.reply(402, 'Not supported by file system');
|
||||
|
||||
const simple = command.directive === 'NLST';
|
||||
|
||||
let dataSocket;
|
||||
const directory = command.arg || '.';
|
||||
const path = command.arg || '.';
|
||||
return this.connector.waitForConnection()
|
||||
.then(socket => {
|
||||
this.commandSocket.pause();
|
||||
dataSocket = socket;
|
||||
})
|
||||
.then(() => when.try(this.fs.list.bind(this.fs), directory))
|
||||
.tap(() => this.commandSocket.pause())
|
||||
.then(() => when.try(this.fs.get.bind(this.fs), path))
|
||||
.then(stat => stat.isDirectory() ? when.try(this.fs.list.bind(this.fs), path) : [stat])
|
||||
.then(files => {
|
||||
const getFileMessage = file => {
|
||||
if (simple) return file.name;
|
||||
@@ -31,7 +29,7 @@ module.exports = {
|
||||
return {
|
||||
raw: true,
|
||||
message,
|
||||
socket: dataSocket
|
||||
socket: this.connector.socket
|
||||
};
|
||||
});
|
||||
return this.reply(150)
|
||||
|
||||
@@ -1,8 +1,34 @@
|
||||
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] = []) {
|
||||
switch (_.toUpper(setting)) {
|
||||
case 'ON':
|
||||
this.encoding = 'utf8';
|
||||
return this.reply(200, 'UTF8 encoding on');
|
||||
case 'OFF':
|
||||
this.encoding = 'ascii';
|
||||
return this.reply(200, 'UTF8 encoding off');
|
||||
default:
|
||||
return this.reply(501, 'Unknown setting for option');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
module.exports = {
|
||||
directive: 'PBSZ',
|
||||
handler: function ({command} = {}) {
|
||||
if (!this.server._tls || !this.secure) return this.reply(202, 'Not suppored');
|
||||
if (!this.secure) return this.reply(202, 'Not suppored');
|
||||
this.bufferSize = parseInt(command.arg, 10);
|
||||
return this.reply(200, this.bufferSize === 0 ? 'OK' : 'Buffer too large: PBSZ=0');
|
||||
},
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Protection Buffer Size',
|
||||
flags: {
|
||||
no_auth: true
|
||||
no_auth: true,
|
||||
feat: 'PBSZ'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ const _ = require('lodash');
|
||||
module.exports = {
|
||||
directive: 'PROT',
|
||||
handler: function ({command} = {}) {
|
||||
if (!this.server._tls || !this.secure) return this.reply(202, 'Not suppored');
|
||||
if (!this.secure) return this.reply(202, 'Not suppored');
|
||||
if (!this.bufferSize && typeof this.bufferSize !== 'number') return this.reply(503);
|
||||
|
||||
switch (_.toUpper(command.arg)) {
|
||||
@@ -17,6 +17,7 @@ module.exports = {
|
||||
syntax: '{{cmd}}',
|
||||
description: 'Data Channel Protection Level',
|
||||
flags: {
|
||||
no_auth: true
|
||||
no_auth: true,
|
||||
feat: 'PROT'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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, `Resarting next transfer at ${byteCount}`);
|
||||
},
|
||||
syntax: '{{cmd}} <byte-count>',
|
||||
description: 'Restart transfer from the specified point. Resets after any STORE or RETRIEVE'
|
||||
};
|
||||
@@ -6,23 +6,26 @@ 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))
|
||||
.tap(() => this.commandSocket.pause())
|
||||
.then(() => when.try(this.fs.read.bind(this.fs), command.arg, {start: this.restByteCount}))
|
||||
.then(stream => {
|
||||
return when.promise((resolve, reject) => {
|
||||
dataSocket.on('error', err => stream.emit('error', err));
|
||||
this.restByteCount = 0;
|
||||
|
||||
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());
|
||||
const eventsPromise = when.promise((resolve, reject) => {
|
||||
this.connector.socket.once('error', err => reject(err));
|
||||
|
||||
stream.on('data', data => this.connector.socket
|
||||
&& this.connector.socket.write(data, this.transferType));
|
||||
stream.once('error', err => reject(err));
|
||||
stream.once('end', () => resolve());
|
||||
});
|
||||
|
||||
return this.reply(150).then(() => this.connector.socket.resume())
|
||||
.then(() => eventsPromise)
|
||||
.finally(() => stream.destroy ? stream.destroy() : null);
|
||||
})
|
||||
.then(() => this.reply(226))
|
||||
.catch(when.TimeoutError, err => {
|
||||
log.error(err);
|
||||
return this.reply(425, 'No connection established');
|
||||
|
||||
@@ -9,23 +9,32 @@ 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(() => when.try(this.fs.write.bind(this.fs), fileName, {append, start: this.restByteCount}))
|
||||
.then(stream => {
|
||||
return when.promise((resolve, reject) => {
|
||||
stream.on('error', err => dataSocket.emit('error', err));
|
||||
this.restByteCount = 0;
|
||||
|
||||
dataSocket.on('end', () => stream.end(() => resolve(this.reply(226, fileName))));
|
||||
dataSocket.on('error', err => reject(err));
|
||||
dataSocket.on('data', data => stream.write(data, this.encoding));
|
||||
this.reply(150).then(() => dataSocket.resume());
|
||||
const streamPromise = when.promise((resolve, reject) => {
|
||||
stream.once('error', err => reject(err));
|
||||
stream.once('finish', () => resolve());
|
||||
});
|
||||
|
||||
const socketPromise = when.promise((resolve, reject) => {
|
||||
this.connector.socket.on('data', data => stream.write(data, this.transferType));
|
||||
this.connector.socket.once('end', () => {
|
||||
if (stream.listenerCount('close')) stream.emit('close');
|
||||
else stream.end();
|
||||
resolve();
|
||||
});
|
||||
this.connector.socket.once('error', err => reject(err));
|
||||
});
|
||||
|
||||
return this.reply(150).then(() => this.connector.socket.resume())
|
||||
.then(() => when.join(streamPromise, socketPromise))
|
||||
.finally(() => stream.destroy ? stream.destroy() : null);
|
||||
})
|
||||
.then(() => this.reply(226, fileName))
|
||||
.catch(when.TimeoutError, err => {
|
||||
log.error(err);
|
||||
return this.reply(425, 'No connection established');
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
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'),
|
||||
|
||||
@@ -15,8 +15,11 @@ class FtpConnection {
|
||||
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);
|
||||
|
||||
@@ -34,7 +37,7 @@ class FtpConnection {
|
||||
}
|
||||
|
||||
_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)));
|
||||
}
|
||||
@@ -47,6 +50,20 @@ class FtpConnection {
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -58,7 +75,7 @@ class FtpConnection {
|
||||
return when.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});
|
||||
}
|
||||
@@ -102,7 +119,7 @@ class FtpConnection {
|
||||
const packet = !letter.raw ? _.compact([letter.code || options.code, letter.message]).join(seperator) : letter.message;
|
||||
|
||||
if (letter.socket && letter.socket.writable) {
|
||||
this.log.trace({port: letter.socket.address().port, packet}, 'Reply');
|
||||
this.log.trace({port: letter.socket.address().port, encoding: letter.encoding, packet}, 'Reply');
|
||||
letter.socket.write(packet + '\r\n', letter.encoding, err => {
|
||||
if (err) {
|
||||
this.log.error(err);
|
||||
|
||||
@@ -26,7 +26,7 @@ class Active extends Connector {
|
||||
return closeExistingServer()
|
||||
.then(() => {
|
||||
this.dataSocket = new Socket();
|
||||
this.dataSocket.setEncoding(this.encoding);
|
||||
this.dataSocket.setEncoding(this.connection.transferType);
|
||||
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.pause();
|
||||
|
||||
@@ -14,6 +14,10 @@ class Connector {
|
||||
return this.connection.log;
|
||||
}
|
||||
|
||||
get socket() {
|
||||
return this.dataSocket;
|
||||
}
|
||||
|
||||
get server() {
|
||||
return this.connection.server;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,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,10 +54,10 @@ class Passive extends Connector {
|
||||
this.dataSocket = socket;
|
||||
}
|
||||
this.dataSocket.connected = true;
|
||||
this.dataSocket.setEncoding(this.connection.encoding);
|
||||
this.dataSocket.setEncoding(this.connection.transferType);
|
||||
this.dataSocket.on('error', err => 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();
|
||||
});
|
||||
};
|
||||
@@ -67,7 +67,7 @@ class Passive extends Connector {
|
||||
this.dataServer.maxConnections = 1;
|
||||
this.dataServer.on('error', err => 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;
|
||||
});
|
||||
|
||||
@@ -75,7 +75,7 @@ class Passive extends Connector {
|
||||
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);
|
||||
}
|
||||
});
|
||||
@@ -89,7 +89,8 @@ class Passive extends Connector {
|
||||
this.server.options.pasv_range.split('-').map(v => v ? parseInt(v) : v) :
|
||||
[this.server.options.pasv_range];
|
||||
return findPort(min, max);
|
||||
} else return undefined;
|
||||
}
|
||||
throw new errors.ConnectorError('Invalid pasv_range');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
11
src/fs.js
11
src/fs.js
@@ -65,21 +65,22 @@ 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.on('error', () => fs.unlink(fsPath));
|
||||
const stream = syncFs.createWriteStream(fsPath, {flags: !append ? 'w+' : 'a+', start});
|
||||
stream.once('error', () => fs.unlink(fsPath));
|
||||
stream.once('close', () => stream.end());
|
||||
return stream;
|
||||
}
|
||||
|
||||
read(fileName) {
|
||||
read(fileName, {start = undefined} = {}) {
|
||||
const {fsPath} = this._resolvePath(fileName);
|
||||
return fs.stat(fsPath)
|
||||
.tap(stat => {
|
||||
if (stat.isDirectory()) throw new errors.FileSystemError('Cannot read a directory');
|
||||
})
|
||||
.then(() => {
|
||||
const stream = syncFs.createReadStream(fsPath, {flags: 'r'});
|
||||
const stream = syncFs.createReadStream(fsPath, {flags: 'r', start});
|
||||
return stream;
|
||||
});
|
||||
}
|
||||
|
||||
21
src/index.js
21
src/index.js
@@ -47,17 +47,13 @@ class FtpServer {
|
||||
|
||||
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() {
|
||||
@@ -94,8 +90,8 @@ class FtpServer {
|
||||
}
|
||||
|
||||
setupTLS(_tls) {
|
||||
if (!tls) return false;
|
||||
return _.assign(_tls, {
|
||||
if (!_tls) return false;
|
||||
return _.assign({}, _tls, {
|
||||
cert: _tls.cert ? fs.readFileSync(_tls.cert) : undefined,
|
||||
key: _tls.key ? fs.readFileSync(_tls.key) : undefined,
|
||||
ca: _tls.ca ? Array.isArray(_tls.ca) ? _tls.ca.map(_ca => fs.readFileSync(_ca)) : [fs.readFileSync(_tls.ca)] : undefined
|
||||
@@ -134,10 +130,15 @@ class FtpServer {
|
||||
});
|
||||
}
|
||||
|
||||
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))
|
||||
return when.map(Object.keys(this.connections), id => when.try(this.disconnectClient.bind(this), id))
|
||||
.then(() => when.promise(resolve => {
|
||||
this.server.close(err => {
|
||||
if (err) this.log.error(err, 'Error closing server');
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -25,29 +25,25 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('// successful | no active connection', done => {
|
||||
it('// successful | no active connection', () => {
|
||||
mockClient.connector.waitForConnection.restore();
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').rejects();
|
||||
|
||||
cmdFn()
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.connector.waitForConnection.callCount).to.equal(1);
|
||||
expect(mockClient.connector.end.callCount).to.equal(0);
|
||||
expect(mockClient.reply.args[0][0]).to.equal(226);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// successful | active connection', done => {
|
||||
cmdFn()
|
||||
it('// successful | active connection', () => {
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.connector.waitForConnection.callCount).to.equal(1);
|
||||
expect(mockClient.connector.end.callCount).to.equal(1);
|
||||
expect(mockClient.reply.args[0][0]).to.equal(426);
|
||||
expect(mockClient.reply.args[1][0]).to.equal(226);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,12 +19,10 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('// successful', done => {
|
||||
cmdFn()
|
||||
it('// successful', () => {
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(202);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,31 +22,25 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('TLS // supported', done => {
|
||||
cmdFn({command: { arg: 'TLS', directive: CMD}})
|
||||
it('TLS // supported', () => {
|
||||
return cmdFn({command: { arg: 'TLS', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(234);
|
||||
expect(mockClient.secure).to.equal(true);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('SSL // not supported', done => {
|
||||
cmdFn({command: { arg: 'SSL', directive: CMD}})
|
||||
it('SSL // not supported', () => {
|
||||
return cmdFn({command: { arg: 'SSL', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('bad // bad', done => {
|
||||
cmdFn({command: { arg: 'bad', directive: CMD}})
|
||||
it('bad // bad', () => {
|
||||
return cmdFn({command: { arg: 'bad', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -25,13 +25,11 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('.. // successful', done => {
|
||||
cmdFn({log, command: {directive: CMD}})
|
||||
it('.. // successful', () => {
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(250);
|
||||
expect(mockClient.fs.chdir.args[0][0]).to.equal('..');
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,63 +23,56 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', done => {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = { reply: () => {} };
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
badCmdFn()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on no fs chdir command', done => {
|
||||
it('fails on no fs chdir command', () => {
|
||||
const badMockClient = { reply: () => {}, fs: {} };
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
badCmdFn()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('test // successful', done => {
|
||||
cmdFn({log, command: { arg: 'test', directive: CMD}})
|
||||
it('test // successful', () => {
|
||||
return cmdFn({log, command: { arg: 'test', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(250);
|
||||
expect(mockClient.fs.chdir.args[0][0]).to.equal('test');
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('test // successful', done => {
|
||||
it('test // successful', () => {
|
||||
mockClient.fs.chdir.restore();
|
||||
sandbox.stub(mockClient.fs, 'chdir').resolves('/test');
|
||||
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');
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('bad // unsuccessful', done => {
|
||||
it('bad // unsuccessful', () => {
|
||||
mockClient.fs.chdir.restore();
|
||||
sandbox.stub(mockClient.fs, 'chdir').rejects(new Error('Bad'));
|
||||
|
||||
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');
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,51 +23,45 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', done => {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = { reply: () => {} };
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
badCmdFn()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on no fs delete command', done => {
|
||||
it('fails on no fs delete command', () => {
|
||||
const badMockClient = { reply: () => {}, fs: {} };
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
badCmdFn()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('test // successful', done => {
|
||||
cmdFn({log, command: { arg: 'test', directive: CMD}})
|
||||
it('test // successful', () => {
|
||||
return cmdFn({log, command: { arg: 'test', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(250);
|
||||
expect(mockClient.fs.delete.args[0][0]).to.equal('test');
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('bad // unsuccessful', done => {
|
||||
it('bad // unsuccessful', () => {
|
||||
mockClient.fs.delete.restore();
|
||||
sandbox.stub(mockClient.fs, 'delete').rejects(new Error('Bad'));
|
||||
|
||||
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');
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,39 +19,31 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('// successful', done => {
|
||||
cmdFn({command: { directive: CMD }})
|
||||
it('// successful', () => {
|
||||
return cmdFn({command: { directive: CMD }})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(211);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('help // successful', done => {
|
||||
cmdFn({command: { arg: 'help', directive: CMD}})
|
||||
it('help // successful', () => {
|
||||
return cmdFn({command: { arg: 'help', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(214);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('help // successful', done => {
|
||||
cmdFn({command: { arg: 'allo', directive: CMD}})
|
||||
it('allo // successful', () => {
|
||||
return cmdFn({command: { arg: 'allo', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(214);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('bad // unsuccessful', done => {
|
||||
cmdFn({command: { arg: 'bad', directive: CMD}})
|
||||
it('bad // unsuccessful', () => {
|
||||
return cmdFn({command: { arg: 'bad', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(502);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,7 +9,10 @@ describe(CMD, function () {
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
fs: { list: () => {} },
|
||||
fs: {
|
||||
list: () => {},
|
||||
get: () => {}
|
||||
},
|
||||
connector: {
|
||||
waitForConnection: () => when({}),
|
||||
end: () => {}
|
||||
@@ -25,6 +28,24 @@ describe(CMD, function () {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
sandbox.stub(mockClient, 'reply').resolves();
|
||||
sandbox.stub(mockClient.fs, 'get').resolves({
|
||||
name: 'testdir',
|
||||
dev: 2114,
|
||||
ino: 48064969,
|
||||
mode: 33188,
|
||||
nlink: 1,
|
||||
uid: 85,
|
||||
gid: 100,
|
||||
rdev: 0,
|
||||
size: 527,
|
||||
blksize: 4096,
|
||||
blocks: 8,
|
||||
atime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
mtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => true
|
||||
});
|
||||
sandbox.stub(mockClient.fs, 'list').resolves([{
|
||||
name: 'test1',
|
||||
dev: 2114,
|
||||
@@ -42,6 +63,23 @@ describe(CMD, function () {
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => false
|
||||
}, {
|
||||
name: 'test2',
|
||||
dev: 2114,
|
||||
ino: 48064969,
|
||||
mode: 33188,
|
||||
nlink: 1,
|
||||
uid: 85,
|
||||
gid: 100,
|
||||
rdev: 0,
|
||||
size: 527,
|
||||
blksize: 4096,
|
||||
blocks: 8,
|
||||
atime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
mtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => true
|
||||
}]);
|
||||
});
|
||||
afterEach(() => {
|
||||
@@ -49,64 +87,89 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', done => {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = { reply: () => {} };
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
badCmdFn()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on no fs list command', done => {
|
||||
it('fails on no fs list command', () => {
|
||||
const badMockClient = { reply: () => {}, fs: {} };
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
badCmdFn()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('. // successful', done => {
|
||||
cmdFn({log, command: {directive: CMD}})
|
||||
it('. // successful', () => {
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(150);
|
||||
expect(mockClient.reply.args[1].length).to.equal(3);
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('raw');
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('message');
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('socket');
|
||||
expect(mockClient.reply.args[2][0]).to.equal(226);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('. // unsuccessful', done => {
|
||||
it('testfile.txt // successful', () => {
|
||||
mockClient.fs.get.restore();
|
||||
sandbox.stub(mockClient.fs, 'get').resolves({
|
||||
name: 'testfile.txt',
|
||||
dev: 2114,
|
||||
ino: 48064969,
|
||||
mode: 33188,
|
||||
nlink: 1,
|
||||
uid: 85,
|
||||
gid: 100,
|
||||
rdev: 0,
|
||||
size: 527,
|
||||
blksize: 4096,
|
||||
blocks: 8,
|
||||
atime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
mtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => false
|
||||
});
|
||||
|
||||
return cmdFn({log, command: {directive: CMD, arg: 'testfile.txt'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(150);
|
||||
expect(mockClient.reply.args[1].length).to.equal(2);
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('raw');
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('message');
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('socket');
|
||||
expect(mockClient.reply.args[2][0]).to.equal(226);
|
||||
});
|
||||
});
|
||||
|
||||
it('. // unsuccessful', () => {
|
||||
mockClient.fs.list.restore();
|
||||
sandbox.stub(mockClient.fs, 'list').rejects(new Error());
|
||||
|
||||
cmdFn({log, command: {directive: CMD}})
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(451);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('. // unsuccessful (timeout)', done => {
|
||||
it('. // unsuccessful (timeout)', () => {
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').returns(when.reject(new when.TimeoutError()));
|
||||
|
||||
cmdFn({log, command: {directive: CMD}})
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(425);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,50 +23,44 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', done => {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = { reply: () => {} };
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
badCmdFn()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on no fs get command', done => {
|
||||
it('fails on no fs get command', () => {
|
||||
const badMockClient = { reply: () => {}, fs: {} };
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
badCmdFn()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('. // successful', done => {
|
||||
cmdFn({log, command: {directive: CMD}})
|
||||
it('. // successful', () => {
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(213);
|
||||
//expect(mockClient.reply.args[0][1]).to.equal('20111010172411.000');
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('. // unsuccessful', done => {
|
||||
it('. // unsuccessful', () => {
|
||||
mockClient.fs.get.restore();
|
||||
sandbox.stub(mockClient.fs, 'get').rejects(new Error());
|
||||
|
||||
cmdFn({log, command: {directive: CMD}})
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,63 +23,56 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', done => {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = { reply: () => {} };
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
badCmdFn()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on no fs mkdir command', done => {
|
||||
it('fails on no fs mkdir command', () => {
|
||||
const badMockClient = { reply: () => {}, fs: {} };
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
badCmdFn()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('test // successful', done => {
|
||||
cmdFn({log, command: {arg: 'test', directive: CMD}})
|
||||
it('test // successful', () => {
|
||||
return cmdFn({log, command: {arg: 'test', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(257);
|
||||
expect(mockClient.fs.mkdir.args[0][0]).to.equal('test');
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('test // successful', done => {
|
||||
it('test // successful', () => {
|
||||
mockClient.fs.mkdir.restore();
|
||||
sandbox.stub(mockClient.fs, 'mkdir').resolves('test');
|
||||
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);
|
||||
expect(mockClient.fs.mkdir.args[0][0]).to.equal('test');
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('bad // unsuccessful', done => {
|
||||
it('bad // unsuccessful', () => {
|
||||
mockClient.fs.mkdir.restore();
|
||||
sandbox.stub(mockClient.fs, 'mkdir').rejects(new Error('Bad'));
|
||||
|
||||
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.mkdir.args[0][0]).to.equal('bad');
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,21 +19,17 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('S // successful', done => {
|
||||
cmdFn({command: {arg: 'S'}})
|
||||
it('S // successful', () => {
|
||||
return cmdFn({command: {arg: 'S'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('Q // unsuccessful', done => {
|
||||
cmdFn({command: {arg: 'Q'}})
|
||||
it('Q // unsuccessful', () => {
|
||||
return cmdFn({command: {arg: 'Q'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,7 +9,10 @@ describe(CMD, function () {
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
const mockClient = {
|
||||
reply: () => {},
|
||||
fs: { list: () => {} },
|
||||
fs: {
|
||||
get: () => {},
|
||||
list: () => {}
|
||||
},
|
||||
connector: {
|
||||
waitForConnection: () => when({}),
|
||||
end: () => {}
|
||||
@@ -25,6 +28,24 @@ describe(CMD, function () {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
sandbox.stub(mockClient, 'reply').resolves();
|
||||
sandbox.stub(mockClient.fs, 'get').resolves({
|
||||
name: 'testdir',
|
||||
dev: 2114,
|
||||
ino: 48064969,
|
||||
mode: 33188,
|
||||
nlink: 1,
|
||||
uid: 85,
|
||||
gid: 100,
|
||||
rdev: 0,
|
||||
size: 527,
|
||||
blksize: 4096,
|
||||
blocks: 8,
|
||||
atime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
mtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => true
|
||||
});
|
||||
sandbox.stub(mockClient.fs, 'list').resolves([{
|
||||
name: 'test1',
|
||||
dev: 2114,
|
||||
@@ -42,22 +63,70 @@ describe(CMD, function () {
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => false
|
||||
}, {
|
||||
name: 'test2',
|
||||
dev: 2114,
|
||||
ino: 48064969,
|
||||
mode: 33188,
|
||||
nlink: 1,
|
||||
uid: 85,
|
||||
gid: 100,
|
||||
rdev: 0,
|
||||
size: 527,
|
||||
blksize: 4096,
|
||||
blocks: 8,
|
||||
atime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
mtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => true
|
||||
}]);
|
||||
});
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('. // successful', done => {
|
||||
cmdFn({log, command: {directive: CMD}})
|
||||
it('. // successful', () => {
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(150);
|
||||
expect(mockClient.reply.args[1].length).to.equal(3);
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('raw');
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('message');
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('socket');
|
||||
expect(mockClient.reply.args[2][0]).to.equal(226);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('testfile.txt // successful', () => {
|
||||
mockClient.fs.get.restore();
|
||||
sandbox.stub(mockClient.fs, 'get').resolves({
|
||||
name: 'testfile.txt',
|
||||
dev: 2114,
|
||||
ino: 48064969,
|
||||
mode: 33188,
|
||||
nlink: 1,
|
||||
uid: 85,
|
||||
gid: 100,
|
||||
rdev: 0,
|
||||
size: 527,
|
||||
blksize: 4096,
|
||||
blocks: 8,
|
||||
atime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
mtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
ctime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
birthtime: 'Mon, 10 Oct 2011 23:24:11 GMT',
|
||||
isDirectory: () => false
|
||||
});
|
||||
|
||||
return cmdFn({log, command: {directive: CMD, arg: 'testfile.txt'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(150);
|
||||
expect(mockClient.reply.args[1].length).to.equal(2);
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('raw');
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('message');
|
||||
expect(mockClient.reply.args[1][1]).to.have.property('socket');
|
||||
expect(mockClient.reply.args[2][0]).to.equal(226);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,12 +19,10 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('// successful', done => {
|
||||
cmdFn()
|
||||
it('// successful', () => {
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,12 +19,40 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('// successful', done => {
|
||||
cmdFn()
|
||||
it('// unsuccessful', () => {
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,61 +24,51 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('pass // successful', done => {
|
||||
cmdFn({log, command: {arg: 'pass', directive: CMD}})
|
||||
it('pass // successful', () => {
|
||||
return cmdFn({log, command: {arg: 'pass', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(230);
|
||||
expect(mockClient.login.args[0]).to.eql(['anonymous', 'pass']);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// successful (already authenticated)', done => {
|
||||
it('// successful (already authenticated)', () => {
|
||||
mockClient.server.options.anonymous = true;
|
||||
mockClient.authenticated = true;
|
||||
cmdFn({log, command: {directive: CMD}})
|
||||
return cmdFn({log, command: {directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(202);
|
||||
expect(mockClient.login.callCount).to.equal(0);
|
||||
mockClient.server.options.anonymous = false;
|
||||
mockClient.authenticated = false;
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('bad // unsuccessful', done => {
|
||||
it('bad // unsuccessful', () => {
|
||||
mockClient.login.restore();
|
||||
sandbox.stub(mockClient, 'login').rejects('bad');
|
||||
|
||||
cmdFn({log, command: {arg: 'bad', directive: CMD}})
|
||||
return cmdFn({log, command: {arg: 'bad', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(530);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('bad // unsuccessful', done => {
|
||||
it('bad // unsuccessful', () => {
|
||||
mockClient.login.restore();
|
||||
sandbox.stub(mockClient, 'login').rejects({});
|
||||
|
||||
cmdFn({log, command: {arg: 'bad', directive: CMD}})
|
||||
return cmdFn({log, command: {arg: 'bad', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(530);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('bad // unsuccessful', done => {
|
||||
it('bad // unsuccessful', () => {
|
||||
delete mockClient.username;
|
||||
cmdFn({log, command: {arg: 'bad', directive: CMD}})
|
||||
return cmdFn({log, command: {arg: 'bad', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(503);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,8 +7,7 @@ describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockClient = {
|
||||
reply: () => when.resolve(),
|
||||
server: {},
|
||||
secure: true
|
||||
server: {}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
@@ -21,36 +20,32 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('// unsuccessful', done => {
|
||||
cmdFn()
|
||||
it('// unsuccessful', () => {
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(202);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// successful', done => {
|
||||
it('// successful', () => {
|
||||
mockClient.secure = true;
|
||||
mockClient.server._tls = {};
|
||||
|
||||
cmdFn({command: {arg: '0'}})
|
||||
return cmdFn({command: {arg: '0'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
expect(mockClient.bufferSize).to.equal(0);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// successful', done => {
|
||||
it('// successful', () => {
|
||||
mockClient.secure = true;
|
||||
mockClient.server._tls = {};
|
||||
|
||||
cmdFn({command: {arg: '10'}})
|
||||
return cmdFn({command: {arg: '10'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
expect(mockClient.bufferSize).to.equal(10);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,33 +22,27 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('// unsuccessful | no argument', done => {
|
||||
cmdFn()
|
||||
it('// unsuccessful | no argument', () => {
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(425);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful | invalid argument', done => {
|
||||
cmdFn({ command: { arg: '1,2,3,4,5' } })
|
||||
it('// unsuccessful | invalid argument', () => {
|
||||
return cmdFn({ command: { arg: '1,2,3,4,5' } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(425);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// successful', done => {
|
||||
cmdFn({ command: { arg: '192,168,0,100,137,214' } })
|
||||
it('// successful', () => {
|
||||
return cmdFn({ command: { arg: '192,168,0,100,137,214' } })
|
||||
.then(() => {
|
||||
const [ip, port] = ActiveConnector.prototype.setupConnection.args[0];
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
expect(ip).to.equal('192.168.0.100');
|
||||
expect(port).to.equal(35286);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,8 +7,7 @@ describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockClient = {
|
||||
reply: () => when.resolve(),
|
||||
server: {},
|
||||
secure: true
|
||||
server: {}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
@@ -21,52 +20,46 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('// unsuccessful', done => {
|
||||
cmdFn()
|
||||
it('// unsuccessful', () => {
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(202);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful - no bufferSize', done => {
|
||||
it('// unsuccessful - no bufferSize', () => {
|
||||
mockClient.server._tls = {};
|
||||
mockClient.secure = true;
|
||||
|
||||
cmdFn({command: {arg: 'P'}})
|
||||
return cmdFn({command: {arg: 'P'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(503);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// successful', done => {
|
||||
it('// successful', () => {
|
||||
mockClient.bufferSize = 0;
|
||||
mockClient.secure = true;
|
||||
|
||||
cmdFn({command: {arg: 'p'}})
|
||||
return cmdFn({command: {arg: 'p'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful - unsupported', done => {
|
||||
cmdFn({command: {arg: 'C'}})
|
||||
it('// unsuccessful - unsupported', () => {
|
||||
mockClient.secure = true;
|
||||
return cmdFn({command: {arg: 'C'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(536);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful - unknown', done => {
|
||||
cmdFn({command: {arg: 'QQ'}})
|
||||
it('// unsuccessful - unknown', () => {
|
||||
mockClient.secure = true;
|
||||
return cmdFn({command: {arg: 'QQ'}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,61 +23,53 @@ describe(CMD, function () {
|
||||
});
|
||||
|
||||
describe('// check', function () {
|
||||
it('fails on no fs', done => {
|
||||
it('fails on no fs', () => {
|
||||
const badMockClient = { reply: () => {} };
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
badCmdFn()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(550);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on no fs currentDirectory command', done => {
|
||||
it('fails on no fs currentDirectory command', () => {
|
||||
const badMockClient = { reply: () => {}, fs: {} };
|
||||
const badCmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(badMockClient);
|
||||
sandbox.stub(badMockClient, 'reply').resolves();
|
||||
badCmdFn()
|
||||
|
||||
return badCmdFn()
|
||||
.then(() => {
|
||||
expect(badMockClient.reply.args[0][0]).to.equal(402);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('// successful', done => {
|
||||
cmdFn({log, command: { arg: 'test', directive: CMD}})
|
||||
it('// successful', () => {
|
||||
return cmdFn({log, command: { arg: 'test', directive: CMD}})
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(257);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// successful', done => {
|
||||
it('// successful', () => {
|
||||
mockClient.fs.currentDirectory.restore();
|
||||
sandbox.stub(mockClient.fs, 'currentDirectory').resolves('/test');
|
||||
|
||||
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);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful', done => {
|
||||
it('// unsuccessful', () => {
|
||||
mockClient.fs.currentDirectory.restore();
|
||||
sandbox.stub(mockClient.fs, 'currentDirectory').rejects(new Error('Bad'));
|
||||
|
||||
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);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,12 +18,10 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('// successful', done => {
|
||||
cmdFn()
|
||||
it('// successful', () => {
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.close.callCount).to.equal(1);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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 when = require('when');
|
||||
|
||||
const CMD = 'REST';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
const mockClient = {
|
||||
reply: () => when.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);
|
||||
});
|
||||
});
|
||||
});
|
||||
75
test/commands/registration/retr.spec.js
Normal file
75
test/commands/registration/retr.spec.js
Normal file
@@ -0,0 +1,75 @@
|
||||
const when = require('when');
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const CMD = 'RETR';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
const mockClient = {
|
||||
commandSocket: {
|
||||
pause: () => {},
|
||||
resume: () => {}
|
||||
},
|
||||
reply: () => when.resolve(),
|
||||
connector: {
|
||||
waitForConnection: () => when.resolve({
|
||||
resume: () => {}
|
||||
}),
|
||||
end: () => {}
|
||||
}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
mockClient.fs = {
|
||||
read: () => {}
|
||||
};
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
afterEach(() => sandbox.restore());
|
||||
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful | connector times out', () => {
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').callsFake(function () {
|
||||
return when.reject(new when.TimeoutError());
|
||||
});
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'} })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(425);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful | connector errors out', () => {
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').callsFake(function () {
|
||||
return when.reject(new Error('test'));
|
||||
});
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'} })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(551);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -24,47 +24,39 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('// unsuccessful | no file system', done => {
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
|
||||
cmdFn()
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful | file system does not have functions', done => {
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
|
||||
cmdFn()
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('test // unsuccessful | file get fails', done => {
|
||||
it('test // unsuccessful | file get fails', () => {
|
||||
mockClient.fs.get.restore();
|
||||
sandbox.stub(mockClient.fs, 'get').rejects(new Error('test'));
|
||||
|
||||
cmdFn({ log: mockLog, command: { arg: 'test' } })
|
||||
return cmdFn({ log: mockLog, command: { arg: 'test' } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('test // successful', done => {
|
||||
cmdFn({ log: mockLog, command: { arg: 'test' } })
|
||||
it('test // successful', () => {
|
||||
return cmdFn({ log: mockLog, command: { arg: 'test' } })
|
||||
.then(() => {
|
||||
expect(mockClient.fs.get.args[0][0]).to.equal('test');
|
||||
expect(mockClient.reply.args[0][0]).to.equal(350);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -25,58 +25,48 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('// unsuccessful | no renameFrom set', done => {
|
||||
it('// unsuccessful | no renameFrom set', () => {
|
||||
delete mockClient.renameFrom;
|
||||
|
||||
cmdFn()
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(503);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful | no file system', done => {
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
|
||||
cmdFn()
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful | file system does not have functions', done => {
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
|
||||
cmdFn()
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('new // unsuccessful | rename fails', done => {
|
||||
it('new // unsuccessful | rename fails', () => {
|
||||
mockClient.fs.rename.restore();
|
||||
sandbox.stub(mockClient.fs, 'rename').rejects(new Error('test'));
|
||||
|
||||
cmdFn({ log: mockLog, command: { arg: 'new' } })
|
||||
return cmdFn({ log: mockLog, command: { arg: 'new' } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('new // successful', done => {
|
||||
cmdFn({ command: { arg: 'new' } })
|
||||
it('new // successful', () => {
|
||||
return cmdFn({ command: { arg: 'new' } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(250);
|
||||
expect(mockClient.fs.rename.args[0]).to.eql(['test', 'new']);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,45 +22,37 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('// unsuccessful | no file system', done => {
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
|
||||
cmdFn()
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful | file system does not have functions', done => {
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
|
||||
cmdFn()
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful | file get fails', done => {
|
||||
it('// unsuccessful | file get fails', () => {
|
||||
sandbox.stub(mockClient.fs, 'get').rejects(new Error('test'));
|
||||
|
||||
cmdFn({ log: mockLog, command: { arg: 'test' } })
|
||||
return cmdFn({ log: mockLog, command: { arg: 'test' } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// successful', done => {
|
||||
cmdFn({ command: { arg: 'test' } })
|
||||
it('// successful', () => {
|
||||
return cmdFn({ command: { arg: 'test' } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(213);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,49 +23,41 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('// successful', done => {
|
||||
cmdFn()
|
||||
it('// successful', () => {
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(211);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful | no file system', done => {
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
|
||||
cmdFn({ command: { arg: 'test' } })
|
||||
return cmdFn({ command: { arg: 'test' } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful | file system does not have functions', done => {
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
|
||||
cmdFn({ command: { arg: 'test' } })
|
||||
return cmdFn({ command: { arg: 'test' } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful | file get fails', done => {
|
||||
it('// unsuccessful | file get fails', () => {
|
||||
sandbox.stub(mockClient.fs, 'get').rejects(new Error('test'));
|
||||
|
||||
cmdFn({ log: mockLog, command: { arg: 'test' } })
|
||||
return cmdFn({ log: mockLog, command: { arg: 'test' } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(450);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// successful | file', done => {
|
||||
it('// successful | file', () => {
|
||||
sandbox.stub(mockClient.fs, 'get').returns({
|
||||
name: 'test_file',
|
||||
dev: 2114,
|
||||
@@ -85,15 +77,13 @@ describe(CMD, function () {
|
||||
isDirectory: () => false
|
||||
});
|
||||
|
||||
cmdFn({ command: { arg: 'test' } })
|
||||
return cmdFn({ command: { arg: 'test' } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(212);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// successful | directory', done => {
|
||||
it('// successful | directory', () => {
|
||||
sandbox.stub(mockClient.fs, 'list').returns([{
|
||||
name: 'test_file',
|
||||
dev: 2114,
|
||||
@@ -132,11 +122,9 @@ describe(CMD, function () {
|
||||
isDirectory: () => true
|
||||
});
|
||||
|
||||
cmdFn({ command: { arg: 'test' } })
|
||||
return cmdFn({ command: { arg: 'test' } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(213);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
75
test/commands/registration/stor.spec.js
Normal file
75
test/commands/registration/stor.spec.js
Normal file
@@ -0,0 +1,75 @@
|
||||
const when = require('when');
|
||||
const bunyan = require('bunyan');
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const CMD = 'STOR';
|
||||
describe(CMD, function () {
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: CMD});
|
||||
const mockClient = {
|
||||
commandSocket: {
|
||||
pause: () => {},
|
||||
resume: () => {}
|
||||
},
|
||||
reply: () => when.resolve(),
|
||||
connector: {
|
||||
waitForConnection: () => when.resolve({
|
||||
resume: () => {}
|
||||
}),
|
||||
end: () => {}
|
||||
}
|
||||
};
|
||||
const cmdFn = require(`../../../src/commands/registration/${CMD.toLowerCase()}`).handler.bind(mockClient);
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
mockClient.fs = {
|
||||
write: () => {}
|
||||
};
|
||||
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
afterEach(() => sandbox.restore());
|
||||
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful | connector times out', () => {
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').callsFake(function () {
|
||||
return when.reject(new when.TimeoutError());
|
||||
});
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'} })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(425);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful | connector errors out', () => {
|
||||
sandbox.stub(mockClient.connector, 'waitForConnection').callsFake(function () {
|
||||
return when.reject(new Error('test'));
|
||||
});
|
||||
|
||||
return cmdFn({log, command: {arg: 'test.txt'} })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -30,54 +30,46 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('// unsuccessful | no file system', done => {
|
||||
it('// unsuccessful | no file system', () => {
|
||||
delete mockClient.fs;
|
||||
|
||||
cmdFn()
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(550);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful | file system does not have functions', done => {
|
||||
it('// unsuccessful | file system does not have functions', () => {
|
||||
mockClient.fs = {};
|
||||
|
||||
cmdFn()
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(402);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// successful | given name is unique', done => {
|
||||
it('// successful | given name is unique', () => {
|
||||
mockClient.fs.get.restore();
|
||||
sandbox.stub(mockClient.fs, 'get').rejects({});
|
||||
|
||||
cmdFn({ command: { arg: 'good' } })
|
||||
return cmdFn({ command: { arg: 'good' } })
|
||||
.then(() => {
|
||||
const call = stor.handler.call.args[0][1];
|
||||
expect(call).to.have.property('command');
|
||||
expect(call.command).to.have.property('arg');
|
||||
expect(call.command.arg).to.eql('good');
|
||||
expect(mockClient.fs.getUniqueName.callCount).to.equal(0);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// successful | generates unique name', done => {
|
||||
cmdFn({ command: { arg: 'bad' } })
|
||||
it('// successful | generates unique name', () => {
|
||||
return cmdFn({ command: { arg: 'bad' } })
|
||||
.then(() => {
|
||||
const call = stor.handler.call.args[0][1];
|
||||
expect(call).to.have.property('command');
|
||||
expect(call.command).to.have.property('arg');
|
||||
expect(call.command.arg).to.eql('4');
|
||||
expect(mockClient.fs.getUniqueName.callCount).to.equal(1);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,21 +19,17 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('// successful', done => {
|
||||
cmdFn({command: { arg: 'F' } })
|
||||
it('// successful', () => {
|
||||
return cmdFn({command: { arg: 'F' } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('// unsuccessful', done => {
|
||||
cmdFn({command: { arg: 'X' } })
|
||||
it('// unsuccessful', () => {
|
||||
return cmdFn({command: { arg: 'X' } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(504);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,12 +19,10 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('// successful', done => {
|
||||
cmdFn()
|
||||
it('// successful', () => {
|
||||
return cmdFn()
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(215);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,50 +13,42 @@ describe(CMD, function () {
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
mockClient.encoding = null;
|
||||
mockClient.transferType = null;
|
||||
sandbox.spy(mockClient, 'reply');
|
||||
});
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('A // successful', done => {
|
||||
cmdFn({ command: { arg: 'A' } })
|
||||
it('A // successful', () => {
|
||||
return cmdFn({ command: { arg: 'A' } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
expect(mockClient.encoding).to.equal('utf8');
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
expect(mockClient.transferType).to.equal('ascii');
|
||||
});
|
||||
});
|
||||
|
||||
it('I // successful', done => {
|
||||
cmdFn({ command: { arg: 'I' } })
|
||||
it('I // successful', () => {
|
||||
return cmdFn({ command: { arg: 'I' } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
expect(mockClient.encoding).to.equal('binary');
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
expect(mockClient.transferType).to.equal('binary');
|
||||
});
|
||||
});
|
||||
|
||||
it('L // successful', done => {
|
||||
cmdFn({ command: { arg: 'L' } })
|
||||
it('L // successful', () => {
|
||||
return cmdFn({ command: { arg: 'L' } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(200);
|
||||
expect(mockClient.encoding).to.equal('binary');
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
expect(mockClient.transferType).to.equal('binary');
|
||||
});
|
||||
});
|
||||
|
||||
it('X // successful', done => {
|
||||
cmdFn({ command: { arg: 'X' } })
|
||||
it('X // successful', () => {
|
||||
return cmdFn({ command: { arg: 'X' } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501);
|
||||
expect(mockClient.encoding).to.equal(null);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
expect(mockClient.transferType).to.equal(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,70 +28,80 @@ describe(CMD, function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('test // successful | prompt for password', done => {
|
||||
cmdFn({ command: { arg: 'test' } })
|
||||
it('test // successful | prompt for password', () => {
|
||||
return cmdFn({ command: { arg: 'test' } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(331);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('test // successful | anonymous login', done => {
|
||||
it('test // successful | anonymous login', () => {
|
||||
mockClient.server.options = {anonymous: true};
|
||||
|
||||
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);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('test // unsuccessful | no username provided', done => {
|
||||
cmdFn({ command: { } })
|
||||
it('test // unsuccessful | no username provided', () => {
|
||||
return cmdFn({ command: { } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(501);
|
||||
expect(mockClient.login.callCount).to.equal(0);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('test // unsuccessful | already set username', done => {
|
||||
it('test // unsuccessful | already set username', () => {
|
||||
mockClient.username = 'test';
|
||||
|
||||
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);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('test // successful | regular login if anonymous is true', done => {
|
||||
it('test // successful | regular login if anonymous is true', () => {
|
||||
mockClient.server.options = {anonymous: true};
|
||||
|
||||
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);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('test // successful | anonymous login with set username', done => {
|
||||
it('test // successful | anonymous login with set username', () => {
|
||||
mockClient.server.options = {anonymous: 'sillyrabbit'};
|
||||
|
||||
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);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('test // unsuccessful | anonymous login fails', () => {
|
||||
mockClient.server.options = {anonymous: true};
|
||||
mockClient.login.restore();
|
||||
sandbox.stub(mockClient, 'login').rejects(new Error('test'));
|
||||
|
||||
return cmdFn({ log: mockLog, command: { arg: 'anonymous' } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(530);
|
||||
expect(mockClient.login.callCount).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('test // successful | does not login if already authenticated', () => {
|
||||
mockClient.authenticated = true;
|
||||
|
||||
return cmdFn({ log: mockLog, command: { arg: 'sillyrabbit' } })
|
||||
.then(() => {
|
||||
expect(mockClient.reply.args[0][0]).to.equal(230);
|
||||
expect(mockClient.login.callCount).to.equal(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
|
||||
const net = require('net');
|
||||
const tls = require('tls');
|
||||
|
||||
const ActiveConnector = require('../../src/connector/active');
|
||||
const findPort = require('../../src/helpers/find-port');
|
||||
@@ -33,34 +34,49 @@ describe('Connector - Active //', function () {
|
||||
server.close(done);
|
||||
});
|
||||
|
||||
it('sets up a connection', function (done) {
|
||||
active.setupConnection('127.0.0.1', PORT)
|
||||
it('sets up a connection', function () {
|
||||
return active.setupConnection('127.0.0.1', PORT)
|
||||
.then(() => {
|
||||
expect(active.dataSocket).to.exist;
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('destroys existing connection, then sets up a connection', function (done) {
|
||||
it('destroys existing connection, then sets up a connection', function () {
|
||||
const destroyFnSpy = sandbox.spy(active.dataSocket, 'destroy');
|
||||
|
||||
active.setupConnection('127.0.0.1', PORT)
|
||||
return active.setupConnection('127.0.0.1', PORT)
|
||||
.then(() => {
|
||||
expect(destroyFnSpy.callCount).to.equal(1);
|
||||
expect(active.dataSocket).to.exist;
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('waits for connection', function (done) {
|
||||
active.setupConnection('127.0.0.1', PORT)
|
||||
it('waits for connection', function () {
|
||||
return active.setupConnection('127.0.0.1', PORT)
|
||||
.then(() => {
|
||||
expect(active.dataSocket).to.exist;
|
||||
return active.waitForConnection();
|
||||
})
|
||||
.then(() => done())
|
||||
.catch(done);
|
||||
.then(dataSocket => {
|
||||
expect(dataSocket.connected).to.equal(true);
|
||||
expect(dataSocket instanceof net.Socket).to.equal(true);
|
||||
expect(dataSocket instanceof tls.TLSSocket).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('upgrades to a secure connection', function () {
|
||||
mockConnection.secure = true;
|
||||
mockConnection.server = { _tls: {} };
|
||||
|
||||
return active.setupConnection('127.0.0.1', PORT)
|
||||
.then(() => {
|
||||
expect(active.dataSocket).to.exist;
|
||||
return active.waitForConnection();
|
||||
})
|
||||
.then(dataSocket => {
|
||||
expect(dataSocket.connected).to.equal(true);
|
||||
expect(dataSocket instanceof net.Socket).to.equal(true);
|
||||
expect(dataSocket instanceof tls.TLSSocket).to.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,6 +20,10 @@ describe('Connector - Passive //', function () {
|
||||
};
|
||||
let sandbox;
|
||||
|
||||
function shouldNotResolve() {
|
||||
throw new Error('Should not resolve');
|
||||
}
|
||||
|
||||
before(() => {
|
||||
passive = new PassiveConnector(mockConnection);
|
||||
});
|
||||
@@ -36,45 +40,49 @@ describe('Connector - Passive //', function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('cannot wait for connection with no server', function (done) {
|
||||
passive.waitForConnection()
|
||||
.then(() => done('should not happen'))
|
||||
it('cannot wait for connection with no server', function () {
|
||||
return passive.waitForConnection()
|
||||
.then(shouldNotResolve)
|
||||
.catch(err => {
|
||||
expect(err.name).to.equal('ConnectorError');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('has invalid pasv range', function (done) {
|
||||
it('no pasv range provided', function () {
|
||||
delete mockConnection.server.options.pasv_range;
|
||||
|
||||
return passive.setupServer()
|
||||
.then(shouldNotResolve)
|
||||
.catch(err => {
|
||||
expect(err.name).to.equal('ConnectorError');
|
||||
});
|
||||
});
|
||||
|
||||
it('has invalid pasv range', function () {
|
||||
mockConnection.server.options.pasv_range = -1;
|
||||
|
||||
passive.setupServer()
|
||||
.then(() => done('should not happen'))
|
||||
return passive.setupServer()
|
||||
.then(shouldNotResolve)
|
||||
.catch(err => {
|
||||
expect(err.name).to.equal('RangeError');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('sets up a server', function (done) {
|
||||
passive.setupServer()
|
||||
it('sets up a server', function () {
|
||||
return passive.setupServer()
|
||||
.then(() => {
|
||||
expect(passive.dataServer).to.exist;
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('destroys existing server, then sets up a server', function (done) {
|
||||
it('destroys existing server, then sets up a server', function () {
|
||||
const closeFnSpy = sandbox.spy(passive.dataServer, 'close');
|
||||
|
||||
passive.setupServer()
|
||||
return passive.setupServer()
|
||||
.then(() => {
|
||||
expect(closeFnSpy.callCount).to.equal(1);
|
||||
expect(passive.dataServer).to.exist;
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('refuses connection with different remote address', function (done) {
|
||||
@@ -97,8 +105,8 @@ describe('Connector - Passive //', function () {
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('accepts connection', function (done) {
|
||||
passive.setupServer()
|
||||
it('accepts connection', function () {
|
||||
return passive.setupServer()
|
||||
.then(() => {
|
||||
expect(passive.dataServer).to.exist;
|
||||
|
||||
@@ -109,8 +117,6 @@ describe('Connector - Passive //', function () {
|
||||
.then(() => {
|
||||
expect(passive.dataSocket).to.exist;
|
||||
passive.end();
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,10 +2,9 @@ const {expect} = require('chai');
|
||||
const escapePath = require('../../src/helpers/escape-path');
|
||||
|
||||
describe('helpers // escape-path', function () {
|
||||
it('escapes quotes', done => {
|
||||
it('escapes quotes', () => {
|
||||
const string = '"test"';
|
||||
const escapedString = escapePath(string);
|
||||
expect(escapedString).to.equal('""test""');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,22 +17,19 @@ describe('helpers // find-port', function () {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
it('finds a port', done => {
|
||||
findPort(1)
|
||||
it('finds a port', () => {
|
||||
return findPort(1)
|
||||
.then(port => {
|
||||
expect(Server.prototype.listen.callCount).to.be.above(1);
|
||||
expect(port).to.be.above(1);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('does not find a port', done => {
|
||||
findPort(1, 2)
|
||||
.then(() => done('no'))
|
||||
it('does not find a port', () => {
|
||||
return findPort(1, 2)
|
||||
.then(() => expect(1).to.equal(2)) // should not happen
|
||||
.catch(err => {
|
||||
expect(err).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,36 +1,51 @@
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const resolveHost = require('../../src/helpers/resolve-host');
|
||||
|
||||
describe('helpers //resolve-host', function () {
|
||||
this.timeout(4000);
|
||||
|
||||
it('fetches ip address', done => {
|
||||
let sandbox;
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
});
|
||||
afterEach(() => sandbox.restore());
|
||||
|
||||
it('fetches ip address', () => {
|
||||
const hostname = '0.0.0.0';
|
||||
resolveHost(hostname)
|
||||
return resolveHost(hostname)
|
||||
.then(resolvedHostname => {
|
||||
expect(resolvedHostname).to.match(/^\d+\.\d+\.\d+\.\d+$/);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('fetches ip address', done => {
|
||||
it('fetches ip address', () => {
|
||||
const hostname = null;
|
||||
resolveHost(hostname)
|
||||
return resolveHost(hostname)
|
||||
.then(resolvedHostname => {
|
||||
expect(resolvedHostname).to.match(/^\d+\.\d+\.\d+\.\d+$/);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('does nothing', done => {
|
||||
it('does nothing', () => {
|
||||
const hostname = '127.0.0.1';
|
||||
resolveHost(hostname)
|
||||
return resolveHost(hostname)
|
||||
.then(resolvedHostname => {
|
||||
expect(resolvedHostname).to.equal(hostname);
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
});
|
||||
|
||||
it('fails on getting hostname', () => {
|
||||
sandbox.stub(require('http'), 'get').callsFake(function (url, cb) {
|
||||
cb({
|
||||
statusCode: 420
|
||||
});
|
||||
});
|
||||
|
||||
return resolveHost(null)
|
||||
.then(() => expect(1).to.equal(2))
|
||||
.catch(err => {
|
||||
expect(err.code).to.equal(420);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* eslint no-unused-expressions: 0 */
|
||||
const {expect} = require('chai');
|
||||
const sinon = require('sinon');
|
||||
const bunyan = require('bunyan');
|
||||
const fs = require('fs');
|
||||
|
||||
@@ -10,11 +11,14 @@ before(() => require('dotenv').load());
|
||||
|
||||
describe('FtpServer', function () {
|
||||
this.timeout(2000);
|
||||
let sandbox;
|
||||
let log = bunyan.createLogger({name: 'test'});
|
||||
let server;
|
||||
let client;
|
||||
|
||||
before(done => {
|
||||
let connection;
|
||||
|
||||
before(() => {
|
||||
server = new FtpServer(process.env.FTP_URL, {
|
||||
log,
|
||||
pasv_range: process.env.PASV_RANGE,
|
||||
@@ -22,14 +26,21 @@ describe('FtpServer', function () {
|
||||
key: `${process.cwd()}/test/cert/server.key`,
|
||||
cert: `${process.cwd()}/test/cert/server.crt`,
|
||||
ca: `${process.cwd()}/test/cert/server.csr`
|
||||
}
|
||||
},
|
||||
greeting: ['hello', 'world']
|
||||
});
|
||||
server.on('login', (data, resolve) => {
|
||||
connection = data.connection;
|
||||
resolve({root: process.cwd()});
|
||||
});
|
||||
|
||||
server.listen()
|
||||
.then(() => done());
|
||||
return server.listen();
|
||||
});
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
});
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
after(() => {
|
||||
server.close();
|
||||
@@ -95,16 +106,42 @@ describe('FtpServer', function () {
|
||||
client.list('.', (err, data) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(data).to.be.an('array');
|
||||
expect(data.length).to.be.above(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('STOR test.txt', done => {
|
||||
const buffer = Buffer.from('test text file');
|
||||
client.put(buffer, 'test.txt', err => {
|
||||
it('LIST index.spec.js', done => {
|
||||
client.list('index.spec.js', (err, data) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(fs.existsSync('./test/test.txt')).to.equal(true);
|
||||
fs.readFile('./test/test.txt', (fserr, data) => {
|
||||
expect(data).to.be.an('array');
|
||||
expect(data.length).to.be.equal(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('STOR fail.txt', done => {
|
||||
sandbox.stub(connection.fs, 'write').callsFake(function () {
|
||||
const fsPath = './test/fail.txt';
|
||||
const stream = require('fs').createWriteStream(fsPath, {flags: 'w+'});
|
||||
stream.once('error', () => fs.unlink(fsPath));
|
||||
setTimeout(() => stream.emit('error', new Error('STOR fail test'), 1));
|
||||
return stream;
|
||||
});
|
||||
const buffer = Buffer.from('test text file');
|
||||
client.put(buffer, 'fail.txt', err => {
|
||||
expect(err).to.exist;
|
||||
expect(fs.existsSync('./test/fail.txt')).to.equal(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('STOR tést.txt', done => {
|
||||
const buffer = Buffer.from('test text file');
|
||||
client.put(buffer, 'tést.txt', err => {
|
||||
expect(err).to.not.exist;
|
||||
expect(fs.existsSync('./test/tést.txt')).to.equal(true);
|
||||
fs.readFile('./test/tést.txt', (fserr, data) => {
|
||||
expect(fserr).to.not.exist;
|
||||
expect(data.toString()).to.equal('test text file');
|
||||
done();
|
||||
@@ -112,11 +149,11 @@ describe('FtpServer', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('APPE test.txt', done => {
|
||||
it('APPE tést.txt', done => {
|
||||
const buffer = Buffer.from(', awesome!');
|
||||
client.append(buffer, 'test.txt', err => {
|
||||
client.append(buffer, 'tést.txt', err => {
|
||||
expect(err).to.not.exist;
|
||||
fs.readFile('./test/test.txt', (fserr, data) => {
|
||||
fs.readFile('./test/tést.txt', (fserr, data) => {
|
||||
expect(fserr).to.not.exist;
|
||||
expect(data.toString()).to.equal('test text file, awesome!');
|
||||
done();
|
||||
@@ -124,8 +161,8 @@ describe('FtpServer', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('RETR test.txt', done => {
|
||||
client.get('test.txt', (err, stream) => {
|
||||
it('RETR tést.txt', done => {
|
||||
client.get('tést.txt', (err, stream) => {
|
||||
expect(err).to.not.exist;
|
||||
let text = '';
|
||||
stream.on('data', data => {
|
||||
@@ -138,10 +175,10 @@ describe('FtpServer', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('RNFR test.txt, RNTO awesome.txt', done => {
|
||||
client.rename('test.txt', 'awesome.txt', err => {
|
||||
it('RNFR tést.txt, RNTO awesome.txt', done => {
|
||||
client.rename('tést.txt', 'awesome.txt', err => {
|
||||
expect(err).to.not.exist;
|
||||
expect(fs.existsSync('./test/test.txt')).to.equal(false);
|
||||
expect(fs.existsSync('./test/tést.txt')).to.equal(false);
|
||||
expect(fs.existsSync('./test/awesome.txt')).to.equal(true);
|
||||
fs.readFile('./test/awesome.txt', (fserr, data) => {
|
||||
expect(fserr).to.not.exist;
|
||||
@@ -186,19 +223,19 @@ describe('FtpServer', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('MKD tmp', done => {
|
||||
if (fs.existsSync('./test/tmp')) {
|
||||
fs.rmdirSync('./test/tmp');
|
||||
it('MKD témp', done => {
|
||||
if (fs.existsSync('./test/témp')) {
|
||||
fs.rmdirSync('./test/témp');
|
||||
}
|
||||
client.mkdir('tmp', err => {
|
||||
client.mkdir('témp', err => {
|
||||
expect(err).to.not.exist;
|
||||
expect(fs.existsSync('./test/tmp')).to.equal(true);
|
||||
expect(fs.existsSync('./test/témp')).to.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('CWD tmp', done => {
|
||||
client.cwd('tmp', (err, data) => {
|
||||
it('CWD témp', done => {
|
||||
client.cwd('témp', (err, data) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(data).to.be.a('string');
|
||||
done();
|
||||
@@ -212,10 +249,10 @@ describe('FtpServer', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('RMD tmp', done => {
|
||||
client.rmdir('tmp', err => {
|
||||
it('RMD témp', done => {
|
||||
client.rmdir('témp', err => {
|
||||
expect(err).to.not.exist;
|
||||
expect(fs.existsSync('./test/tmp')).to.equal(false);
|
||||
expect(fs.existsSync('./test/témp')).to.equal(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user