ssh2-sftp-client 7.0.4 → 7.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -3
- package/README.org +24 -3
- package/package.json +11 -15
- package/src/index.js +79 -49
- package/src/utils.js +17 -8
package/README.md
CHANGED
|
@@ -61,9 +61,9 @@ an SFTP client for node.js, a wrapper around [SSH2](https://github.com/mscdex/ss
|
|
|
61
61
|
|
|
62
62
|
Documentation on the methods and available options in the underlying modules can be found on the [SSH2](https://github.com/mscdex/ssh2) project pages.
|
|
63
63
|
|
|
64
|
-
Current stable release is **v7.
|
|
64
|
+
Current stable release is **v7.2.2**.
|
|
65
65
|
|
|
66
|
-
Code has been tested against Node versions
|
|
66
|
+
Code has been tested against Node versions 14.18.3, 16.13.2 and 17.4.0
|
|
67
67
|
|
|
68
68
|
Node versions < 10.x are not supported.
|
|
69
69
|
|
|
@@ -99,6 +99,8 @@ sftp.connect({
|
|
|
99
99
|
|
|
100
100
|
- **Breaking Change** Expanded option handling for `get()` and `put()` methods. A number of use cases were identified where setting specific options on the read and write streams and the pipe operation are necessary. For example, disabling `autoClose` on streams or the `end` event in pipes. The `options` argument for `get()` and `put()` calls now supports properties for `readStreamOptions`, `writeStreamOptions` and `pipeOptions`. Note that options are only applied to streams created by the `get()` and `put()` methods. Streams passed into these methods are under the control of the client code and therefore cannot have options supplied in arguments to those streams (you would apply such options when you create the streams). Options are typically only necessary in special use cases. Most of the time, no options are required. However, if you are currently using options to either `put()` or `get()`, you will need to update your code to map these options to the new structure.
|
|
101
101
|
|
|
102
|
+
- **Breaking Change 7.1.0** A race condition was identified when using a put() call with a writeStream option of `autoClose: false`. In some situations, the promise would be resolved before the final close of the write stream. This could result in errors if you immediately attempt to access the uploaded file. To avoid this situatioin, the promise is now resolved once a `close` event is emitted. This means that setting `autoClose: false` can no longer be supported. The write stream for `put()` will autoClose once data writing has completed.
|
|
103
|
+
|
|
102
104
|
- Improved event handling. A listener for a global error event is now defined to catch errors which occur in-between method calls i.e. connection lost in-between calls to the library methods. A new mechanism has also been added for removal of listeners when no longer required.
|
|
103
105
|
|
|
104
106
|
# Documentation<a id="sec-5"></a>
|
|
@@ -497,11 +499,12 @@ Upload data from local system to remote server. If the `src` argument is a strin
|
|
|
497
499
|
flags: 'w', // w - write and a - append
|
|
498
500
|
encoding: null, // use null for binary files
|
|
499
501
|
mode: 0o666, // mode to use for created file (rwx)
|
|
500
|
-
autoClose: true // automatically close the write stream when finished
|
|
501
502
|
}}
|
|
502
503
|
```
|
|
503
504
|
|
|
504
505
|
The most common options to use are mode and encoding. The values shown above are the defaults. You do not have to set encoding to utf-8 for text files, null is fine for all file types. However, using utf-8 encoding for binary files will often result in data corruption.
|
|
506
|
+
|
|
507
|
+
Note that you cannot set `autoClose: false` for `writeStreamOptions`. If you attempt to set this property to false, it will be ignored. This is necessary to avoid a race condition which may exist when setting `autoClose` to false on the writeStream. As there is no easy way to access the writeStream once the promise has been resolved, setting this to autoClose false is not terribly useful as there is no easy way to manually close the stream after the promise has been resolved.
|
|
505
508
|
|
|
506
509
|
2. Example Use
|
|
507
510
|
|
|
@@ -1356,3 +1359,6 @@ Thanks to the following for their contributions -
|
|
|
1356
1359
|
- **anton-erofeev:** Documentation fix
|
|
1357
1360
|
- **Ladislav Jacho:** Contributed solution explanation for connections hanging when transferring larger files.
|
|
1358
1361
|
- **Emma Milner:** Contributed fix for put() bug
|
|
1362
|
+
- **Witni Davis:** Contributed PR to fix put() RCE when using 'finish' rather than 'close' to resolve promise
|
|
1363
|
+
- **Maik Marschner:** Contributed fix for connect() not returning sftp object. Also included test to check for this regression in future.
|
|
1364
|
+
- **cakemasher:** Contributed fix for removeTempListeners().
|
package/README.org
CHANGED
|
@@ -9,9 +9,9 @@ convenience abstraction as well as a Promise based API.
|
|
|
9
9
|
Documentation on the methods and available options in the underlying modules can
|
|
10
10
|
be found on the [[https://github.com/mscdex/ssh2][SSH2]] project pages.
|
|
11
11
|
|
|
12
|
-
Current stable release is *v7.
|
|
12
|
+
Current stable release is *v7.2.2*.
|
|
13
13
|
|
|
14
|
-
Code has been tested against Node versions
|
|
14
|
+
Code has been tested against Node versions 14.18.3, 16.13.2 and 17.4.0
|
|
15
15
|
|
|
16
16
|
Node versions < 10.x are not supported.
|
|
17
17
|
|
|
@@ -62,6 +62,14 @@ npm install ssh2-sftp-client
|
|
|
62
62
|
currently using options to either ~put()~ or ~get()~, you will need to update
|
|
63
63
|
your code to map these options to the new structure.
|
|
64
64
|
|
|
65
|
+
- *Breaking Change 7.1.0* A race condition was identified when using a put()
|
|
66
|
+
call with a writeStream option of ~autoClose: false~. In some situations, the
|
|
67
|
+
promise would be resolved before the final close of the write stream. This
|
|
68
|
+
could result in errors if you immediately attempt to access the uploaded
|
|
69
|
+
file. To avoid this situatioin, the promise is now resolved once a ~close~
|
|
70
|
+
event is emitted. This means that setting ~autoClose: false~ can no longer be
|
|
71
|
+
supported. The write stream for ~put()~ will autoClose once data writing has completed.
|
|
72
|
+
|
|
65
73
|
- Improved event handling. A listener for a global error event is now defined to
|
|
66
74
|
catch errors which occur in-between method calls i.e. connection lost
|
|
67
75
|
in-between calls to the library methods. A new mechanism has also been added
|
|
@@ -546,7 +554,6 @@ option value. For example, you might use the following to set ~writeStream~ opti
|
|
|
546
554
|
flags: 'w', // w - write and a - append
|
|
547
555
|
encoding: null, // use null for binary files
|
|
548
556
|
mode: 0o666, // mode to use for created file (rwx)
|
|
549
|
-
autoClose: true // automatically close the write stream when finished
|
|
550
557
|
}}
|
|
551
558
|
#+end_src
|
|
552
559
|
|
|
@@ -555,6 +562,14 @@ the defaults. You do not have to set encoding to utf-8 for text files, null is
|
|
|
555
562
|
fine for all file types. However, using utf-8 encoding for binary files will
|
|
556
563
|
often result in data corruption.
|
|
557
564
|
|
|
565
|
+
Note that you cannot set ~autoClose: false~ for ~writeStreamOptions~. If you
|
|
566
|
+
attempt to set this property to false, it will be ignored. This is necessary to
|
|
567
|
+
avoid a race condition which may exist when setting ~autoClose~ to false on the
|
|
568
|
+
writeStream. As there is no easy way to access the writeStream once the promise
|
|
569
|
+
has been resolved, setting this to autoClose false is not terribly useful as
|
|
570
|
+
there is no easy way to manually close the stream after the promise has been
|
|
571
|
+
resolved.
|
|
572
|
+
|
|
558
573
|
**** Example Use
|
|
559
574
|
|
|
560
575
|
#+begin_src javascript
|
|
@@ -1765,3 +1780,9 @@ Thanks to the following for their contributions -
|
|
|
1765
1780
|
- Ladislav Jacho :: Contributed solution explanation for connections hanging
|
|
1766
1781
|
when transferring larger files.
|
|
1767
1782
|
- Emma Milner :: Contributed fix for put() bug
|
|
1783
|
+
- Witni Davis :: Contributed PR to fix put() RCE when using 'finish' rather than
|
|
1784
|
+
'close' to resolve promise
|
|
1785
|
+
- Maik Marschner :: Contributed fix for connect() not returning sftp object.
|
|
1786
|
+
Also included test to check for this regression in future.
|
|
1787
|
+
- cakemasher :: Contributed fix for removeTempListeners().
|
|
1788
|
+
|
package/package.json
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ssh2-sftp-client",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.2.2",
|
|
4
4
|
"description": "ssh2 sftp client for node",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/theophilusx/ssh2-sftp-client"
|
|
9
9
|
},
|
|
10
|
-
"keywords": [
|
|
11
|
-
"sftp",
|
|
12
|
-
"nodejs",
|
|
13
|
-
"promises"
|
|
14
|
-
],
|
|
10
|
+
"keywords": ["sftp", "nodejs", "promises"],
|
|
15
11
|
"scripts": {
|
|
16
12
|
"test": "mocha",
|
|
17
13
|
"coverage": "nyc npm run test",
|
|
@@ -32,24 +28,24 @@
|
|
|
32
28
|
"dependencies": {
|
|
33
29
|
"concat-stream": "^2.0.0",
|
|
34
30
|
"promise-retry": "^2.0.1",
|
|
35
|
-
"ssh2": "^1.
|
|
31
|
+
"ssh2": "^1.6.0"
|
|
36
32
|
},
|
|
37
33
|
"devDependencies": {
|
|
38
|
-
"chai": "^4.
|
|
34
|
+
"chai": "^4.3.4",
|
|
39
35
|
"chai-as-promised": "^7.1.1",
|
|
40
36
|
"chai-subset": "^1.6.0",
|
|
41
37
|
"checksum": "^1.0.0",
|
|
42
|
-
"dotenv": "^
|
|
43
|
-
"eslint": "^
|
|
38
|
+
"dotenv": "^15.0.0",
|
|
39
|
+
"eslint": "^8.5.0",
|
|
44
40
|
"eslint-config-prettier": "^8.3.0",
|
|
45
|
-
"eslint-plugin-mocha": "^
|
|
41
|
+
"eslint-plugin-mocha": "^10.0.3",
|
|
46
42
|
"eslint-plugin-node": "^11.1.0",
|
|
47
|
-
"eslint-plugin-promise": "^
|
|
48
|
-
"eslint-plugin-unicorn": "^
|
|
49
|
-
"mocha": "^9.
|
|
43
|
+
"eslint-plugin-promise": "^6.0.0",
|
|
44
|
+
"eslint-plugin-unicorn": "^40.1.0",
|
|
45
|
+
"mocha": "^9.1.2",
|
|
50
46
|
"moment": "^2.29.1",
|
|
51
47
|
"nyc": "^15.1.0",
|
|
52
|
-
"prettier": "^2.
|
|
48
|
+
"prettier": "^2.5.0",
|
|
53
49
|
"through2": "^4.0.2",
|
|
54
50
|
"winston": "^3.3.3"
|
|
55
51
|
}
|
package/src/index.js
CHANGED
|
@@ -34,6 +34,7 @@ class SftpClient {
|
|
|
34
34
|
this.remotePathSep = '/';
|
|
35
35
|
this.remotePlatform = 'unix';
|
|
36
36
|
this.debug = undefined;
|
|
37
|
+
this.tempListeners = {};
|
|
37
38
|
|
|
38
39
|
this.client.on('close', () => {
|
|
39
40
|
if (this.endCalled || this.closeHandled) {
|
|
@@ -114,7 +115,7 @@ class SftpClient {
|
|
|
114
115
|
*
|
|
115
116
|
* @param {Object} config - an SFTP configuration object
|
|
116
117
|
*
|
|
117
|
-
* @return {Promise} which will resolve to an sftp client object
|
|
118
|
+
* @return {Promise<Object>} which will resolve to an sftp client object
|
|
118
119
|
*
|
|
119
120
|
*/
|
|
120
121
|
getConnection(config) {
|
|
@@ -150,6 +151,7 @@ class SftpClient {
|
|
|
150
151
|
this.client.sftp((err, sftp) => {
|
|
151
152
|
if (err) {
|
|
152
153
|
this.debugMsg(`getSftpChannel: SFTP Channel Error: ${err.message}`);
|
|
154
|
+
this.client.end();
|
|
153
155
|
reject(fmtError(err, 'getSftpChannel', err.code));
|
|
154
156
|
} else {
|
|
155
157
|
this.debugMsg('getSftpChannel: SFTP channel established');
|
|
@@ -173,7 +175,7 @@ class SftpClient {
|
|
|
173
175
|
*
|
|
174
176
|
* @param {Object} config - an SFTP configuration object
|
|
175
177
|
*
|
|
176
|
-
* @return {Promise} which will resolve to an sftp client object
|
|
178
|
+
* @return {Promise<Object>} which will resolve to an sftp client object
|
|
177
179
|
*
|
|
178
180
|
*/
|
|
179
181
|
async connect(config) {
|
|
@@ -204,7 +206,7 @@ class SftpClient {
|
|
|
204
206
|
minTimeout: config.retry_minTimeout || 1000,
|
|
205
207
|
}
|
|
206
208
|
);
|
|
207
|
-
|
|
209
|
+
return this.getSftpChannel();
|
|
208
210
|
} catch (err) {
|
|
209
211
|
this.debugMsg(`connect: Error ${err.message}`);
|
|
210
212
|
this._resetEventFlags();
|
|
@@ -221,7 +223,7 @@ class SftpClient {
|
|
|
221
223
|
* Returns undefined if the path does not exists.
|
|
222
224
|
*
|
|
223
225
|
* @param {String} remotePath - remote path, may be relative
|
|
224
|
-
* @returns {Promise} - remote absolute path or
|
|
226
|
+
* @returns {Promise<String>} - remote absolute path or ''
|
|
225
227
|
*/
|
|
226
228
|
realPath(remotePath) {
|
|
227
229
|
return new Promise((resolve, reject) => {
|
|
@@ -249,6 +251,13 @@ class SftpClient {
|
|
|
249
251
|
});
|
|
250
252
|
}
|
|
251
253
|
|
|
254
|
+
/**
|
|
255
|
+
* @async
|
|
256
|
+
*
|
|
257
|
+
* Return the current workding directory path
|
|
258
|
+
*
|
|
259
|
+
* @returns {Promise<String>} - current remote working directory
|
|
260
|
+
*/
|
|
252
261
|
cwd() {
|
|
253
262
|
return this.realPath('.');
|
|
254
263
|
}
|
|
@@ -257,7 +266,7 @@ class SftpClient {
|
|
|
257
266
|
* Retrieves attributes for path
|
|
258
267
|
*
|
|
259
268
|
* @param {String} remotePath - a string containing the path to a file
|
|
260
|
-
* @return {Promise} stats - attributes info
|
|
269
|
+
* @return {Promise<Object>} stats - attributes info
|
|
261
270
|
*/
|
|
262
271
|
async stat(remotePath) {
|
|
263
272
|
const _stat = (aPath) => {
|
|
@@ -301,7 +310,7 @@ class SftpClient {
|
|
|
301
310
|
}
|
|
302
311
|
});
|
|
303
312
|
}).finally(() => {
|
|
304
|
-
removeTempListeners(this, '
|
|
313
|
+
removeTempListeners(this, '_stat');
|
|
305
314
|
});
|
|
306
315
|
};
|
|
307
316
|
|
|
@@ -323,7 +332,7 @@ class SftpClient {
|
|
|
323
332
|
*
|
|
324
333
|
* @param {string} remotePath - path to the object on the sftp server.
|
|
325
334
|
*
|
|
326
|
-
* @return {Promise} returns false if object does not exist. Returns type of
|
|
335
|
+
* @return {Promise<Boolean|String>} returns false if object does not exist. Returns type of
|
|
327
336
|
* object if it does
|
|
328
337
|
*/
|
|
329
338
|
async exists(remotePath) {
|
|
@@ -382,8 +391,7 @@ class SftpClient {
|
|
|
382
391
|
*
|
|
383
392
|
* @param {String} remotePath - path to remote directory
|
|
384
393
|
* @param {RegExp} pattern - regular expression to match filenames
|
|
385
|
-
* @returns {Promise} array of file description objects
|
|
386
|
-
* @throws {Error}
|
|
394
|
+
* @returns {Promise<Array>} array of file description objects
|
|
387
395
|
*/
|
|
388
396
|
list(remotePath, pattern = /.*/) {
|
|
389
397
|
return new Promise((resolve, reject) => {
|
|
@@ -449,7 +457,7 @@ class SftpClient {
|
|
|
449
457
|
* @param {Object} options - options object with supported properties of readStreamOptions,
|
|
450
458
|
* writeStreamOptions and pipeOptions.
|
|
451
459
|
*
|
|
452
|
-
* @return {Promise}
|
|
460
|
+
* @return {Promise<String|Stream|Buffer>}
|
|
453
461
|
*/
|
|
454
462
|
get(
|
|
455
463
|
remotePath,
|
|
@@ -507,13 +515,29 @@ class SftpClient {
|
|
|
507
515
|
)
|
|
508
516
|
);
|
|
509
517
|
});
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
518
|
+
if (
|
|
519
|
+
Object.hasOwnProperty.call(options, 'pipeOptions') &&
|
|
520
|
+
Object.hasOwnProperty.call(options.pipeOptions, 'end') &&
|
|
521
|
+
!options.pipeOptions.end
|
|
522
|
+
) {
|
|
523
|
+
rdr.once('end', () => {
|
|
524
|
+
this.debugMsg('get resolved on reader end event');
|
|
525
|
+
if (typeof dst === 'string') {
|
|
526
|
+
resolve(dst);
|
|
527
|
+
} else {
|
|
528
|
+
resolve(wtr);
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
} else {
|
|
532
|
+
wtr.once('finish', () => {
|
|
533
|
+
this.debugMsg('get resolved on writer finish event');
|
|
534
|
+
if (typeof dst === 'string') {
|
|
535
|
+
resolve(dst);
|
|
536
|
+
} else {
|
|
537
|
+
resolve(wtr);
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
}
|
|
517
541
|
}
|
|
518
542
|
rdr.pipe(wtr, options.pipeOptions ? options.pipeOptions : {});
|
|
519
543
|
}
|
|
@@ -522,14 +546,16 @@ class SftpClient {
|
|
|
522
546
|
this._resetEventFlags();
|
|
523
547
|
if (
|
|
524
548
|
rdr &&
|
|
525
|
-
options
|
|
549
|
+
Object.hasOwnProperty.call(options, 'readStreamOptions') &&
|
|
550
|
+
Object.hasOwnProperty.call(options.readStreamOptions, 'autoClose') &&
|
|
526
551
|
options.readStreamOptions.autoClose === false
|
|
527
552
|
) {
|
|
528
553
|
rdr.destroy();
|
|
529
554
|
}
|
|
530
555
|
if (
|
|
531
556
|
wtr &&
|
|
532
|
-
options
|
|
557
|
+
Object.hasOwnProperty.call(options, 'writeStreamOptions') &&
|
|
558
|
+
Object.hasOwnProperty.call(options.writeStreamOptions, 'autoClose') &&
|
|
533
559
|
options.writeStreamOptions.autoClose === false &&
|
|
534
560
|
typeof dst === 'string'
|
|
535
561
|
) {
|
|
@@ -543,13 +569,10 @@ class SftpClient {
|
|
|
543
569
|
* Downloads a file at remotePath to localPath using parallel reads
|
|
544
570
|
* for faster throughput.
|
|
545
571
|
*
|
|
546
|
-
* See 'fastGet' at
|
|
547
|
-
* https://github.com/mscdex/ssh2-streams/blob/master/SFTPStream.md
|
|
548
|
-
*
|
|
549
572
|
* @param {String} remotePath
|
|
550
573
|
* @param {String} localPath
|
|
551
574
|
* @param {Object} options
|
|
552
|
-
* @return {Promise} the result of downloading the file
|
|
575
|
+
* @return {Promise<String>} the result of downloading the file
|
|
553
576
|
*/
|
|
554
577
|
async fastGet(remotePath, localPath, options) {
|
|
555
578
|
try {
|
|
@@ -569,7 +592,7 @@ class SftpClient {
|
|
|
569
592
|
err.code = errorCode.badPath;
|
|
570
593
|
throw err;
|
|
571
594
|
}
|
|
572
|
-
await new Promise((resolve, reject) => {
|
|
595
|
+
let rslt = await new Promise((resolve, reject) => {
|
|
573
596
|
if (haveConnection(this, 'fastGet', reject)) {
|
|
574
597
|
this.debugMsg(
|
|
575
598
|
`fastGet -> remote: ${remotePath} local: ${localPath} `,
|
|
@@ -587,6 +610,7 @@ class SftpClient {
|
|
|
587
610
|
}).finally(() => {
|
|
588
611
|
removeTempListeners(this, 'fastGet');
|
|
589
612
|
});
|
|
613
|
+
return rslt;
|
|
590
614
|
} catch (err) {
|
|
591
615
|
this._resetEventFlags();
|
|
592
616
|
throw fmtError(err, 'fastGet');
|
|
@@ -604,7 +628,7 @@ class SftpClient {
|
|
|
604
628
|
* @param {String} localPath
|
|
605
629
|
* @param {String} remotePath
|
|
606
630
|
* @param {Object} options
|
|
607
|
-
* @return {Promise} the result of downloading the file
|
|
631
|
+
* @return {Promise<String>} the result of downloading the file
|
|
608
632
|
*/
|
|
609
633
|
fastPut(localPath, remotePath, options) {
|
|
610
634
|
this.debugMsg(`fastPut -> local ${localPath} remote ${remotePath}`);
|
|
@@ -664,12 +688,16 @@ class SftpClient {
|
|
|
664
688
|
* @param {Object} options - options used for read, write stream and pipe configuration
|
|
665
689
|
* value supported by node. Allowed properties are readStreamOptions,
|
|
666
690
|
* writeStreamOptions and pipeOptions.
|
|
667
|
-
* @return {Promise}
|
|
691
|
+
* @return {Promise<String>}
|
|
668
692
|
*/
|
|
669
693
|
put(
|
|
670
694
|
localSrc,
|
|
671
695
|
remotePath,
|
|
672
|
-
options = {
|
|
696
|
+
options = {
|
|
697
|
+
readStreamOptions: {},
|
|
698
|
+
writeStreamOptions: { autoClose: true },
|
|
699
|
+
pipeOptions: {},
|
|
700
|
+
}
|
|
673
701
|
) {
|
|
674
702
|
let wtr, rdr;
|
|
675
703
|
|
|
@@ -691,13 +719,15 @@ class SftpClient {
|
|
|
691
719
|
addTempListeners(this, 'put', reject);
|
|
692
720
|
wtr = this.sftp.createWriteStream(
|
|
693
721
|
remotePath,
|
|
694
|
-
options.writeStreamOptions
|
|
722
|
+
options.writeStreamOptions
|
|
723
|
+
? { ...options.writeStreamOptions, autoClose: true }
|
|
724
|
+
: {}
|
|
695
725
|
);
|
|
696
726
|
wtr.once('error', (err) => {
|
|
697
727
|
this.debugMsg(`put: write stream error ${err.message}`);
|
|
698
728
|
reject(fmtError(`${err.message} ${remotePath}`, 'put', err.code));
|
|
699
729
|
});
|
|
700
|
-
wtr.once('
|
|
730
|
+
wtr.once('close', () => {
|
|
701
731
|
this.debugMsg('put: promise resolved');
|
|
702
732
|
resolve(`Uploaded data stream to ${remotePath}`);
|
|
703
733
|
});
|
|
@@ -735,19 +765,13 @@ class SftpClient {
|
|
|
735
765
|
this._resetEventFlags();
|
|
736
766
|
if (
|
|
737
767
|
rdr &&
|
|
738
|
-
options
|
|
768
|
+
Object.hasOwnProperty.call(options, 'readStreamOptions') &&
|
|
769
|
+
Object.hasOwnProperty.call(options.readStreamOptions, 'autoClose') &&
|
|
739
770
|
options.readStreamOptions.autoClose === false &&
|
|
740
771
|
typeof localSrc === 'string'
|
|
741
772
|
) {
|
|
742
773
|
rdr.destroy();
|
|
743
774
|
}
|
|
744
|
-
if (
|
|
745
|
-
wtr &&
|
|
746
|
-
options.writeStreamOptions &&
|
|
747
|
-
options.writeStreamOptions.autoClose === false
|
|
748
|
-
) {
|
|
749
|
-
wtr.destroy();
|
|
750
|
-
}
|
|
751
775
|
});
|
|
752
776
|
}
|
|
753
777
|
|
|
@@ -757,9 +781,8 @@ class SftpClient {
|
|
|
757
781
|
* @param {Buffer|stream} input
|
|
758
782
|
* @param {String} remotePath
|
|
759
783
|
* @param {Object} options
|
|
760
|
-
* @return {Promise}
|
|
784
|
+
* @return {Promise<String>}
|
|
761
785
|
*/
|
|
762
|
-
|
|
763
786
|
async append(input, remotePath, options = {}) {
|
|
764
787
|
const fileType = await this.exists(remotePath);
|
|
765
788
|
if (fileType && fileType === 'd') {
|
|
@@ -807,7 +830,7 @@ class SftpClient {
|
|
|
807
830
|
*
|
|
808
831
|
* @param {string} remotePath - remote directory path.
|
|
809
832
|
* @param {boolean} recursive - if true, recursively create directories
|
|
810
|
-
* @return {Promise}
|
|
833
|
+
* @return {Promise<String>}
|
|
811
834
|
*/
|
|
812
835
|
async mkdir(remotePath, recursive = false) {
|
|
813
836
|
const _mkdir = (p) => {
|
|
@@ -845,6 +868,14 @@ class SftpClient {
|
|
|
845
868
|
try {
|
|
846
869
|
haveConnection(this, 'mkdir');
|
|
847
870
|
let rPath = await normalizeRemotePath(this, remotePath);
|
|
871
|
+
let targetExists = await this.exists(rPath);
|
|
872
|
+
if (targetExists && targetExists !== 'd') {
|
|
873
|
+
let error = new Error(`Bad path: ${rPath} already exists as a file`);
|
|
874
|
+
error.code = errorCode.badPath;
|
|
875
|
+
throw error;
|
|
876
|
+
} else if (targetExists) {
|
|
877
|
+
return `${rPath} already exists`;
|
|
878
|
+
}
|
|
848
879
|
if (!recursive) {
|
|
849
880
|
return await _mkdir(rPath);
|
|
850
881
|
}
|
|
@@ -873,7 +904,7 @@ class SftpClient {
|
|
|
873
904
|
* @param {string} remotePath - path to directory to be removed
|
|
874
905
|
* @param {boolean} recursive - if true, remove directories/files in target
|
|
875
906
|
* directory
|
|
876
|
-
* @return {Promise}
|
|
907
|
+
* @return {Promise<String>}
|
|
877
908
|
*/
|
|
878
909
|
async rmdir(remotePath, recursive = false) {
|
|
879
910
|
const _rmdir = (p) => {
|
|
@@ -926,7 +957,7 @@ class SftpClient {
|
|
|
926
957
|
* @param {string} remotePath - path to the file to delete
|
|
927
958
|
* @param {boolean} notFoundOK - if true, ignore errors for missing target.
|
|
928
959
|
* Default is false.
|
|
929
|
-
* @return {Promise} with string 'Successfully deleted file' once resolved
|
|
960
|
+
* @return {Promise<String>} with string 'Successfully deleted file' once resolved
|
|
930
961
|
*
|
|
931
962
|
*/
|
|
932
963
|
delete(remotePath, notFoundOK = false) {
|
|
@@ -963,7 +994,7 @@ class SftpClient {
|
|
|
963
994
|
* @param {string} fromPath - path to the file to be renamed.
|
|
964
995
|
* @param {string} toPath - path to the new name.
|
|
965
996
|
*
|
|
966
|
-
* @return {Promise}
|
|
997
|
+
* @return {Promise<String>}
|
|
967
998
|
*
|
|
968
999
|
*/
|
|
969
1000
|
rename(fromPath, toPath) {
|
|
@@ -1000,7 +1031,7 @@ class SftpClient {
|
|
|
1000
1031
|
* @param {string} fromPath - path to the file to be renamed.
|
|
1001
1032
|
* @param {string} toPath - path the new name.
|
|
1002
1033
|
*
|
|
1003
|
-
* @return {Promise}
|
|
1034
|
+
* @return {Promise<String>}
|
|
1004
1035
|
*
|
|
1005
1036
|
*/
|
|
1006
1037
|
posixRename(fromPath, toPath) {
|
|
@@ -1036,7 +1067,7 @@ class SftpClient {
|
|
|
1036
1067
|
* @param {string} remotePath - path to the remote target object.
|
|
1037
1068
|
* @param {number | string} mode - the new octal mode to set
|
|
1038
1069
|
*
|
|
1039
|
-
* @return {Promise}
|
|
1070
|
+
* @return {Promise<String>}
|
|
1040
1071
|
*/
|
|
1041
1072
|
chmod(remotePath, mode) {
|
|
1042
1073
|
return new Promise((resolve, reject) => {
|
|
@@ -1064,8 +1095,7 @@ class SftpClient {
|
|
|
1064
1095
|
* @param {String} dstDir - remote destination directory
|
|
1065
1096
|
* @param {RegExp} filter - (Optional) a regular expression used to select
|
|
1066
1097
|
* files and directories to upload
|
|
1067
|
-
* @returns {String}
|
|
1068
|
-
* @throws {Error}
|
|
1098
|
+
* @returns {Promise<String>}
|
|
1069
1099
|
*/
|
|
1070
1100
|
async uploadDir(srcDir, dstDir, filter = /.*/) {
|
|
1071
1101
|
try {
|
|
@@ -1124,8 +1154,7 @@ class SftpClient {
|
|
|
1124
1154
|
* @param {String} dstDir - local destination directory
|
|
1125
1155
|
* @param {RegExp} filter - (Optional) a regular expression used to select
|
|
1126
1156
|
* files and directories to upload
|
|
1127
|
-
* @returns {Promise}
|
|
1128
|
-
* @throws {Error}
|
|
1157
|
+
* @returns {Promise<String>}
|
|
1129
1158
|
*/
|
|
1130
1159
|
async downloadDir(srcDir, dstDir, filter = /.*/) {
|
|
1131
1160
|
try {
|
|
@@ -1176,6 +1205,7 @@ class SftpClient {
|
|
|
1176
1205
|
*
|
|
1177
1206
|
* End the SFTP connection
|
|
1178
1207
|
*
|
|
1208
|
+
* @returns {Promise<Boolean>}
|
|
1179
1209
|
*/
|
|
1180
1210
|
end() {
|
|
1181
1211
|
let endCloseHandler;
|
package/src/utils.js
CHANGED
|
@@ -58,7 +58,13 @@ function fmtError(err, name = 'sftp', eCode, retryCount) {
|
|
|
58
58
|
return newError;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
function addToTempListenerList(obj, name, evt, fn) {
|
|
62
|
+
if (name in obj.tempListeners) {
|
|
63
|
+
obj.tempListeners[name].push([evt, fn]);
|
|
64
|
+
} else {
|
|
65
|
+
obj.tempListeners[name] = [[evt, fn]];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
62
68
|
|
|
63
69
|
/**
|
|
64
70
|
* Simple default error listener. Will reformat the error message and
|
|
@@ -83,7 +89,7 @@ function errorListener(client, name, reject) {
|
|
|
83
89
|
}
|
|
84
90
|
}
|
|
85
91
|
};
|
|
86
|
-
|
|
92
|
+
addToTempListenerList(client, name, 'error', fn);
|
|
87
93
|
return fn;
|
|
88
94
|
}
|
|
89
95
|
|
|
@@ -104,7 +110,7 @@ function endListener(client, name, reject) {
|
|
|
104
110
|
}
|
|
105
111
|
}
|
|
106
112
|
};
|
|
107
|
-
|
|
113
|
+
addToTempListenerList(client, name, 'end', fn);
|
|
108
114
|
return fn;
|
|
109
115
|
}
|
|
110
116
|
|
|
@@ -125,7 +131,7 @@ function closeListener(client, name, reject) {
|
|
|
125
131
|
}
|
|
126
132
|
}
|
|
127
133
|
};
|
|
128
|
-
|
|
134
|
+
addToTempListenerList(client, name, 'close', fn);
|
|
129
135
|
return fn;
|
|
130
136
|
}
|
|
131
137
|
|
|
@@ -138,10 +144,12 @@ function addTempListeners(obj, name, reject) {
|
|
|
138
144
|
|
|
139
145
|
function removeTempListeners(obj, name) {
|
|
140
146
|
obj.debugMsg(`${name}: Removing temp event listeners`);
|
|
141
|
-
|
|
142
|
-
obj.
|
|
143
|
-
|
|
144
|
-
|
|
147
|
+
if (name in obj.tempListeners) {
|
|
148
|
+
obj.tempListeners[name].forEach(([e, fn]) => {
|
|
149
|
+
obj.client.removeListener(e, fn);
|
|
150
|
+
});
|
|
151
|
+
obj.tempListeners[name] = [];
|
|
152
|
+
}
|
|
145
153
|
}
|
|
146
154
|
|
|
147
155
|
/**
|
|
@@ -337,6 +345,7 @@ function sleep(ms) {
|
|
|
337
345
|
|
|
338
346
|
module.exports = {
|
|
339
347
|
fmtError,
|
|
348
|
+
addToTempListenerList,
|
|
340
349
|
errorListener,
|
|
341
350
|
endListener,
|
|
342
351
|
closeListener,
|