Compare commits

..

15 Commits

Author SHA1 Message Date
Amr Ibrahim
1ad45fc757 fix pasv_url resolverFunction docs (#297) 2022-04-18 15:50:49 -06:00
Matt Forster
bc8abb14da feat: npm updates, security zero, test corrections (#296) 2022-04-18 15:50:14 -06:00
dependabot[bot]
0b55b3f79d chore(deps): bump ssri from 6.0.1 to 6.0.2 (#293)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-14 14:44:51 -06:00
dependabot[bot]
4f4a6c25a5 chore(deps): bump moment from 2.22.2 to 2.29.2 (#288)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-14 14:44:41 -06:00
dependabot[bot]
45a4bf15bf chore(deps): bump trim-off-newlines from 1.0.1 to 1.0.3 (#289)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-14 14:44:32 -06:00
dependabot[bot]
a1be4416a7 chore(deps): bump node-fetch from 2.6.1 to 2.6.7 (#290)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-14 14:44:08 -06:00
Bas Broerse
32a0750e2c feat: allow mkd command to create directories recursively (#294)
Co-authored-by: Bas Broerse <b.broerse@sector-orange.com>
2022-04-14 14:43:33 -06:00
Matt Forster
4eb17015f1 chore: update CI context (#295) 2022-04-14 14:40:36 -06:00
dependabot[bot]
d2566e7745 chore(deps): bump npm-user-validate from 1.0.0 to 1.0.1 (#292)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-09 19:50:41 +00:00
dependabot[bot]
f8cd1e8f64 chore(deps): bump tar from 4.4.13 to 4.4.19 (#291)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-09 13:48:56 -06:00
dependabot[bot]
e0e676e7e9 chore(deps): bump pathval from 1.1.0 to 1.1.1 (#285)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-09 13:46:14 -06:00
dependabot[bot]
a7775a46ae chore(deps): bump minimist from 1.2.5 to 1.2.6 (#287)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-09 13:44:16 -06:00
Chen WeiJian
f9c81b162a Provide more friendly example code. (#282)
* Update README.md

* Update README.md
2021-12-06 09:32:59 -07:00
dependabot[bot]
e1f1aa09cd chore(deps): bump path-parse from 1.0.6 to 1.0.7 (#274)
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-11-04 14:50:26 -06:00
Victor
b0174bb24e chore: make fileName available in getUniqueName (#273) 2021-11-04 14:49:50 -06:00
10 changed files with 13609 additions and 7122 deletions

View File

@@ -1,7 +1,7 @@
version: 2.1
orbs:
node: circleci/node@4.1.0
node: circleci/node@5.0.2
commands:
setup_git_bot:
@@ -58,6 +58,32 @@ jobs:
workflows:
version: 2
release_scheduled:
triggers:
# 6:03 UTC (mornings) 1 monday
- schedule:
cron: "3 6 * * 1"
filters:
branches:
only:
- main
jobs:
- lint
- node/test:
matrix:
parameters:
version:
- '12.22'
- '14.19'
- '16.14'
- 'current'
- release:
context: npm-deploy-av
requires:
- node/test
- lint
test:
jobs:
- lint
@@ -65,14 +91,14 @@ workflows:
matrix:
parameters:
version:
- '10.23'
- '12.20'
- '14.15'
- '12.22'
- '14.19'
- '16.14'
- 'current'
- release_dry_run:
filters:
branches:
only: master
only: main
requires:
- node/test
- lint
@@ -81,6 +107,6 @@ workflows:
requires:
- release_dry_run
- release:
context: deploy-npm
context: npm-deploy-av
requires:
- hold_release

View File

@@ -36,16 +36,25 @@
## Usage
```js
// Quick start
// Quick start, create an active ftp server.
const FtpSrv = require('ftp-srv');
const ftpServer = new FtpSrv({ options ... });
ftpServer.on('login', (data, resolve, reject) => { ... });
...
const port=21;
const ftpServer = new FtpSrv({
url: "ftp://0.0.0.0:" + port,
anonymous: true
});
ftpServer.listen()
.then(() => { ... });
ftpServer.on('login', (data, resolve, reject) => {
if(data.username === 'anonymous' && data.password === 'anonymous'){
return resolve({ root:"/" });
}
return reject(new errors.GeneralError('Invalid username or password', 401));
});
ftpServer.listen().then(() => {
console.log('Ftp server is starting...')
});
```
## API
@@ -65,23 +74,34 @@ __Default:__ `"ftp://127.0.0.1:21"`
- A function which takes one parameter containing the remote IP address of the FTP client. This can be useful when the user wants to return a different IP address depending if the user is connecting from Internet or from an LAN address.
Example:
```js
const {Netmask} = require('netmask');
const networks = {
'$GATEWAY_IP/32': `${public_ip}`,
'10.0.0.0/8' : `${lan_ip}`
}
const { networkInterfaces } = require('os');
const { Netmask } = require('netmask');
const resolverFunction = (address) => {
for (const network in networks) {
try {
const mask = new Netmask(network);
if (mask.contains(address)) return networks[network];
}
catch (err) {
logger.error('Error checking source network', err);
const nets = networkInterfaces();
function getNetworks() {
let networks = {};
for (const name of Object.keys(nets)) {
for (const net of nets[name]) {
if (net.family === 'IPv4' && !net.internal) {
networks[net.address + "/24"] = net.address
}
}
}
return '127.0.0.1';
return networks;
}
const resolverFunction = (address) => {
// const networks = {
// '$GATEWAY_IP/32': `${public_ip}`,
// '10.0.0.0/8' : `${lan_ip}`
// }
const networks = getNetworks();
for (const network in networks) {
if (new Netmask(network).contains(address)) {
return networks[network];
}
}
return "127.0.0.1";
}
new FtpSrv({pasv_url: resolverFunction});
@@ -345,8 +365,8 @@ __Used in:__ `RNFR`, `RNTO`
Modifies a file or directory's permissions
__Used in:__ `SITE CHMOD`
#### [`getUniqueName()`](src/fs.js#L131)
Returns a unique file name to write to
#### [`getUniqueName(fileName)`](src/fs.js#L131)
Returns a unique file name to write to. Client requested filename available if you want to base your function on it.
__Used in:__ `STOU`
## Contributing

2
ftp-srv.d.ts vendored
View File

@@ -38,7 +38,7 @@ export class FileSystem {
chmod(path: string, mode: string): Promise<any>;
getUniqueName(): string;
getUniqueName(fileName: string): string;
}
export class FtpConnection extends EventEmitter {

20550
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -31,8 +31,10 @@
},
"release": {
"verifyConditions": "condition-circle",
"branch": "master",
"branches": ["master"]
"branch": "main",
"branches": [
"main"
]
},
"husky": {
"hooks": {
@@ -42,8 +44,7 @@
},
"lint-staged": {
"*.js": [
"eslint --fix",
"git add"
"eslint --fix"
]
},
"commitlint": {
@@ -74,16 +75,16 @@
},
"devDependencies": {
"@commitlint/cli": "^10.0.0",
"@commitlint/config-conventional": "^8.1.0",
"@commitlint/config-conventional": "^16.2.1",
"@icetee/ftp": "^1.0.2",
"chai": "^4.2.0",
"condition-circle": "^2.0.2",
"eslint": "^5.14.1",
"husky": "^1.3.1",
"lint-staged": "^8.2.1",
"mocha": "^8.1.1",
"lint-staged": "^12.3.7",
"mocha": "^9.2.2",
"rimraf": "^2.6.1",
"semantic-release": "^17.2.3",
"semantic-release": "^19.0.2",
"sinon": "^2.3.5"
},
"engines": {

View File

@@ -7,7 +7,7 @@ module.exports = {
if (!this.fs) return this.reply(550, 'File system not instantiated');
if (!this.fs.mkdir) return this.reply(402, 'Not supported by file system');
return Promise.try(() => this.fs.mkdir(command.arg))
return Promise.try(() => this.fs.mkdir(command.arg, { recursive: true }))
.then((dir) => {
const path = dir ? `"${escapePath(dir)}"` : undefined;
return this.reply(257, path);

View File

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

View File

@@ -119,7 +119,7 @@ class FileSystem {
mkdir(path) {
const {fsPath} = this._resolvePath(path);
return fsAsync.mkdir(fsPath)
return fsAsync.mkdir(fsPath, { recursive: true })
.then(() => fsPath);
}

View File

@@ -117,17 +117,22 @@ class FtpServer extends EventEmitter {
}
disconnectClient(id) {
return new Promise((resolve) => {
return new Promise((resolve, reject) => {
const client = this.connections[id];
if (!client) return resolve();
delete this.connections[id];
setTimeout(() => {
reject('Timed out disconnecting client')
}, this.options.timeout || 1000)
try {
client.close(0);
} catch (err) {
this.log.error(err, 'Error closing connection', {id});
} finally {
resolve('Disconnected');
}
resolve('Disconnected');
});
}
@@ -137,16 +142,22 @@ class FtpServer extends EventEmitter {
}
close() {
this.log.info('Server closing...');
this.server.maxConnections = 0;
return Promise.map(Object.keys(this.connections), (id) => Promise.try(this.disconnectClient.bind(this, id)))
this.log.info('Closing connections:', Object.keys(this.connections).length);
return Promise.all(Object.keys(this.connections).map((id) => this.disconnectClient(id)))
.then(() => new Promise((resolve) => {
this.server.close((err) => {
this.log.info('Server closing...');
if (err) this.log.error(err, 'Error closing server');
resolve('Closed');
});
}))
.then(() => this.removeAllListeners());
.then(() => {
this.log.debug('Removing event listeners...')
this.removeAllListeners();
return;
});
}
}

View File

@@ -24,10 +24,13 @@ describe('Integration', function () {
before(() => {
return startServer({url: 'ftp://127.0.0.1:8880'});
});
beforeEach(() => {
sandbox = sinon.sandbox.create().usingPromise(Promise);
});
afterEach(() => sandbox.restore());
after(() => server.close());
before(() => {
@@ -101,7 +104,8 @@ describe('Integration', function () {
if (stat.isDirectory()) directoryPurge(item);
else fs.unlinkSync(item);
});
fs.rmdirSync(dir);
fs.rmdirSync(dir, { recursive: true });
}
function runFileSystemTests(name) {
@@ -259,13 +263,16 @@ describe('Integration', function () {
client.get('tést.txt', (err, stream) => {
expect(err).to.not.exist;
let text = '';
stream.on('data', (data) => {
text += data.toString();
});
stream.on('end', () => {
stream.on('finish', () => {
expect(text).to.equal('test text file, awesome!');
done();
});
stream.resume();
});
});
@@ -338,7 +345,25 @@ describe('Integration', function () {
});
});
it('MKD témp multiple levels deep', (done) => {
const path = `${clientDirectory}/${name}/témp`;
if (fs.existsSync(path)) {
fs.rmdirSync(path, {recursive: true});
}
client.mkdir('témp/first/second', (err) => {
expect(err).to.not.exist;
expect(fs.existsSync(path)).to.equal(true);
fs.rmdirSync(path, {recursive: true});
done();
});
});
it('CWD témp', (done) => {
const path = `${clientDirectory}/${name}/témp`;
fs.mkdirSync(path)
client.cwd('témp', (err, data) => {
expect(err).to.not.exist;
expect(data).to.to.be.a('string');