ssh2-sftp-client
Advanced tools
Comparing version 4.0.4 to 4.1.0
{ | ||
"name": "ssh2-sftp-client", | ||
"version": "4.0.4", | ||
"version": "4.1.0", | ||
"description": "ssh2 sftp client for node", | ||
@@ -5,0 +5,0 @@ "main": "src/index.js", |
245
README.md
# Table of Contents | ||
1. [SSH2 SFTP Client](#org9c08e76) | ||
2. [Installation](#org3f4f406) | ||
3. [Basic Usage](#org93a40da) | ||
4. [Breaking Changes in Version 4.0.0](#org18495a3) | ||
5. [Documentation](#org3222656) | ||
1. [Methods](#org96b12ba) | ||
1. [connect(config) ===> SFTPstream](#orgd70af09) | ||
2. [list(path, pattern) ==> Array[object]](#org6f3a281) | ||
3. [exists(path) ==> boolean](#orgf77e86f) | ||
4. [stat(path) ==> object](#org398bf4f) | ||
5. [get(path, dst, options) ==> String|Stream|Buffer](#org0587f21) | ||
6. [fastGet(remotePath, localPath, options) ===> string](#orgeb63dff) | ||
7. [put(src, remotePath, options) ==> string](#org1fe2c81) | ||
8. [fastPut(localPath, remotePath, options) ==> string](#orgee6b633) | ||
9. [append(input, remotePath, options) ==> string](#orgccf6a54) | ||
10. [mkdir(path, recursive) ==> string](#org0cc89e2) | ||
11. [rmdir(path, recursive) ==> string](#orgca9b1cf) | ||
12. [delete(path) ==> string](#orgb50e5b0) | ||
13. [rename(fromPath, toPath) ==> string](#org3827ade) | ||
14. [chmod(path, mode) ==> string](#org4ac8f13) | ||
15. [end() ==> boolean](#org2c3d2eb) | ||
16. [Add and Remove Listeners](#org6d6ffe5) | ||
6. [FAQ](#org5e4228c) | ||
1. [How can you pass writable stream as dst for get method?](#org8e9aed7) | ||
2. [How can I upload files without having to specify a password?](#org46645bb) | ||
3. [How can I connect through a Socks Proxy](#org785250c) | ||
7. [Change Log](#org9b06f52) | ||
1. [v4.0.4](#org0fd9b56) | ||
2. [v4.0.3](#orgd5633bd) | ||
3. [v4.0.2](#org51ec745) | ||
4. [v4.0.0](#orge421d28) | ||
5. [v2.5.2](#org243214c) | ||
6. [v2.5.1](#org23c6382) | ||
7. [v2.5.0](#orga9175d5) | ||
8. [v2.4.3](#org29bd506) | ||
9. [v2.4.2](#org6d8fc47) | ||
10. [v2.4.1](#org0ea121a) | ||
11. [v2.4.0](#org5f98b56) | ||
12. [v2.3.0](#org5b5e078) | ||
13. [v3.0.0 – deprecate this version](#org03e1923) | ||
14. [v2.1.1](#org38facd8) | ||
15. [v2.0.1](#org56e3cfb) | ||
16. [v1.1.0](#org84a40a5) | ||
17. [v1.0.5:](#org73e9d70) | ||
8. [Logging Issues](#org1e66e09) | ||
9. [Pull Requests](#org08ee895) | ||
10. [Contributors](#org06d8d77) | ||
1. [SSH2 SFTP Client](#org43ca01c) | ||
2. [Installation](#org85ec107) | ||
3. [Basic Usage](#org7ca5e28) | ||
4. [Breaking Changes in Version 4.x](#org11186bf) | ||
5. [Enhancements in Version 4.1.x](#orge5f7732) | ||
6. [Documentation](#org8b8cd18) | ||
1. [Methods](#org820f1d6) | ||
1. [connect(config) ===> SFTPstream](#org83fbd25) | ||
2. [list(path, pattern) ==> Array[object]](#org2871c47) | ||
3. [exists(path) ==> boolean](#org1f19090) | ||
4. [stat(path) ==> object](#org625195b) | ||
5. [get(path, dst, options) ==> String|Stream|Buffer](#org2c1f4a0) | ||
6. [fastGet(remotePath, localPath, options) ===> string](#orgdc896c4) | ||
7. [put(src, remotePath, options) ==> string](#orgb196a2e) | ||
8. [fastPut(localPath, remotePath, options) ==> string](#org243adae) | ||
9. [append(input, remotePath, options) ==> string](#org7d9d07f) | ||
10. [mkdir(path, recursive) ==> string](#orgb972a31) | ||
11. [rmdir(path, recursive) ==> string](#org5db8003) | ||
12. [delete(path) ==> string](#org6301aa9) | ||
13. [rename(fromPath, toPath) ==> string](#orgae0f051) | ||
14. [chmod(path, mode) ==> string](#org462d46f) | ||
15. [realPath(path) ===> string](#orgde5aa73) | ||
16. [cwd() ==> string](#org705f820) | ||
17. [end() ==> boolean](#org40dd1ae) | ||
18. [Add and Remove Listeners](#org169bbdb) | ||
7. [FAQ](#org0dd8098) | ||
1. [How can you pass writable stream as dst for get method?](#org93da735) | ||
2. [How can I upload files without having to specify a password?](#orgde3dfae) | ||
3. [How can I connect through a Socks Proxy](#org97b11c7) | ||
8. [Change Log](#orgcd88ff2) | ||
1. [v4.1.0 (Prod Version)](#org4624fd1) | ||
2. [v4.0.4](#org99482d5) | ||
3. [v4.0.3](#orga917421) | ||
4. [v4.0.2](#orgc8b9bd3) | ||
5. [v4.0.0](#org56a1855) | ||
6. [v2.5.2](#org7c011ac) | ||
7. [v2.5.1](#orgfb00d0f) | ||
8. [v2.5.0](#org175b195) | ||
9. [v2.4.3](#org5c185aa) | ||
10. [v2.4.2](#org70e34eb) | ||
11. [v2.4.1](#orgcc4683d) | ||
12. [v2.4.0](#org4413262) | ||
13. [v2.3.0](#orgd6201fe) | ||
14. [v3.0.0 – deprecate this version](#org24ca85f) | ||
15. [v2.1.1](#org6410067) | ||
16. [v2.0.1](#orgd0190f0) | ||
17. [v1.1.0](#org2007ebd) | ||
18. [v1.0.5:](#org6eb1f80) | ||
9. [Logging Issues](#orgec61686) | ||
10. [Pull Requests](#orgb91eb96) | ||
11. [Contributors](#orgdd815db) | ||
<a id="org9c08e76"></a> | ||
<a id="org43ca01c"></a> | ||
@@ -63,3 +67,3 @@ # SSH2 SFTP Client | ||
Current stable release is **v4.0.3**. | ||
Current stable release is **v4.1.0**. | ||
@@ -71,3 +75,3 @@ Code has been tested against Node versions 8.16.1, 10.16.3 and 12.9.1 | ||
<a id="org3f4f406"></a> | ||
<a id="org85ec107"></a> | ||
@@ -79,3 +83,3 @@ # Installation | ||
<a id="org93a40da"></a> | ||
<a id="org7ca5e28"></a> | ||
@@ -101,5 +105,5 @@ # Basic Usage | ||
<a id="org18495a3"></a> | ||
<a id="org11186bf"></a> | ||
# Breaking Changes in Version 4.0.0 | ||
# Breaking Changes in Version 4.x | ||
@@ -130,4 +134,17 @@ There has been minor changes to the API signatures | ||
<a id="org3222656"></a> | ||
<a id="orge5f7732"></a> | ||
# Enhancements in Version 4.1.x | ||
- Some of the data upload/download methods would create an empty destination | ||
file when the source file did not exist. This has now been fixed. | ||
- Handling of relative path names was weak and inconsistent. This has now been | ||
made more consistent and reliable. Two new methods `realPath()` and `cwd()` | ||
have been added | ||
- More error checking and provision of error messages with more meaningful | ||
information. Expansion and enhancements of test cases. | ||
<a id="org8b8cd18"></a> | ||
# Documentation | ||
@@ -142,3 +159,3 @@ | ||
<a id="org96b12ba"></a> | ||
<a id="org820f1d6"></a> | ||
@@ -148,3 +165,3 @@ ## Methods | ||
<a id="orgd70af09"></a> | ||
<a id="org83fbd25"></a> | ||
@@ -219,3 +236,3 @@ ### connect(config) ===> SFTPstream | ||
<a id="org6f3a281"></a> | ||
<a id="org2871c47"></a> | ||
@@ -293,3 +310,3 @@ ### list(path, pattern) ==> Array[object] | ||
<a id="orgf77e86f"></a> | ||
<a id="org1f19090"></a> | ||
@@ -329,3 +346,3 @@ ### exists(path) ==> boolean | ||
<a id="org398bf4f"></a> | ||
<a id="org625195b"></a> | ||
@@ -377,3 +394,3 @@ ### stat(path) ==> object | ||
<a id="org0587f21"></a> | ||
<a id="org2c1f4a0"></a> | ||
@@ -438,3 +455,3 @@ ### get(path, dst, options) ==> String|Stream|Buffer | ||
<a id="orgeb63dff"></a> | ||
<a id="orgdc896c4"></a> | ||
@@ -482,3 +499,3 @@ ### fastGet(remotePath, localPath, options) ===> string | ||
<a id="org1fe2c81"></a> | ||
<a id="orgb196a2e"></a> | ||
@@ -536,3 +553,3 @@ ### put(src, remotePath, options) ==> string | ||
<a id="orgee6b633"></a> | ||
<a id="org243adae"></a> | ||
@@ -580,3 +597,3 @@ ### fastPut(localPath, remotePath, options) ==> string | ||
<a id="orgccf6a54"></a> | ||
<a id="org7d9d07f"></a> | ||
@@ -590,3 +607,3 @@ ### append(input, remotePath, options) ==> string | ||
- **input:** string | readStream. Data to append to remote file | ||
- **input:** buffer | readStream. Data to append to remote file | ||
- **remotePath:** string. Path to remote file | ||
@@ -627,3 +644,3 @@ - **options:** object. Options to pass to writeStream (see below) | ||
<a id="org0cc89e2"></a> | ||
<a id="orgb972a31"></a> | ||
@@ -657,3 +674,3 @@ ### mkdir(path, recursive) ==> string | ||
<a id="orgca9b1cf"></a> | ||
<a id="org5db8003"></a> | ||
@@ -688,3 +705,3 @@ ### rmdir(path, recursive) ==> string | ||
<a id="orgb50e5b0"></a> | ||
<a id="org6301aa9"></a> | ||
@@ -714,3 +731,3 @@ ### delete(path) ==> string | ||
<a id="org3827ade"></a> | ||
<a id="orgae0f051"></a> | ||
@@ -740,3 +757,3 @@ ### rename(fromPath, toPath) ==> string | ||
<a id="org4ac8f13"></a> | ||
<a id="org462d46f"></a> | ||
@@ -769,4 +786,21 @@ ### chmod(path, mode) ==> string | ||
<a id="org2c3d2eb"></a> | ||
<a id="orgde5aa73"></a> | ||
### realPath(path) ===> string | ||
Converts a relative path to an absolute path on the remote server. This method | ||
is mainly used internally to resolve remote path names. | ||
- **path:** A file path, either relative or absolute | ||
<a id="org705f820"></a> | ||
### cwd() ==> string | ||
Returns what the server believes is the current remote working directory. | ||
<a id="org40dd1ae"></a> | ||
### end() ==> boolean | ||
@@ -793,3 +827,3 @@ | ||
<a id="org6d6ffe5"></a> | ||
<a id="org169bbdb"></a> | ||
@@ -820,3 +854,3 @@ ### Add and Remove Listeners | ||
<a id="org5e4228c"></a> | ||
<a id="org0dd8098"></a> | ||
@@ -826,3 +860,3 @@ # FAQ | ||
<a id="org8e9aed7"></a> | ||
<a id="org93da735"></a> | ||
@@ -891,3 +925,3 @@ ## How can you pass writable stream as dst for get method? | ||
<a id="org46645bb"></a> | ||
<a id="orgde3dfae"></a> | ||
@@ -927,3 +961,3 @@ ## How can I upload files without having to specify a password? | ||
<a id="org785250c"></a> | ||
<a id="org97b11c7"></a> | ||
@@ -963,3 +997,3 @@ ## How can I connect through a Socks Proxy | ||
<a id="org9b06f52"></a> | ||
<a id="orgcd88ff2"></a> | ||
@@ -969,4 +1003,21 @@ # Change Log | ||
<a id="org0fd9b56"></a> | ||
<a id="org4624fd1"></a> | ||
## v4.1.0 (Prod Version) | ||
- move `end()` call to resolve into close hook | ||
- Prevent `put()` and `get()` from creating empty files in destination when | ||
unable to read source | ||
- Expand tests for operations when lacking required permissions | ||
- Add additional data checks for `append()` | ||
- Verify file exists | ||
- Verify file is writeable | ||
- Verify file is a regular file | ||
- Fix handling of relative paths | ||
- Add `realPath()` method | ||
- Add `cwd()` method | ||
<a id="org99482d5"></a> | ||
## v4.0.4 | ||
@@ -978,3 +1029,3 @@ | ||
<a id="orgd5633bd"></a> | ||
<a id="orga917421"></a> | ||
@@ -987,3 +1038,3 @@ ## v4.0.3 | ||
<a id="org51ec745"></a> | ||
<a id="orgc8b9bd3"></a> | ||
@@ -995,3 +1046,3 @@ ## v4.0.2 | ||
<a id="orge421d28"></a> | ||
<a id="org56a1855"></a> | ||
@@ -1014,3 +1065,3 @@ ## v4.0.0 | ||
<a id="org243214c"></a> | ||
<a id="org7c011ac"></a> | ||
@@ -1023,3 +1074,3 @@ ## v2.5.2 | ||
<a id="org23c6382"></a> | ||
<a id="orgfb00d0f"></a> | ||
@@ -1031,3 +1082,3 @@ ## v2.5.1 | ||
<a id="orga9175d5"></a> | ||
<a id="org175b195"></a> | ||
@@ -1039,3 +1090,3 @@ ## v2.5.0 | ||
<a id="org29bd506"></a> | ||
<a id="org5c185aa"></a> | ||
@@ -1048,3 +1099,3 @@ ## v2.4.3 | ||
<a id="org6d8fc47"></a> | ||
<a id="org70e34eb"></a> | ||
@@ -1057,3 +1108,3 @@ ## v2.4.2 | ||
<a id="org0ea121a"></a> | ||
<a id="orgcc4683d"></a> | ||
@@ -1066,3 +1117,3 @@ ## v2.4.1 | ||
<a id="org5f98b56"></a> | ||
<a id="org4413262"></a> | ||
@@ -1080,3 +1131,3 @@ ## v2.4.0 | ||
<a id="org5b5e078"></a> | ||
<a id="orgd6201fe"></a> | ||
@@ -1090,3 +1141,3 @@ ## v2.3.0 | ||
<a id="org03e1923"></a> | ||
<a id="org24ca85f"></a> | ||
@@ -1099,3 +1150,3 @@ ## v3.0.0 – deprecate this version | ||
<a id="org38facd8"></a> | ||
<a id="org6410067"></a> | ||
@@ -1108,3 +1159,3 @@ ## v2.1.1 | ||
<a id="org56e3cfb"></a> | ||
<a id="orgd0190f0"></a> | ||
@@ -1119,3 +1170,3 @@ ## v2.0.1 | ||
<a id="org84a40a5"></a> | ||
<a id="org2007ebd"></a> | ||
@@ -1127,3 +1178,3 @@ ## v1.1.0 | ||
<a id="org73e9d70"></a> | ||
<a id="org6eb1f80"></a> | ||
@@ -1136,3 +1187,3 @@ ## v1.0.5: | ||
<a id="org1e66e09"></a> | ||
<a id="orgec61686"></a> | ||
@@ -1146,3 +1197,3 @@ # Logging Issues | ||
<a id="org08ee895"></a> | ||
<a id="orgb91eb96"></a> | ||
@@ -1167,3 +1218,3 @@ # Pull Requests | ||
<a id="org06d8d77"></a> | ||
<a id="orgdd815db"></a> | ||
@@ -1170,0 +1221,0 @@ # Contributors |
776
src/index.js
@@ -11,3 +11,3 @@ /** | ||
const retry = require('retry'); | ||
const {join, posix} = require('path'); | ||
const {join, posix, normalize} = require('path'); | ||
@@ -30,2 +30,4 @@ let SftpClient = function() { | ||
//console.dir(err); | ||
if (typeof err === 'string') { | ||
@@ -81,57 +83,20 @@ msg = `${name}: ${err}`; | ||
/** | ||
* Retrieves a directory listing. The pattern argument may be a regular expression | ||
* or simple 'glob' style *. | ||
* | ||
* @param {String} path, a string containing the path to a directory | ||
* @param {regex} pattern - An optional pattern used to filter the list | ||
* @return {Promise} data, list info | ||
*/ | ||
SftpClient.prototype.list = function(path, pattern = /.*/) { | ||
const reg = /-/gi; | ||
SftpClient.prototype.realPath = function(path) { | ||
const sftp = this.sftp; | ||
return new Promise((resolve, reject) => { | ||
let sftp = this.sftp; | ||
try { | ||
if (!sftp) { | ||
reject(formatError('No SFTP connection available', 'sftp.list')); | ||
return reject( | ||
formatError('No SFTP connection available', 'sftp.realPath') | ||
); | ||
} | ||
sftp.readdir(path, (err, list) => { | ||
sftp.realpath(path, (err, absPath) => { | ||
if (err) { | ||
reject(formatError(err, 'sftp.list')); | ||
} else { | ||
let newList = []; | ||
// reset file info | ||
if (list) { | ||
newList = list.map(item => { | ||
return { | ||
type: item.longname.substr(0, 1), | ||
name: item.filename, | ||
size: item.attrs.size, | ||
modifyTime: item.attrs.mtime * 1000, | ||
accessTime: item.attrs.atime * 1000, | ||
rights: { | ||
user: item.longname.substr(1, 3).replace(reg, ''), | ||
group: item.longname.substr(4, 3).replace(reg, ''), | ||
other: item.longname.substr(7, 3).replace(reg, '') | ||
}, | ||
owner: item.attrs.uid, | ||
group: item.attrs.gid | ||
}; | ||
}); | ||
} | ||
// provide some compatibility for auxList | ||
let regex; | ||
if (pattern instanceof RegExp) { | ||
regex = pattern; | ||
} else { | ||
let newPattern = pattern.replace(/\*([^*])*?/gi, '.*'); | ||
regex = new RegExp(newPattern); | ||
} | ||
resolve(newList.filter(item => regex.test(item.name))); | ||
reject(formatError(`${err.message} ${path}`, 'sftp.realPath')); | ||
} | ||
resolve(absPath); | ||
}); | ||
} catch (err) { | ||
reject(formatError(err, 'sftp.list')); | ||
reject(formatError(`${err.message} ${path}`, 'sftp.realPath')); | ||
} | ||
@@ -141,3 +106,74 @@ }); | ||
SftpClient.prototype.cwd = function() { | ||
return this.realPath('.'); | ||
}; | ||
SftpClient.prototype._list = function(path, pattern = /.*/) { | ||
const _this = this; | ||
const reg = /-/gi; | ||
return new Promise((resolve, reject) => { | ||
const sftp = _this.sftp; | ||
sftp.readdir(path, (err, list) => { | ||
if (err) { | ||
reject(formatError(err, 'sftp.list')); | ||
} else { | ||
let newList = []; | ||
// reset file info | ||
if (list) { | ||
newList = list.map(item => { | ||
return { | ||
type: item.longname.substr(0, 1), | ||
name: item.filename, | ||
size: item.attrs.size, | ||
modifyTime: item.attrs.mtime * 1000, | ||
accessTime: item.attrs.atime * 1000, | ||
rights: { | ||
user: item.longname.substr(1, 3).replace(reg, ''), | ||
group: item.longname.substr(4, 3).replace(reg, ''), | ||
other: item.longname.substr(7, 3).replace(reg, '') | ||
}, | ||
owner: item.attrs.uid, | ||
group: item.attrs.gid | ||
}; | ||
}); | ||
} | ||
// provide some compatibility for auxList | ||
let regex; | ||
if (pattern instanceof RegExp) { | ||
regex = pattern; | ||
} else { | ||
let newPattern = pattern.replace(/\*([^*])*?/gi, '.*'); | ||
regex = new RegExp(newPattern); | ||
} | ||
resolve(newList.filter(item => regex.test(item.name))); | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Retrieves a directory listing. The pattern argument may be a regular expression | ||
* or simple 'glob' style *. | ||
* | ||
* @param {String} path, a string containing the path to a directory | ||
* @param {regex} pattern - An optional pattern used to filter the list | ||
* @return {Promise} data, list info | ||
*/ | ||
SftpClient.prototype.list = async function(path, pattern = /.*/) { | ||
try { | ||
if (!this.sftp) { | ||
return Promise.reject( | ||
formatError('No SFTP connection available', 'sftp.list') | ||
); | ||
} | ||
let absPath = await this.realPath(path); | ||
return this._list(absPath, pattern); | ||
} catch (err) { | ||
return Promise.reject(formatError(err, 'sftp.list')); | ||
} | ||
}; | ||
/** | ||
* Retrieves a directory listing with a filter | ||
@@ -158,2 +194,29 @@ * | ||
SftpClient.prototype._exists = function(path) { | ||
return new Promise((resolve, reject) => { | ||
const sftp = this.sftp; | ||
let {dir, base} = posix.parse(path); | ||
sftp.readdir(dir, (err, list) => { | ||
if (err) { | ||
if (err.code === 2) { | ||
resolve(false); | ||
} else { | ||
reject(formatError(err, 'sftp.exists')); | ||
} | ||
} else { | ||
let [type] = list | ||
.filter(item => item.filename === base) | ||
.map(item => item.longname.substr(0, 1)); | ||
if (type) { | ||
resolve(type); | ||
} else { | ||
resolve(false); | ||
} | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
@@ -170,36 +233,44 @@ * @async | ||
*/ | ||
SftpClient.prototype.exists = function(path) { | ||
SftpClient.prototype.exists = async function(remotePath) { | ||
try { | ||
if (!this.sftp) { | ||
return Promise.reject( | ||
formatError('No SFTP connection available', 'sftp.exists') | ||
); | ||
} | ||
let absPath = await this.realPath(remotePath); | ||
return this._exists(absPath); | ||
} catch (err) { | ||
if (err.message.match(/No such file/)) { | ||
return Promise.resolve(false); | ||
} | ||
return Promise.reject(formatError(err, 'sftp.exists')); | ||
} | ||
}; | ||
SftpClient.prototype._stat = function(remotePath) { | ||
return new Promise((resolve, reject) => { | ||
let sftp = this.sftp; | ||
const sftp = this.sftp; | ||
try { | ||
if (!sftp) { | ||
reject(formatError('No SFTP connection available', 'sftp.exists')); | ||
sftp.stat(remotePath, function(err, stats) { | ||
if (err) { | ||
reject(formatError(err, 'sftp.stat')); | ||
} else { | ||
resolve({ | ||
mode: stats.mode, | ||
uid: stats.uid, | ||
gid: stats.gid, | ||
size: stats.size, | ||
accessTime: stats.atime * 1000, | ||
modifyTime: stats.mtime * 1000, | ||
isDirectory: stats.isDirectory(), | ||
isFile: stats.isFile(), | ||
isBlockDevice: stats.isBlockDevice(), | ||
isCharacterDevice: stats.isCharacterDevice(), | ||
isSymbolicLink: stats.isSymbolicLink(), | ||
isFIFO: stats.isFIFO(), | ||
isSocket: stats.isSocket() | ||
}); | ||
} | ||
let {dir, base} = posix.parse(path); | ||
if (base === '.') { | ||
// the '.' directory exists by definition | ||
resolve('d'); | ||
} | ||
sftp.readdir(dir, (err, list) => { | ||
if (err) { | ||
if (err.code === 2) { | ||
resolve(false); | ||
} else { | ||
reject(formatError(err, 'sftp.exists')); | ||
} | ||
} else { | ||
let [type] = list | ||
.filter(item => item.filename === base) | ||
.map(item => item.longname.substr(0, 1)); | ||
if (type) { | ||
resolve(type); | ||
} else { | ||
resolve(false); | ||
} | ||
} | ||
}); | ||
} catch (err) { | ||
reject(formatError(err, 'sftp.exists')); | ||
} | ||
}); | ||
}); | ||
@@ -214,33 +285,55 @@ }; | ||
*/ | ||
SftpClient.prototype.stat = function(remotePath) { | ||
SftpClient.prototype.stat = async function(remotePath) { | ||
try { | ||
if (!this.sftp) { | ||
return Promise.reject( | ||
formatError('No SFTP connection available', 'sftp.stat') | ||
); | ||
} | ||
let absPath = await this.realPath(remotePath); | ||
return this._stat(absPath); | ||
} catch (err) { | ||
return Promise.reject(formatError(err, 'sftp.stat')); | ||
} | ||
}; | ||
SftpClient.prototype._get = function(path, dst, options) { | ||
return new Promise((resolve, reject) => { | ||
let sftp = this.sftp; | ||
const sftp = this.sftp; | ||
try { | ||
if (!sftp) { | ||
reject(formatError('No SFTP connection available', 'sftp.stat')); | ||
let rdr = sftp.createReadStream(path, options); | ||
rdr.on('error', err => { | ||
removeListeners(rdr); | ||
reject(formatError(err, 'sftp.get')); | ||
}); | ||
if (dst === undefined) { | ||
// no dst specified, return buffer of data | ||
let concatStream = concat(buff => { | ||
rdr.removeAllListeners('error'); | ||
resolve(buff); | ||
}); | ||
rdr.pipe(concatStream); | ||
} else { | ||
let wtr; | ||
if (typeof dst === 'string') { | ||
// dst local file path | ||
wtr = fs.createWriteStream(dst); | ||
} else { | ||
// assume dst is a writeable | ||
wtr = dst; | ||
} | ||
sftp.stat(remotePath, function(err, stats) { | ||
if (err) { | ||
reject(formatError(err, 'sftp.stat')); | ||
wtr.on('error', err => { | ||
removeListeners(rdr); | ||
reject(formatError(err, 'sftp.get')); | ||
}); | ||
wtr.on('finish', () => { | ||
removeListeners(rdr); | ||
if (typeof dst === 'string') { | ||
resolve(dst); | ||
} else { | ||
resolve({ | ||
mode: stats.mode, | ||
uid: stats.uid, | ||
gid: stats.gid, | ||
size: stats.size, | ||
accessTime: stats.atime * 1000, | ||
modifyTime: stats.mtime * 1000, | ||
isDirectory: stats.isDirectory(), | ||
isFile: stats.isFile(), | ||
isBlockDevice: stats.isBlockDevice(), | ||
isCharacterDevice: stats.isCharacterDevice(), | ||
isSymbolicLink: stats.isSymbolicLink(), | ||
isFIFO: stats.isFIFO(), | ||
isSocket: stats.isSocket() | ||
}); | ||
resolve(wtr); | ||
} | ||
}); | ||
} catch (err) { | ||
reject(formatError(err, 'sftp.stat')); | ||
rdr.pipe(wtr); | ||
} | ||
@@ -264,50 +357,39 @@ }); | ||
*/ | ||
SftpClient.prototype.get = function(path, dst, options) { | ||
return new Promise((resolve, reject) => { | ||
let sftp = this.sftp; | ||
SftpClient.prototype.get = async function(path, dst, options) { | ||
try { | ||
if (!this.sftp) { | ||
return Promise.reject( | ||
formatError('No SFTP connection available', 'sftp.get') | ||
); | ||
} | ||
try { | ||
if (!sftp) { | ||
reject(formatError('No SFTP connection available', 'sftp.get')); | ||
} | ||
let rdr = sftp.createReadStream(path, options); | ||
let absPath = await this.realPath(path); | ||
rdr.on('error', err => { | ||
removeListeners(rdr); | ||
reject(formatError(err, 'sftp.get')); | ||
}); | ||
let stats = await this.stat(absPath); | ||
if ((stats.mode & 0o444) === 0) { | ||
return Promise.reject( | ||
formatError(`No read permission for ${absPath}`, 'sftp.get') | ||
); | ||
} | ||
if (dst === undefined) { | ||
// no dst specified, return buffer of data | ||
let concatStream = concat(buff => { | ||
rdr.removeAllListeners('error'); | ||
resolve(buff); | ||
}); | ||
rdr.pipe(concatStream); | ||
} else { | ||
let wtr; | ||
if (typeof dst === 'string') { | ||
// dst local file path | ||
wtr = fs.createWriteStream(dst); | ||
} else { | ||
// assume dst is a writeable | ||
wtr = dst; | ||
} | ||
wtr.on('error', err => { | ||
removeListeners(rdr); | ||
reject(formatError(err, 'sftp.get')); | ||
}); | ||
wtr.on('finish', () => { | ||
removeListeners(rdr); | ||
if (typeof dst === 'string') { | ||
resolve(dst); | ||
} else { | ||
resolve(wtr); | ||
} | ||
}); | ||
rdr.pipe(wtr); | ||
if (typeof dst === 'string') { | ||
let dstPath = normalize(dst); | ||
return this._get(absPath, dstPath, options); | ||
} | ||
return this._get(absPath, dst, options); | ||
} catch (err) { | ||
return Promise.reject(formatError(err, 'sftp.get')); | ||
} | ||
}; | ||
SftpClient.prototype._fastGet = function(remotePath, localPath, options) { | ||
return new Promise((resolve, reject) => { | ||
const sftp = this.sftp; | ||
sftp.fastGet(remotePath, localPath, options, function(err) { | ||
if (err) { | ||
reject(formatError(err, 'sftp.fastGet')); | ||
} | ||
} catch (err) { | ||
reject(formatError(err, 'sftp.get')); | ||
} | ||
resolve(`${remotePath} was successfully download to ${localPath}!`); | ||
}); | ||
}); | ||
@@ -328,19 +410,27 @@ }; | ||
*/ | ||
SftpClient.prototype.fastGet = function(remotePath, localPath, options) { | ||
SftpClient.prototype.fastGet = async function(remotePath, localPath, options) { | ||
try { | ||
if (!this.sftp) { | ||
return Promise.reject( | ||
formatError('No SFTP connection available', 'sftp.fastGet') | ||
); | ||
} | ||
let src = await this.realPath(remotePath); | ||
let dst = normalize(localPath); | ||
return this._fastGet(src, dst, options); | ||
} catch (err) { | ||
return Promise.reject(formatError(err, 'sftp.fastGet')); | ||
} | ||
}; | ||
SftpClient.prototype._fastPut = function(localPath, remotePath, options) { | ||
const sftp = this.sftp; | ||
return new Promise((resolve, reject) => { | ||
let sftp = this.sftp; | ||
try { | ||
if (!sftp) { | ||
reject(formatError('No SFTP connection available', 'sftp.fastGet')); | ||
sftp.fastPut(localPath, remotePath, options, function(err) { | ||
if (err) { | ||
reject(formatError(err, 'sftp.fastPut')); | ||
} | ||
sftp.fastGet(remotePath, localPath, options, function(err) { | ||
if (err) { | ||
reject(formatError(err, 'sftp.fastGet')); | ||
} | ||
resolve(`${remotePath} was successfully download to ${localPath}!`); | ||
}); | ||
} catch (err) { | ||
reject(formatError(err, 'sftp.fastGet')); | ||
} | ||
resolve(`${localPath} was successfully uploaded to ${remotePath}!`); | ||
}); | ||
}); | ||
@@ -362,18 +452,54 @@ }; | ||
*/ | ||
SftpClient.prototype.fastPut = function(localPath, remotePath, options) { | ||
SftpClient.prototype.fastPut = async function(localPath, remotePath, options) { | ||
try { | ||
if (!this.sftp) { | ||
return Promise.reject( | ||
formatError('No SFTP connection available', 'sftp.fastPut') | ||
); | ||
} | ||
let src = fs.realpathSync(localPath); | ||
let dst = remotePath; | ||
if (dst.startsWith('../')) { | ||
let root = await this.realPath('..'); | ||
dst = join(root, dst.substring(3)); | ||
} else if (dst.startsWith('./')) { | ||
let root = await this.realPath('.'); | ||
dst = join(root, dst.substring(2)); | ||
} | ||
return this._fastPut(src, dst, options); | ||
} catch (err) { | ||
return Promise.reject(formatError(err, 'sftp.fastPut')); | ||
} | ||
}; | ||
SftpClient.prototype._put = function(src, remotePath, options) { | ||
return new Promise((resolve, reject) => { | ||
let sftp = this.sftp; | ||
const sftp = this.sftp; | ||
try { | ||
if (!sftp) { | ||
reject(formatError('No SFTP connection available', 'sftp.fastPut')); | ||
let stream = sftp.createWriteStream(remotePath, options); | ||
stream.on('error', err => { | ||
reject(formatError(err, 'sftp.put')); | ||
}); | ||
stream.on('finish', () => { | ||
removeListeners(stream); | ||
resolve(`Uploaded data stream to ${remotePath}`); | ||
}); | ||
if (src instanceof Buffer) { | ||
stream.end(src); | ||
} else { | ||
let rdr; | ||
if (typeof src === 'string') { | ||
rdr = fs.createReadStream(src); | ||
} else { | ||
rdr = src; | ||
} | ||
sftp.fastPut(localPath, remotePath, options, function(err) { | ||
if (err) { | ||
reject(formatError(err, 'sftp.fastPut')); | ||
} | ||
resolve(`${localPath} was successfully uploaded to ${remotePath}!`); | ||
rdr.on('error', err => { | ||
removeListeners(stream); | ||
reject(formatError(err, 'sftp.put')); | ||
}); | ||
} catch (err) { | ||
reject(formatError(err, 'sftp.fastPut')); | ||
rdr.pipe(stream); | ||
} | ||
@@ -394,39 +520,59 @@ }); | ||
*/ | ||
SftpClient.prototype.put = function(src, remotePath, options) { | ||
SftpClient.prototype.put = async function(localSrc, remotePath, options) { | ||
try { | ||
if (!this.sftp) { | ||
return Promise.reject( | ||
formatError('No SFTP connections available', 'sftp.put') | ||
); | ||
} | ||
let src = localSrc; | ||
if (typeof src === 'string') { | ||
src = fs.realpathSync(src); | ||
fs.accessSync(src, fs.constants.R_OK); | ||
} | ||
let dst = remotePath; | ||
if (dst.startsWith('../')) { | ||
let root = await this.realPath('..'); | ||
dst = join(root, dst.substring(3)); | ||
} else if (dst.startsWith('./')) { | ||
let root = await this.realPath('.'); | ||
dst = join(root, dst.substring(2)); | ||
} | ||
return this._put(src, dst, options); | ||
} catch (err) { | ||
return Promise.reject(formatError(err, 'sftp.put')); | ||
} | ||
}; | ||
SftpClient.prototype._append = function(input, remotePath, options) { | ||
return new Promise((resolve, reject) => { | ||
let sftp = this.sftp; | ||
const sftp = this.sftp; | ||
try { | ||
if (!sftp) { | ||
reject(new Error('sftp.put: No SFTP connections available')); | ||
} | ||
let writerOptions; | ||
let stream = sftp.createWriteStream(remotePath, options); | ||
if (options) { | ||
writerOptions = options; | ||
writerOptions.flags = 'a'; | ||
} else { | ||
writerOptions = { | ||
flags: 'a' | ||
}; | ||
} | ||
stream.on('error', err => { | ||
reject(formatError(err, 'sftp.put')); | ||
}); | ||
let stream = sftp.createWriteStream(remotePath, writerOptions); | ||
stream.on('finish', () => { | ||
removeListeners(stream); | ||
resolve(`Uploaded data stream to ${remotePath}`); | ||
}); | ||
stream.on('error', err => { | ||
removeListeners(stream); | ||
reject(formatError(err, 'sftp.append')); | ||
}); | ||
if (src instanceof Buffer) { | ||
stream.end(src); | ||
} else { | ||
let rdr; | ||
if (typeof src === 'string') { | ||
rdr = fs.createReadStream(src); | ||
} else { | ||
rdr = src; | ||
} | ||
rdr.on('error', err => { | ||
removeListeners(stream); | ||
reject(formatError(err, 'sftp.put')); | ||
}); | ||
rdr.pipe(stream); | ||
} | ||
} catch (err) { | ||
reject(formatError(err, 'sftp.put')); | ||
stream.on('finish', () => { | ||
removeListeners(stream); | ||
resolve(`sftp.append: Uploaded data stream to ${remotePath}`); | ||
}); | ||
if (input instanceof Buffer) { | ||
stream.end(input); | ||
} else { | ||
input.pipe(stream); | ||
} | ||
@@ -444,35 +590,33 @@ }); | ||
*/ | ||
SftpClient.prototype.append = function(input, remotePath, options) { | ||
return new Promise((resolve, reject) => { | ||
let sftp = this.sftp; | ||
try { | ||
if (!sftp) { | ||
reject(formatError('No SFTP connection available', 'sftp.append')); | ||
} | ||
if (typeof input === 'string') { | ||
reject(formatError('Cannot append one file to another', 'sftp.append')); | ||
} | ||
let stream = sftp.createWriteStream(remotePath, options); | ||
stream.on('error', err => { | ||
removeListeners(stream); | ||
reject(formatError(err, 'sftp.append')); | ||
}); | ||
stream.on('finish', () => { | ||
removeListeners(stream); | ||
resolve(`sftp.append: Uploaded data stream to ${remotePath}`); | ||
}); | ||
if (input instanceof Buffer) { | ||
stream.end(input); | ||
} else { | ||
input.pipe(stream); | ||
} | ||
} catch (err) { | ||
reject(formatError(err, 'sftp.append')); | ||
SftpClient.prototype.append = async function(input, remotePath, options) { | ||
try { | ||
if (!this.sftp) { | ||
return Promise.reject( | ||
formatError('No SFTP connection available', 'sftp.append') | ||
); | ||
} | ||
}); | ||
if (typeof input === 'string') { | ||
return Promise.reject( | ||
formatError('Cannot append one file to another', 'sftp.append') | ||
); | ||
} | ||
let absPath = await this.realPath(remotePath); | ||
let stats = await this.stat(absPath); | ||
if ((stats.mode & 0o0100000) === 0) { | ||
return Promise.reject( | ||
formatError( | ||
`${remotePath} Remote path must be a regular file`, | ||
'sftp-append' | ||
) | ||
); | ||
} | ||
if ((stats.mode & 0o0444) === 0) { | ||
return Promise.reject( | ||
formatError(`${remotePath} No write permission`, 'sftp-append') | ||
); | ||
} | ||
return this._append(input, absPath, options); | ||
} catch (err) { | ||
return Promise.reject(formatError(err, 'sftp.append')); | ||
} | ||
}; | ||
@@ -490,3 +634,3 @@ | ||
SftpClient.prototype.mkdir = async function(path, recursive = false) { | ||
let sftp = this.sftp; | ||
const sftp = this.sftp; | ||
@@ -511,10 +655,15 @@ function doMkdir(p) { | ||
let realPath = path; | ||
let {dir} = posix.parse(path); | ||
if (dir === '') { | ||
dir = '.'; | ||
realPath = './' + path; | ||
if (realPath.startsWith('../')) { | ||
let root = await this.realPath('..'); | ||
realPath = join(root, realPath.substring(3)); | ||
} else if (realPath.startsWith('./')) { | ||
let root = await this.realPath('.'); | ||
realPath = join(root, realPath.substring(2)); | ||
} | ||
if (!recursive) { | ||
return doMkdir(path); | ||
return doMkdir(realPath); | ||
} | ||
let {dir} = posix.parse(realPath); | ||
let parent = await this.exists(dir); | ||
@@ -543,3 +692,3 @@ if (!parent) { | ||
SftpClient.prototype.rmdir = async function(path, recursive = false) { | ||
let sftp = this.sftp; | ||
const sftp = this.sftp; | ||
@@ -567,6 +716,7 @@ function doRmdir(p) { | ||
} | ||
let absPath = await this.realPath(path); | ||
if (!recursive) { | ||
return doRmdir(path); | ||
return doRmdir(absPath); | ||
} | ||
let list = await this.list(path); | ||
let list = await this.list(absPath); | ||
if (list.length) { | ||
@@ -577,3 +727,3 @@ let files = list.filter(item => item.type !== 'd'); | ||
try { | ||
await this.delete(join(path, f.name)); | ||
await this.delete(join(absPath, f.name)); | ||
} catch (err) { | ||
@@ -585,3 +735,3 @@ return Promise.reject(formatError(err, 'sftp.rmdir')); | ||
try { | ||
await this.rmdir(join(path, d.name), true); | ||
await this.rmdir(join(absPath, d.name), true); | ||
} catch (err) { | ||
@@ -592,3 +742,3 @@ return Promise.reject(formatError(err, 'sftp.rmdir')); | ||
} | ||
return doRmdir(path); | ||
return doRmdir(absPath); | ||
} catch (err) { | ||
@@ -599,2 +749,15 @@ return Promise.reject(formatError(err, 'sftp.rmdir')); | ||
SftpClient.prototype._delete = function(path) { | ||
return new Promise((resolve, reject) => { | ||
let sftp = this.sftp; | ||
sftp.unlink(path, err => { | ||
if (err) { | ||
reject(formatError(err, 'sftp.delete')); | ||
} | ||
resolve('Successfully deleted file'); | ||
}); | ||
}); | ||
}; | ||
/** | ||
@@ -609,19 +772,26 @@ * @async | ||
*/ | ||
SftpClient.prototype.delete = function(path) { | ||
SftpClient.prototype.delete = async function(path) { | ||
try { | ||
if (!this.sftp) { | ||
return Promise.reject( | ||
formatError('No SFTP connection available', 'sftp.delete') | ||
); | ||
} | ||
let absPath = await this.realPath(path); | ||
return this._delete(absPath); | ||
} catch (err) { | ||
return Promise.reject(formatError(err, 'sftp.delete')); | ||
} | ||
}; | ||
SftpClient.prototype._rename = function(fromPath, toPath) { | ||
return new Promise((resolve, reject) => { | ||
let sftp = this.sftp; | ||
try { | ||
if (!sftp) { | ||
reject(formatError('No SFTP connection available', 'sftp.delete')); | ||
sftp.rename(fromPath, toPath, err => { | ||
if (err) { | ||
reject(formatError(err, 'sftp.rename')); | ||
} | ||
sftp.unlink(path, err => { | ||
if (err) { | ||
reject(formatError(err, 'sftp.delete')); | ||
} | ||
resolve('Successfully deleted file'); | ||
}); | ||
} catch (err) { | ||
reject(formatError(err, 'sftp.delete')); | ||
} | ||
resolve(`Successfully renamed ${fromPath} to ${toPath}`); | ||
}); | ||
}); | ||
@@ -641,19 +811,34 @@ }; | ||
*/ | ||
SftpClient.prototype.rename = function(fromPath, toPath) { | ||
SftpClient.prototype.rename = async function(fromPath, toPath) { | ||
try { | ||
if (!this.sftp) { | ||
return Promise.reject( | ||
formatError('No SFTP connection available', 'sftp.rename') | ||
); | ||
} | ||
let src = await this.realPath(fromPath); | ||
let dst = toPath; | ||
if (dst.startsWith('../')) { | ||
let root = await this.realPath('..'); | ||
dst = join(root, dst.substring(3)); | ||
} else if (dst.startsWith('./')) { | ||
let root = await this.realPath('.'); | ||
dst = join(root, dst.substring(2)); | ||
} | ||
return this._rename(src, dst); | ||
} catch (err) { | ||
return Promise.reject(formatError(err, 'sftp.rename')); | ||
} | ||
}; | ||
SftpClient.prototype._chmod = function(remotePath, mode) { | ||
return new Promise((resolve, reject) => { | ||
let sftp = this.sftp; | ||
const sftp = this.sftp; | ||
try { | ||
if (!sftp) { | ||
reject(formatError('No SFTP connection available', 'sftp.rename')); | ||
sftp.chmod(remotePath, mode, err => { | ||
if (err) { | ||
reject(formatError(err, 'sftp.chmod')); | ||
} | ||
sftp.rename(fromPath, toPath, err => { | ||
if (err) { | ||
reject(formatError(err, 'sftp.rename')); | ||
} | ||
resolve(`Successfully renamed ${fromPath} to ${toPath}`); | ||
}); | ||
} catch (err) { | ||
reject(formatError(err, 'sftp.rename')); | ||
} | ||
resolve('Successfully change file mode'); | ||
}); | ||
}); | ||
@@ -672,20 +857,14 @@ }; | ||
*/ | ||
SftpClient.prototype.chmod = function(remotePath, mode) { | ||
return new Promise((resolve, reject) => { | ||
let sftp = this.sftp; | ||
try { | ||
if (!sftp) { | ||
reject(formatError('No SFTP connection available', 'sftp.chmod')); | ||
} | ||
sftp.chmod(remotePath, mode, err => { | ||
if (err) { | ||
reject(formatError(err, 'sftp.chmod')); | ||
} | ||
resolve('Successfully change file mode'); | ||
}); | ||
} catch (err) { | ||
reject(formatError(err, 'sftp.chmod')); | ||
SftpClient.prototype.chmod = async function(remotePath, mode) { | ||
try { | ||
if (!this.sftp) { | ||
return Promise.reject( | ||
formatError('No SFTP connection available', 'sftp.chmod') | ||
); | ||
} | ||
}); | ||
let path = await this.realPath(remotePath); | ||
return this._chmod(path, mode); | ||
} catch (err) { | ||
return Promise.reject(formatError(err, 'sftp.chmod')); | ||
} | ||
}; | ||
@@ -797,8 +976,13 @@ | ||
SftpClient.prototype.end = function() { | ||
let obj = this; | ||
return new Promise((resolve, reject) => { | ||
try { | ||
// debugListeners(this.client); | ||
this.client.end(); | ||
removeListeners(this.client); | ||
this.sftp = null; | ||
obj.client.on('close', () => { | ||
removeListeners(obj.client); | ||
resolve(true); | ||
}); | ||
obj.client.end(); | ||
//removeListeners(obj.client); | ||
resolve(true); | ||
@@ -805,0 +989,0 @@ } catch (err) { |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
105775
909
1184