Socket
Socket
Sign inDemoInstall

jsftpd

Package Overview
Dependencies
0
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.1.1 to 1.2.0

docs/events.md

5

docs/index.md

@@ -23,7 +23,4 @@ # Welcome to jsftpd documentation!

const server = new ftpd({cnf: {username: 'john', password: 'doe'})
const server = new ftpd({cnf: {username: 'john', password: 'doe', basefolder: '/tmp'})
server.on('log', console.log)
server.on('error', console.error)
server.start()

@@ -30,0 +27,0 @@ ```

123

docs/options.md
# Configuration
jsftpd takes an object when a new instance is created. This object can contain two properties:
jsftpd takes an object when a new instance is created. This object can have different properties:
```{code-block} javascript
new ftpd({tls: {...}, cnf: {...}})
new ftpd({tls: {...}, cnf: {...}, hdl: {...}})
```

@@ -11,2 +11,3 @@

* **cnf**: jsftpd specific configuration items
* **hdl**: jsftpd handler for specific FTP commands

@@ -157,2 +158,13 @@ ## tls

### allowUserFileRetrieve
**Type: `boolean`**\
**Default: `true`**
Allow to retrieve files. This only affects the main user.
```{code-block} javascript
new ftpd({cnf: {allowUserFileRetrieve: true}})
```
### allowUserFileOverwrite

@@ -224,2 +236,13 @@

### allowAnonymousFileRetrieve
**Type: `boolean`**\
**Default: `false`**
Allow the anonymous user to retrieve files.
```{code-block} javascript
new ftpd({cnf: {allowAnonymousFileRetrieve: true}})
```
### allowAnonymousFileOverwrite

@@ -268,1 +291,97 @@

```
## hdl
jsftpd can use handler functions instead of reading/writing to the file system for several FTP commands.
```{code-block} javascript
const handler = {
upload: async function (...) {},
download: async function (...) {},
list: async function (...) {},
rename: async function (...) {}
}
const server = new ftpd({hdl: handler})
```
The following FTP commands are covered by the handlers
* **STOR**: Uploading files to the FTP server
* **RETR**: Downloading files from the FTP server
* **LIST**: Listing directory contents on the FTP server
* **MLSD**: Listing directory contents on the FTP server
* **RNFR**: Renaming files on the FTP server
* **RNTO**: Renaming files on the FTP server
### upload
**Name: `upload`**\
**Returns: `boolean`**
The upload function takes 5 arguments when being called from jsftpd. It must return `true` on success or `false` on any error handling the file upload.
**username: `string` the user who has uploaded the file**\
**path: `string` relative path on the FTP server where the file is stored**\
**fileName: `string` the name of the file that is stored**\
**data: `Buffer` the file content that is stored**\
**offset: `number` the offset of the data received**
```{code-block} javascript
async upload (username, path, fileName, data, offset) {
...
}
```
### download
**Name: `upload`**\
**Returns: `Buffer`**
The download function takes 4 arguments when being called from jsftpd. It must return the file content as a `Buffer`.
**username: `string` the user who has uploaded the file**\
**path: `string` relative path on the FTP server where the file is stored**\
**fileName: `string` the name of the file that is stored**\
**data: `Buffer` the file content that is stored**\
**offset: `number` the offset of the data received**
```{code-block} javascript
async upload (username, path, fileName, data, offset) {
...
}
```
### list
**Name: `list`**\
**Returns: `string`**
The list function takes 3 arguments when being called from jsftpd. It must return the content of the specified directory as a `string`.
**username: `string` the current user**\
**path: `string` relative path on the FTP server**\
**format: `string` the format of the list reply (MLSD | LIST)**
```{code-block} javascript
async upload (username, path, format) {
...
}
```
### rename
**Name: `rename`**\
**Returns: `boolean`**
The rename function takes 4 arguments when being called from jsftpd. It must return `true` on success or `false` on any error handling the file rename.
**username: `string` the current user**\
**path: `string` relative path on the FTP server**\
**fromName: `string` the current file that needs to be renamed**
**newName: `string` the new name of the file**
```{code-block} javascript
async upload (username, path, fromName, newName) {
...
}
```

@@ -32,2 +32,3 @@ /*

allowAnonymousFileCreate: false,
allowAnonymousFileRetrieve: false,
allowAnonymousFileOverwrite: false,

@@ -38,10 +39,16 @@ allowAnonymousFileDelete: false,

allowAnonymousLogin: false,
minDataPort: 1024,
uploadHandler: null,
downloadHandler: null
minDataPort: 1024
}
const HandlerDefaults = {
upload: async function () {},
download: async function () {},
list: async function () {},
rename: async function () {}
}
const UserDefaults = {
allowLoginWithoutPassword: false,
allowUserFileCreate: true,
allowUserFileRetrieve: true,
allowUserFileOverwrite: true,

@@ -71,6 +78,8 @@ allowUserFileDelete: true,

this._opt.cnf = Object.assign({}, FTPdefaults, UserDefaults, options && options.cnf)
this._useTLS = options && options.tls
this._opt.hdl = Object.assign({}, HandlerDefaults, options && options.hdl)
this._useTLS = options && Object.keys(options).indexOf('tls') > -1
this._useHdl = options && Object.keys(options).indexOf('hdl') > -1
// checks
if (!this._opt.cnf.uploadHandler && !fs.existsSync(this._opt.cnf.basefolder)) {
if (!this._useHdl && !fs.existsSync(this._opt.cnf.basefolder)) {
if (this._opt.cnf.basefolder === defaultBaseFolder) {

@@ -92,3 +101,3 @@ fs.mkdirSync(defaultBaseFolder)

this.DebugHandler(`FTP server listening on ${util.inspect(this._tcp.address(), { showHidden: false, depth: null, breakLength: 'Infinity' })}`)
this.emit('listen-tcp', this._tcp.address())
this.emit('listen', { protocol: 'tcp', address: this._tcp.address().address.replace(/::ffff:/g, ''), port: this._tcp.address().port })
})

@@ -103,3 +112,3 @@ this._tcp.maxConnections = this._opt.cnf.maxConnections

this.DebugHandler(`FTP server listening on ${util.inspect(this._tls.address(), { showHidden: false, depth: null, breakLength: 'Infinity' })}`)
this.emit('listen-tls', this._tls.address())
this.emit('listen', { protocol: 'tls', address: this._tls.address().address.replace(/::ffff:/g, ''), port: this._tls.address().port })
})

@@ -141,4 +150,5 @@ this._tls.maxConnections = this._opt.cnf.maxConnections

Handler (main, socket) {
const connectionInfo = `[${socket.remoteAddress.replace(/::ffff:/g, '')}] [${socket.remotePort}]`
const remoteAddr = socket.remoteAddress.replace(/::ffff:/g, '')
const localAddr = socket.localAddress.replace(/::ffff:/g, '')
const connectionInfo = `[${remoteAddr}] [${socket.remotePort}]`
const socketKey = ++main.lastSocketKey

@@ -164,2 +174,3 @@ main.openSockets[socketKey] = socket

let allowFileCreate = false
let allowFileRetrieve = false
let allowFileOverwrite = false

@@ -199,2 +210,3 @@ let allowFileDelete = false

delete main.openSockets[socketKey]
main._informLogoff(username, remoteAddr)
main.DebugHandler(`${connectionInfo} FTP connection closed`)

@@ -215,11 +227,15 @@ if (ftpData) {

case LoginType.None:
return main._writeToSocket(socket, '530', ' ', 'Not logged in', connectionInfo, SocketStateAfterWrite.Open)
main._writeToSocket(socket, '530', ' ', 'Not logged in', connectionInfo, SocketStateAfterWrite.Open)
break
case LoginType.Anonymous:
case LoginType.Password:
return main._writeToSocket(socket, '331', ' ', `Password required for ${username}`, connectionInfo, SocketStateAfterWrite.Open)
main._writeToSocket(socket, '331', ' ', `Password required for ${username}`, connectionInfo, SocketStateAfterWrite.Open)
break
case LoginType.NoPassword:
authenticated = true
return main._writeToSocket(socket, '232', ' ', 'User logged in', connectionInfo, SocketStateAfterWrite.Open)
main._writeToSocket(socket, '232', ' ', 'User logged in', connectionInfo, SocketStateAfterWrite.Open)
main._informLogin(username, remoteAddr)
break
default:
return main._writeToSocket(socket, '331', ' ', `Password required for ${username}`, connectionInfo, SocketStateAfterWrite.Open)
main._writeToSocket(socket, '331', ' ', `Password required for ${username}`, connectionInfo, SocketStateAfterWrite.Open)
}

@@ -235,2 +251,3 @@ }

main._writeToSocket(socket, '230', ' ', 'Logged on', connectionInfo, SocketStateAfterWrite.Open)
main._informLogin(username, remoteAddr)
} else {

@@ -338,36 +355,12 @@ main._writeToSocket(socket, '530', ' ', 'Username or password incorrect', connectionInfo, SocketStateAfterWrite.Open)

let newPath = arg
if (newPath.charAt(0) === '/') {
let folder = path.join(basefolder, newPath)
if (fs.existsSync(folder) === true && fs.statSync(folder).isDirectory() === true && main._beginsWith(basefolder, folder) === true) {
if (folder.charAt(folder.length - 1) !== '/') {
folder += '/'
}
if (newPath.charAt(newPath.length - 1) !== '/') {
newPath += '/'
}
absolutePath = folder
relativePath = newPath
return main._writeToSocket(socket, '250', ' ', `CWD successful. "${relativePath}" is current directory`, connectionInfo, SocketStateAfterWrite.Open)
}
} else if (newPath !== '..') {
let folder = path.join(basefolder, relativePath, newPath)
if (fs.existsSync(folder) === true && fs.statSync(folder).isDirectory() === true && main._beginsWith(basefolder, folder) === true) {
if (folder.charAt(folder.length - 1) !== '/') {
folder += '/'
}
if (newPath.charAt(newPath.length - 1) !== '/') {
newPath += '/'
}
absolutePath = folder
relativePath += newPath
return main._writeToSocket(socket, '250', ' ', `CWD successful. "${relativePath}" is current directory`, connectionInfo, SocketStateAfterWrite.Open)
}
} else if (newPath === '..') {
if (relativePath !== '/') {
newPath = relativePath.split('/')
newPath.pop()
newPath.pop()
newPath = newPath.join('/') + '/'
const folder = path.join(basefolder, newPath)
if (main._useHdl === false) {
if (newPath.charAt(0) === '/') {
let folder = path.join(basefolder, newPath)
if (fs.existsSync(folder) === true && fs.statSync(folder).isDirectory() === true && main._beginsWith(basefolder, folder) === true) {
if (folder.charAt(folder.length - 1) !== '/') {
folder += '/'
}
if (newPath.charAt(newPath.length - 1) !== '/') {
newPath += '/'
}
absolutePath = folder

@@ -377,4 +370,30 @@ relativePath = newPath

}
} else {
return main._writeToSocket(socket, '250', ' ', `CWD successful. "${relativePath}" is current directory`, connectionInfo, SocketStateAfterWrite.Open)
} else if (newPath !== '..') {
let folder = path.join(basefolder, relativePath, newPath)
if (fs.existsSync(folder) === true && fs.statSync(folder).isDirectory() === true && main._beginsWith(basefolder, folder) === true) {
if (folder.charAt(folder.length - 1) !== '/') {
folder += '/'
}
if (newPath.charAt(newPath.length - 1) !== '/') {
newPath += '/'
}
absolutePath = folder
relativePath += newPath
return main._writeToSocket(socket, '250', ' ', `CWD successful. "${relativePath}" is current directory`, connectionInfo, SocketStateAfterWrite.Open)
}
} else if (newPath === '..') {
if (relativePath !== '/') {
newPath = relativePath.split('/')
newPath.pop()
newPath.pop()
newPath = newPath.join('/') + '/'
const folder = path.join(basefolder, newPath)
if (fs.existsSync(folder) === true && fs.statSync(folder).isDirectory() === true && main._beginsWith(basefolder, folder) === true) {
absolutePath = folder
relativePath = newPath
return main._writeToSocket(socket, '250', ' ', `CWD successful. "${relativePath}" is current directory`, connectionInfo, SocketStateAfterWrite.Open)
}
} else {
return main._writeToSocket(socket, '250', ' ', `CWD successful. "${relativePath}" is current directory`, connectionInfo, SocketStateAfterWrite.Open)
}
}

@@ -395,3 +414,3 @@ }

}
if (fs.existsSync(file) === true && fs.statSync(file).isFile() === true && main._beginsWith(basefolder, file) === true) {
if (main._useHdl === false && fs.existsSync(file) === true && fs.statSync(file).isFile() === true && main._beginsWith(basefolder, file) === true) {
main._writeToSocket(socket, '213', ' ', `${fs.statSync(file).size.toString()}`, connectionInfo, SocketStateAfterWrite.Open)

@@ -413,3 +432,3 @@ } else {

}
if (fs.existsSync(file) === true && fs.statSync(file).isFile() === true && main._beginsWith(basefolder, file) === true) {
if (main._useHdl === false && fs.existsSync(file) === true && fs.statSync(file).isFile() === true && main._beginsWith(basefolder, file) === true) {
if (allowFileDelete) {

@@ -437,3 +456,3 @@ fs.unlinkSync(file)

}
if (allowFolderDelete && main._beginsWith(basefolder, folder) === true) {
if (main._useHdl === false && allowFolderDelete && main._beginsWith(basefolder, folder) === true) {
if (fs.existsSync(folder) === true && fs.statSync(folder).isDirectory() === true) {

@@ -460,3 +479,3 @@ fs.rmSync(folder, { force: true, recursive: true })

}
if (allowFolderCreate && main._beginsWith(basefolder, folder) === true) {
if (main._useHdl === false && allowFolderCreate && main._beginsWith(basefolder, folder) === true) {
if (fs.existsSync(folder) === true && fs.statSync(folder).isDirectory() === true) {

@@ -478,30 +497,35 @@ main._writeToSocket(socket, '550', ' ', 'Folder exists', connectionInfo, SocketStateAfterWrite.Open)

const LIST = function (cmd, arg) {
dataObj.method = function (obj) {
if (obj.dataSocket && obj.cmdSocket && obj.absolutePath) {
dataObj.method = async function (obj) {
if (obj.dataSocket && obj.cmdSocket && obj.absolutePath && obj.relativePath) {
if (asciiOn) {
obj.dataSocket.setEncoding('ascii')
}
const read = fs.readdirSync(obj.absolutePath)
let listData = ''
for (let i = 0; i < read.length; i++) {
let line
const file = path.join(obj.absolutePath, read[i].trim())
const stat = fs.statSync(file)
if (obj.MLSD === true) {
const size = (fs.statSync(file).isDirectory() === true) ? '' : 'size=' + stat.size.toString() + ';'
line = util.format('type=%s;modify=%s;%s %s\r\n', (stat.isDirectory() === true) ? 'dir' : 'file'
, main._getDateForMLSD(stat.mtime)
, size
, read[i].trim())
} else {
let size = (fs.statSync(file).isDirectory() === true) ? '0' : stat.size.toString()
size = new Array(14 - size.length).join(' ') + size
line = util.format('%s 1 %s %s %s %s %s\r\n', (stat.isDirectory() === true) ? 'dr--r--r--' : '-r--r--r--'
, username
, username
, size
, main._getDateForLIST(stat.mtime)
, read[i].trim())
if (main._useHdl === true) {
const data = await main._opt.hdl.list(username, obj.relativePath, obj.MLSD)
data && (listData = data)
} else {
const read = fs.readdirSync(obj.absolutePath)
for (let i = 0; i < read.length; i++) {
let line
const file = path.join(obj.absolutePath, read[i].trim())
const stat = fs.statSync(file)
if (obj.MLSD === true) {
const size = (fs.statSync(file).isDirectory() === true) ? '' : 'size=' + stat.size.toString() + ';'
line = util.format('type=%s;modify=%s;%s %s\r\n', (stat.isDirectory() === true) ? 'dir' : 'file'
, main._getDateForMLSD(stat.mtime)
, size
, read[i].trim())
} else {
let size = (fs.statSync(file).isDirectory() === true) ? '0' : stat.size.toString()
size = new Array(14 - size.length).join(' ') + size
line = util.format('%s 1 %s %s %s %s %s\r\n', (stat.isDirectory() === true) ? 'dr--r--r--' : '-r--r--r--'
, username
, username
, size
, main._getDateForLIST(stat.mtime)
, read[i].trim())
}
listData += line
}
listData += line
}

@@ -512,3 +536,3 @@ if (listData.length === 0) {

main.DebugHandler(`${connectionInfo} LIST response on data channel\r\n${listData}`)
obj.dataSocket.end(listData)
obj.dataSocket.end(Buffer.from(listData))
main._writeToSocket(obj.cmdSocket, '226', ' ', `Successfully transferred "${relativePath}"`, connectionInfo, SocketStateAfterWrite.Open)

@@ -520,2 +544,3 @@ }

dataObj.absolutePath = absolutePath
dataObj.relativePath = relativePath
openDataChannel(dataObj)

@@ -549,11 +574,15 @@ }

main._getDataPort((port) => {
ftpData.listen(port, () => {
main.DebugHandler(`${connectionInfo} listening on ${ftpData.address().port} for data connection`)
dataObj = {}
pasv = true
const p1 = (ftpData.address().port) / 256 | 0
const p2 = (ftpData.address().port) % 256
const pasvData = util.format('Entering passive mode (%s,%d,%d)', localAddr.split('.').join(','), p1, p2)
main._writeToSocket(socket, '227', ' ', `${pasvData}`, connectionInfo, SocketStateAfterWrite.Open)
})
if (port > 0) {
ftpData.listen(port, () => {
main.DebugHandler(`${connectionInfo} listening on ${ftpData.address().port} for data connection`)
dataObj = {}
pasv = true
const p1 = (ftpData.address().port) / 256 | 0
const p2 = (ftpData.address().port) % 256
const pasvData = util.format('Entering passive mode (%s,%d,%d)', localAddr.split('.').join(','), p1, p2)
main._writeToSocket(socket, '227', ' ', `${pasvData}`, connectionInfo, SocketStateAfterWrite.Open)
})
} else {
main._writeToSocket(socket, '501', ' ', 'Passive command failed', connectionInfo, SocketStateAfterWrite.Open)
}
})

@@ -587,9 +616,13 @@ }

main._getDataPort((port) => {
ftpData.listen(port, () => {
main.DebugHandler(`${connectionInfo} listening on ${ftpData.address().port} for data connection`)
dataObj = {}
pasv = true
const pasvData = util.format('Entering extended passive mode (|||%d|)', ftpData.address().port)
main._writeToSocket(socket, '229', ' ', `${pasvData}`, connectionInfo, SocketStateAfterWrite.Open)
})
if (port > 0) {
ftpData.listen(port, () => {
main.DebugHandler(`${connectionInfo} listening on ${ftpData.address().port} for data connection`)
dataObj = {}
pasv = true
const pasvData = util.format('Entering extended passive mode (|||%d|)', ftpData.address().port)
main._writeToSocket(socket, '229', ' ', `${pasvData}`, connectionInfo, SocketStateAfterWrite.Open)
})
} else {
main._writeToSocket(socket, '501', ' ', 'Extended passive command failed', connectionInfo, SocketStateAfterWrite.Open)
}
})

@@ -609,12 +642,22 @@ }

}
if (fs.existsSync(file) === true && fs.statSync(file).isFile() === true && main._beginsWith(basefolder, file) === true) {
if (allowFileRetrieve === false) {
return main._writeToSocket(socket, '550', ' ', `Transfer failed "${relativeFile}"`, connectionInfo, SocketStateAfterWrite.Open)
}
if (main._useHdl === false && fs.existsSync(file) === false) {
main._writeToSocket(socket, '550', ' ', 'File not found', connectionInfo, SocketStateAfterWrite.Open)
}
if (((fs.existsSync(file) === true && fs.statSync(file).isFile() === true) || main._useHdl) && main._beginsWith(basefolder, file) === true) {
dataObj.method = async function (obj) {
if (obj.dataSocket && obj.cmdSocket && obj.file && obj.relativeFile) {
asciiOn && obj.dataSocket.setEncoding('ascii')
if (obj.handler) {
const data = await obj.handler(username, relativePath, obj.fileName, retrOffset)
if (main._useHdl) {
const data = await main._opt.hdl.download(username, relativePath, obj.fileName, retrOffset)
retrOffset = 0
obj.dataSocket.write(data)
obj.dataSocket.end()
main._writeToSocket(obj.cmdSocket, '226', ' ', `Successfully transferred "${obj.relativeFile}"`, connectionInfo, SocketStateAfterWrite.Open)
if (Buffer.isBuffer(data)) {
obj.dataSocket.end(data)
main._writeToSocket(obj.cmdSocket, '226', ' ', `Successfully transferred "${obj.relativeFile}"`, connectionInfo, SocketStateAfterWrite.Open)
} else {
obj.dataSocket.end()
main._writeToSocket(obj.cmdSocket, '550', ' ', `Transfer failed "${relativeFile}"`, connectionInfo, SocketStateAfterWrite.Open)
}
} else {

@@ -651,6 +694,3 @@ const streamOpts = {

dataObj.relativeFile = relativeFile
dataObj.handler = main._opt.cnf.downloadHandler
openDataChannel(dataObj)
} else {
main._writeToSocket(socket, '550', ' ', 'File not found', connectionInfo, SocketStateAfterWrite.Open)
}

@@ -695,3 +735,2 @@ }

dataObj.relativeFile = relativeFile
dataObj.handler = main._opt.cnf.uploadHandler
dataObj.method = function (obj) {

@@ -702,8 +741,12 @@ if (obj.dataSocket && obj.cmdSocket && obj.relativeFile) {

}
if (obj.handler) {
if (main._useHdl) {
const data = []
obj.dataSocket.on('data', (d) => data.push(d))
obj.dataSocket.on('close', async () => {
await obj.handler(username, relativePath, obj.fileName, Buffer.concat(data), obj.retrOffset)
main._writeToSocket(obj.cmdSocket, '226', ' ', `Successfully transferred "${obj.relativeFile}"`, connectionInfo, SocketStateAfterWrite.Open)
const success = await main._opt.hdl.upload(username, relativePath, obj.fileName, Buffer.concat(data), obj.retrOffset)
if (success === true) {
main._writeToSocket(obj.cmdSocket, '226', ' ', `Successfully transferred "${obj.relativeFile}"`, connectionInfo, SocketStateAfterWrite.Open)
} else {
main._writeToSocket(obj.cmdSocket, '550', ' ', `Transfer failed "${relativeFile}"`, connectionInfo, SocketStateAfterWrite.Open)
}
})

@@ -719,3 +762,3 @@ } else if (obj.stream) {

}
if (dataObj.handler) {
if (main._useHdl) {
dataObj.retrOffset = retrOffset

@@ -782,3 +825,3 @@ retrOffset = 0

}
if (fs.existsSync(file) === true && fs.statSync(file).isFile() === true && main._beginsWith(basefolder, file) === true) {
if (((fs.existsSync(file) === true && fs.statSync(file).isFile() === true) || main._useHdl === true) && main._beginsWith(basefolder, file) === true) {
renameFrom = file

@@ -794,3 +837,3 @@ main._writeToSocket(socket, '350', ' ', 'File exists', connectionInfo, SocketStateAfterWrite.Open)

*/
const RNTO = function (cmd, arg) {
const RNTO = async function (cmd, arg) {
const relativeFile = arg

@@ -803,6 +846,14 @@ let file

}
if (fs.existsSync(file) === false && main._beginsWith(basefolder, file) === true) {
if (main._useHdl === false && fs.existsSync(file) === false && main._beginsWith(basefolder, file) === true) {
fs.renameSync(renameFrom, file)
renameFrom = ''
main._writeToSocket(socket, '250', ' ', 'File renamed successfully', connectionInfo, SocketStateAfterWrite.Open)
} else if (main._useHdl === true) {
const success = await main._opt.hdl.rename(username, relativePath, path.basename(renameFrom), relativeFile)
renameFrom = ''
if (success === true) {
main._writeToSocket(socket, '250', ' ', 'File renamed successfully', connectionInfo, SocketStateAfterWrite.Open)
} else {
main._writeToSocket(socket, '550', ' ', 'File rename failed', connectionInfo, SocketStateAfterWrite.Open)
}
} else {

@@ -824,6 +875,8 @@ main._writeToSocket(socket, '550', ' ', 'File already exists', connectionInfo, SocketStateAfterWrite.Open)

}
if (fs.existsSync(file) === true && fs.statSync(file).isFile() === true && main._beginsWith(basefolder, file) === true) {
if (main._useHdl === false && fs.existsSync(file) === true && fs.statSync(file).isFile() === true && main._beginsWith(basefolder, file) === true) {
const mtime = main._getDateForMFMT(time)
fs.utimesSync(file, mtime, mtime)
main._writeToSocket(socket, '253', ' ', 'Date/time changed okay', connectionInfo, SocketStateAfterWrite.Open)
} else if (main._useHdl === true) {
main._writeToSocket(socket, '253', ' ', 'Date/time changed okay', connectionInfo, SocketStateAfterWrite.Open)
} else {

@@ -897,2 +950,3 @@ main._writeToSocket(socket, '550', ' ', 'File does not exist', connectionInfo, SocketStateAfterWrite.Open)

allowFileCreate = main._opt.cnf.allowAnonymousFileCreate
allowFileRetrieve = main._opt.cnf.allowAnonymousFileRetrieve
allowFileOverwrite = main._opt.cnf.allowAnonymousFileOverwrite

@@ -926,2 +980,3 @@ allowFileDelete = main._opt.cnf.allowAnonymousFileDelete

Object.prototype.hasOwnProperty.call(obj, 'allowUserFileCreate') && (allowFileCreate = obj.allowUserFileCreate)
Object.prototype.hasOwnProperty.call(obj, 'allowUserFileRetrieve') && (allowFileRetrieve = obj.allowUserFileRetrieve)
Object.prototype.hasOwnProperty.call(obj, 'allowUserFileOverwrite') && (allowFileOverwrite = obj.allowUserFileOverwrite)

@@ -969,3 +1024,3 @@ Object.prototype.hasOwnProperty.call(obj, 'allowUserFileDelete') && (allowFileDelete = obj.allowUserFileDelete)

dataChannel.on('error', main.ErrorHandler)
dataChannel.maxConnections = main._opt.cnf.maxConnections
dataChannel.maxConnections = 1
return dataChannel

@@ -1002,2 +1057,10 @@ }

_informLogin (username, remoteAddr) {
this.emit('login', { user: username, address: remoteAddr, total: Object.keys(this.openSockets).length })
}
_informLogoff (username, remoteAddr) {
this.emit('logoff', { user: username, address: remoteAddr, total: Object.keys(this.openSockets).length })
}
_writeToSocket (socket, code, delimiter, message, connectionInfo, socketState) {

@@ -1014,7 +1077,8 @@ this.LogHandler(`${connectionInfo} > ${code}${delimiter}${message}`)

_getDataPort (resolve) {
if (this._opt.cnf.minDataPort > 0 && this._opt.cnf.minDataPort < 65535) {
const config = this._opt.cnf
if (config.minDataPort > 0 && config.minDataPort < 65535) {
const testPort = function (port) {
const server = net.createServer()
server.once('error', function (_err) {
if (port > (this._opt.cnf.minDataPort + this._opt.cnf.maxConnections)) {
if (port >= (config.minDataPort + config.maxConnections)) {
resolve(0)

@@ -1033,3 +1097,3 @@ } else {

}
testPort(this._opt.cnf.minDataPort)
testPort(config.minDataPort)
} else {

@@ -1036,0 +1100,0 @@ resolve(0)

{
"name": "jsftpd",
"id": "jsftpd",
"version": "1.1.1",
"version": "1.2.0",
"description": "FTP server for node.js",

@@ -43,3 +43,3 @@ "main": "index.js",

"install-dev": "npm install --save-dev && husky install",
"test": "jest"
"test": "jest --runInBand"
},

@@ -46,0 +46,0 @@ "license": "MIT",

@@ -27,7 +27,4 @@ # jsftpd

const server = new ftpd({cnf: {username: 'john', password: 'doe'})
const server = new ftpd({cnf: {username: 'john', password: 'doe', basefolder: '/tmp'})
server.on('log', console.log)
server.on('error', console.error)
server.start()

@@ -42,2 +39,3 @@ ```

- `tls` property object. Takes any configuration option as per node.js tls.createServer [options](https://nodejs.org/api/tls.html#tlscreateserveroptions-secureconnectionlistener)
- `cnf` property object. Takes jsftpd specific configuration items. See full documentation [here](https://jsftpd.readthedocs.io/en/latest/)
- `cnf` property object. Takes jsftpd specific configuration items. See full documentation [here](https://jsftpd.readthedocs.io/en/latest/options.html#cnf)
- `hdl` property object. Takes handler functions for specific FTP commands. See full documentation [here](https://jsftpd.readthedocs.io/en/latest/options.html#hdl)

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc