ssh2-sftp-client 7.0.3 → 7.0.4
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 +1 -1
- package/README.org +1 -1
- package/package.json +18 -4
- package/src/constants.js +3 -5
- package/src/index.js +55 -73
- package/src/utils.js +29 -25
package/README.md
CHANGED
|
@@ -61,7 +61,7 @@ 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.0.
|
|
64
|
+
Current stable release is **v7.0.4**.
|
|
65
65
|
|
|
66
66
|
Code has been tested against Node versions 12.22.1, 14.17.0 and 16.2.0
|
|
67
67
|
|
package/README.org
CHANGED
|
@@ -9,7 +9,7 @@ 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.0.
|
|
12
|
+
Current stable release is *v7.0.4*.
|
|
13
13
|
|
|
14
14
|
Code has been tested against Node versions 12.22.1, 14.17.0 and 16.2.0
|
|
15
15
|
|
package/package.json
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ssh2-sftp-client",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.4",
|
|
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": [
|
|
10
|
+
"keywords": [
|
|
11
|
+
"sftp",
|
|
12
|
+
"nodejs",
|
|
13
|
+
"promises"
|
|
14
|
+
],
|
|
11
15
|
"scripts": {
|
|
12
16
|
"test": "mocha",
|
|
13
17
|
"coverage": "nyc npm run test",
|
|
14
|
-
"lint": "eslint \"src/**/*.js\"
|
|
18
|
+
"lint": "eslint \"src/**/*.js\" \"test/**/*.js\""
|
|
19
|
+
},
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=10.24.1"
|
|
15
22
|
},
|
|
16
23
|
"author": "Tim Cross",
|
|
17
24
|
"email": "theophilusx@gmail.com",
|
|
@@ -25,7 +32,7 @@
|
|
|
25
32
|
"dependencies": {
|
|
26
33
|
"concat-stream": "^2.0.0",
|
|
27
34
|
"promise-retry": "^2.0.1",
|
|
28
|
-
"ssh2": "^1.
|
|
35
|
+
"ssh2": "^1.4.0"
|
|
29
36
|
},
|
|
30
37
|
"devDependencies": {
|
|
31
38
|
"chai": "^4.2.0",
|
|
@@ -33,9 +40,16 @@
|
|
|
33
40
|
"chai-subset": "^1.6.0",
|
|
34
41
|
"checksum": "^1.0.0",
|
|
35
42
|
"dotenv": "^10.0.0",
|
|
43
|
+
"eslint": "^7.32.0",
|
|
44
|
+
"eslint-config-prettier": "^8.3.0",
|
|
45
|
+
"eslint-plugin-mocha": "^9.0.0",
|
|
46
|
+
"eslint-plugin-node": "^11.1.0",
|
|
47
|
+
"eslint-plugin-promise": "^5.1.0",
|
|
48
|
+
"eslint-plugin-unicorn": "^35.0.0",
|
|
36
49
|
"mocha": "^9.0.2",
|
|
37
50
|
"moment": "^2.29.1",
|
|
38
51
|
"nyc": "^15.1.0",
|
|
52
|
+
"prettier": "^2.3.2",
|
|
39
53
|
"through2": "^4.0.2",
|
|
40
54
|
"winston": "^3.3.3"
|
|
41
55
|
}
|
package/src/constants.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
const errorCode = {
|
|
4
2
|
generic: 'ERR_GENERIC_CLIENT',
|
|
5
3
|
connect: 'ERR_NOT_CONNECTED',
|
|
6
4
|
badPath: 'ERR_BAD_PATH',
|
|
7
5
|
permission: 'EACCES',
|
|
8
6
|
notexist: 'ENOENT',
|
|
9
|
-
notdir: 'ENOTDIR'
|
|
7
|
+
notdir: 'ENOTDIR',
|
|
10
8
|
};
|
|
11
9
|
|
|
12
10
|
const targetType = {
|
|
@@ -15,10 +13,10 @@ const targetType = {
|
|
|
15
13
|
writeDir: 3,
|
|
16
14
|
readDir: 4,
|
|
17
15
|
readObj: 5,
|
|
18
|
-
writeObj: 6
|
|
16
|
+
writeObj: 6,
|
|
19
17
|
};
|
|
20
18
|
|
|
21
19
|
module.exports = {
|
|
22
20
|
errorCode,
|
|
23
|
-
targetType
|
|
21
|
+
targetType,
|
|
24
22
|
};
|
package/src/index.js
CHANGED
|
@@ -133,13 +133,12 @@ class SftpClient {
|
|
|
133
133
|
// .catch((err) => {
|
|
134
134
|
// return Promise.reject(err);
|
|
135
135
|
// })
|
|
136
|
-
.finally(async (
|
|
136
|
+
.finally(async () => {
|
|
137
137
|
this.debugMsg('getConnection: finally clause fired');
|
|
138
138
|
await sleep(500);
|
|
139
139
|
this.removeListener('ready', doReady);
|
|
140
140
|
removeTempListeners(this, 'getConnection');
|
|
141
141
|
this._resetEventFlags();
|
|
142
|
-
return resp;
|
|
143
142
|
})
|
|
144
143
|
);
|
|
145
144
|
}
|
|
@@ -158,7 +157,7 @@ class SftpClient {
|
|
|
158
157
|
resolve(sftp);
|
|
159
158
|
}
|
|
160
159
|
});
|
|
161
|
-
}).finally((
|
|
160
|
+
}).finally(() => {
|
|
162
161
|
this.debugMsg('getSftpChannel: finally clause fired');
|
|
163
162
|
removeTempListeners(this, 'getSftpChannel');
|
|
164
163
|
this._resetEventFlags();
|
|
@@ -244,10 +243,9 @@ class SftpClient {
|
|
|
244
243
|
resolve(absPath);
|
|
245
244
|
});
|
|
246
245
|
}
|
|
247
|
-
}).finally((
|
|
246
|
+
}).finally(() => {
|
|
248
247
|
removeTempListeners(this, 'realPath');
|
|
249
248
|
this._resetEventFlags();
|
|
250
|
-
return rsp;
|
|
251
249
|
});
|
|
252
250
|
}
|
|
253
251
|
|
|
@@ -302,9 +300,8 @@ class SftpClient {
|
|
|
302
300
|
resolve(result);
|
|
303
301
|
}
|
|
304
302
|
});
|
|
305
|
-
}).finally((
|
|
303
|
+
}).finally(() => {
|
|
306
304
|
removeTempListeners(this, 'stat');
|
|
307
|
-
return rsp;
|
|
308
305
|
});
|
|
309
306
|
};
|
|
310
307
|
|
|
@@ -404,15 +401,15 @@ class SftpClient {
|
|
|
404
401
|
if (fileList) {
|
|
405
402
|
newList = fileList.map((item) => {
|
|
406
403
|
return {
|
|
407
|
-
type: item.longname.
|
|
404
|
+
type: item.longname.slice(0, 1),
|
|
408
405
|
name: item.filename,
|
|
409
406
|
size: item.attrs.size,
|
|
410
407
|
modifyTime: item.attrs.mtime * 1000,
|
|
411
408
|
accessTime: item.attrs.atime * 1000,
|
|
412
409
|
rights: {
|
|
413
|
-
user: item.longname.
|
|
414
|
-
group: item.longname.
|
|
415
|
-
other: item.longname.
|
|
410
|
+
user: item.longname.slice(1, 4).replace(reg, ''),
|
|
411
|
+
group: item.longname.slice(4, 7).replace(reg, ''),
|
|
412
|
+
other: item.longname.slice(7, 10).replace(reg, ''),
|
|
416
413
|
},
|
|
417
414
|
owner: item.attrs.uid,
|
|
418
415
|
group: item.attrs.gid,
|
|
@@ -433,10 +430,9 @@ class SftpClient {
|
|
|
433
430
|
}
|
|
434
431
|
});
|
|
435
432
|
}
|
|
436
|
-
}).finally((
|
|
433
|
+
}).finally(() => {
|
|
437
434
|
removeTempListeners(this, 'list');
|
|
438
435
|
this._resetEventFlags();
|
|
439
|
-
return rsp;
|
|
440
436
|
});
|
|
441
437
|
}
|
|
442
438
|
|
|
@@ -521,7 +517,7 @@ class SftpClient {
|
|
|
521
517
|
}
|
|
522
518
|
rdr.pipe(wtr, options.pipeOptions ? options.pipeOptions : {});
|
|
523
519
|
}
|
|
524
|
-
}).finally((
|
|
520
|
+
}).finally(() => {
|
|
525
521
|
removeTempListeners(this, 'get');
|
|
526
522
|
this._resetEventFlags();
|
|
527
523
|
if (
|
|
@@ -539,7 +535,6 @@ class SftpClient {
|
|
|
539
535
|
) {
|
|
540
536
|
wtr.destroy();
|
|
541
537
|
}
|
|
542
|
-
return rsp;
|
|
543
538
|
});
|
|
544
539
|
}
|
|
545
540
|
|
|
@@ -589,9 +584,8 @@ class SftpClient {
|
|
|
589
584
|
resolve(`${remotePath} was successfully download to ${localPath}!`);
|
|
590
585
|
});
|
|
591
586
|
}
|
|
592
|
-
}).finally((
|
|
587
|
+
}).finally(() => {
|
|
593
588
|
removeTempListeners(this, 'fastGet');
|
|
594
|
-
return rsp;
|
|
595
589
|
});
|
|
596
590
|
} catch (err) {
|
|
597
591
|
this._resetEventFlags();
|
|
@@ -654,10 +648,9 @@ class SftpClient {
|
|
|
654
648
|
resolve(`${localPath} was successfully uploaded to ${remotePath}!`);
|
|
655
649
|
});
|
|
656
650
|
}
|
|
657
|
-
}).finally((
|
|
651
|
+
}).finally(() => {
|
|
658
652
|
removeTempListeners(this, 'fastPut');
|
|
659
653
|
this._resetEventFlags();
|
|
660
|
-
return rsp;
|
|
661
654
|
});
|
|
662
655
|
}
|
|
663
656
|
|
|
@@ -737,7 +730,7 @@ class SftpClient {
|
|
|
737
730
|
rdr.pipe(wtr, options.pipeOptions ? options.pipeOptions : {});
|
|
738
731
|
}
|
|
739
732
|
}
|
|
740
|
-
}).finally((
|
|
733
|
+
}).finally(() => {
|
|
741
734
|
removeTempListeners(this, 'put');
|
|
742
735
|
this._resetEventFlags();
|
|
743
736
|
if (
|
|
@@ -755,7 +748,6 @@ class SftpClient {
|
|
|
755
748
|
) {
|
|
756
749
|
wtr.destroy();
|
|
757
750
|
}
|
|
758
|
-
return resp;
|
|
759
751
|
});
|
|
760
752
|
}
|
|
761
753
|
|
|
@@ -767,47 +759,44 @@ class SftpClient {
|
|
|
767
759
|
* @param {Object} options
|
|
768
760
|
* @return {Promise}
|
|
769
761
|
*/
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
if (
|
|
783
|
-
|
|
784
|
-
|
|
762
|
+
|
|
763
|
+
async append(input, remotePath, options = {}) {
|
|
764
|
+
const fileType = await this.exists(remotePath);
|
|
765
|
+
if (fileType && fileType === 'd') {
|
|
766
|
+
throw fmtError(
|
|
767
|
+
`Bad path: ${remotePath}: cannot append to a directory`,
|
|
768
|
+
'append',
|
|
769
|
+
errorCode.badPath
|
|
770
|
+
);
|
|
771
|
+
}
|
|
772
|
+
return await new Promise((resolve, reject) => {
|
|
773
|
+
if (haveConnection(this, 'append', reject)) {
|
|
774
|
+
if (typeof input === 'string') {
|
|
775
|
+
reject(fmtError('Cannot append one file to another', 'append'));
|
|
776
|
+
} else {
|
|
777
|
+
this.debugMsg(`append -> remote: ${remotePath} `, options);
|
|
778
|
+
addTempListeners(this, 'append', reject);
|
|
779
|
+
options.flags = 'a';
|
|
780
|
+
let stream = this.sftp.createWriteStream(remotePath, options);
|
|
781
|
+
stream.on('error', (err_1) => {
|
|
782
|
+
reject(
|
|
783
|
+
fmtError(`${err_1.message} ${remotePath}`, 'append', err_1.code)
|
|
784
|
+
);
|
|
785
|
+
});
|
|
786
|
+
stream.on('finish', () => {
|
|
787
|
+
resolve(`Appended data to ${remotePath}`);
|
|
788
|
+
});
|
|
789
|
+
if (input instanceof Buffer) {
|
|
790
|
+
stream.write(input);
|
|
791
|
+
stream.end();
|
|
785
792
|
} else {
|
|
786
|
-
|
|
787
|
-
addTempListeners(this, 'append', reject);
|
|
788
|
-
options.flags = 'a';
|
|
789
|
-
let stream = this.sftp.createWriteStream(remotePath, options);
|
|
790
|
-
stream.on('error', (err) => {
|
|
791
|
-
reject(
|
|
792
|
-
fmtError(`${err.message} ${remotePath}`, 'append', err.code)
|
|
793
|
-
);
|
|
794
|
-
});
|
|
795
|
-
stream.on('finish', () => {
|
|
796
|
-
resolve(`Appended data to ${remotePath}`);
|
|
797
|
-
});
|
|
798
|
-
if (input instanceof Buffer) {
|
|
799
|
-
stream.write(input);
|
|
800
|
-
stream.end();
|
|
801
|
-
} else {
|
|
802
|
-
input.pipe(stream);
|
|
803
|
-
}
|
|
793
|
+
input.pipe(stream);
|
|
804
794
|
}
|
|
805
795
|
}
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
});
|
|
796
|
+
}
|
|
797
|
+
}).finally(() => {
|
|
798
|
+
removeTempListeners(this, 'append');
|
|
799
|
+
this._resetEventFlags();
|
|
811
800
|
});
|
|
812
801
|
}
|
|
813
802
|
|
|
@@ -847,10 +836,9 @@ class SftpClient {
|
|
|
847
836
|
resolve(`${p} directory created`);
|
|
848
837
|
}
|
|
849
838
|
});
|
|
850
|
-
}).finally((
|
|
839
|
+
}).finally(() => {
|
|
851
840
|
removeTempListeners(this, '_mkdir');
|
|
852
841
|
this._resetEventFlags();
|
|
853
|
-
return rsp;
|
|
854
842
|
});
|
|
855
843
|
};
|
|
856
844
|
|
|
@@ -899,9 +887,8 @@ class SftpClient {
|
|
|
899
887
|
}
|
|
900
888
|
resolve('Successfully removed directory');
|
|
901
889
|
});
|
|
902
|
-
}).finally((
|
|
890
|
+
}).finally(() => {
|
|
903
891
|
removeTempListeners(this, 'rmdir');
|
|
904
|
-
return rsp;
|
|
905
892
|
});
|
|
906
893
|
};
|
|
907
894
|
|
|
@@ -962,10 +949,9 @@ class SftpClient {
|
|
|
962
949
|
resolve(`Successfully deleted ${remotePath}`);
|
|
963
950
|
});
|
|
964
951
|
}
|
|
965
|
-
}).finally((
|
|
952
|
+
}).finally(() => {
|
|
966
953
|
removeTempListeners(this, 'delete');
|
|
967
954
|
this._resetEventFlags();
|
|
968
|
-
return rsp;
|
|
969
955
|
});
|
|
970
956
|
}
|
|
971
957
|
|
|
@@ -999,10 +985,9 @@ class SftpClient {
|
|
|
999
985
|
resolve(`Successfully renamed ${fromPath} to ${toPath}`);
|
|
1000
986
|
});
|
|
1001
987
|
}
|
|
1002
|
-
}).finally((
|
|
988
|
+
}).finally(() => {
|
|
1003
989
|
removeTempListeners(this, 'rename');
|
|
1004
990
|
this._resetEventFlags();
|
|
1005
|
-
return rsp;
|
|
1006
991
|
});
|
|
1007
992
|
}
|
|
1008
993
|
|
|
@@ -1037,10 +1022,9 @@ class SftpClient {
|
|
|
1037
1022
|
resolve(`Successful POSIX rename ${fromPath} to ${toPath}`);
|
|
1038
1023
|
});
|
|
1039
1024
|
}
|
|
1040
|
-
}).finally((
|
|
1025
|
+
}).finally(() => {
|
|
1041
1026
|
removeTempListeners(this, 'posixRename');
|
|
1042
1027
|
this._resetEventFlags();
|
|
1043
|
-
return rsp;
|
|
1044
1028
|
});
|
|
1045
1029
|
}
|
|
1046
1030
|
|
|
@@ -1064,10 +1048,9 @@ class SftpClient {
|
|
|
1064
1048
|
}
|
|
1065
1049
|
resolve('Successfully change file mode');
|
|
1066
1050
|
});
|
|
1067
|
-
}).finally((
|
|
1051
|
+
}).finally(() => {
|
|
1068
1052
|
removeTempListeners(this, 'chmod');
|
|
1069
1053
|
this._resetEventFlags();
|
|
1070
|
-
return rsp;
|
|
1071
1054
|
});
|
|
1072
1055
|
}
|
|
1073
1056
|
|
|
@@ -1209,13 +1192,12 @@ class SftpClient {
|
|
|
1209
1192
|
this.debugMsg('end: Have connection - calling end()');
|
|
1210
1193
|
this.client.end();
|
|
1211
1194
|
}
|
|
1212
|
-
}).finally((
|
|
1195
|
+
}).finally(() => {
|
|
1213
1196
|
this.debugMsg('end: finally clause fired');
|
|
1214
1197
|
removeTempListeners(this, 'end');
|
|
1215
1198
|
this.removeListener('close', endCloseHandler);
|
|
1216
1199
|
this.endCalled = false;
|
|
1217
1200
|
this._resetEventFlags();
|
|
1218
|
-
return resp;
|
|
1219
1201
|
});
|
|
1220
1202
|
}
|
|
1221
1203
|
}
|
package/src/utils.js
CHANGED
|
@@ -203,29 +203,33 @@ function haveLocalAccess(filePath, mode = 'r') {
|
|
|
203
203
|
code: 0,
|
|
204
204
|
};
|
|
205
205
|
} catch (err) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
206
|
+
switch (err.errno) {
|
|
207
|
+
case -2:
|
|
208
|
+
return {
|
|
209
|
+
status: false,
|
|
210
|
+
type: null,
|
|
211
|
+
details: 'not exist',
|
|
212
|
+
code: -2,
|
|
213
|
+
};
|
|
214
|
+
case -13:
|
|
215
|
+
return {
|
|
216
|
+
status: false,
|
|
217
|
+
type: localExists(filePath),
|
|
218
|
+
details: 'permission denied',
|
|
219
|
+
code: -13,
|
|
220
|
+
};
|
|
221
|
+
case -20:
|
|
222
|
+
return {
|
|
223
|
+
status: false,
|
|
224
|
+
type: null,
|
|
225
|
+
details: 'parent not a directory',
|
|
226
|
+
};
|
|
227
|
+
default:
|
|
228
|
+
return {
|
|
229
|
+
status: false,
|
|
230
|
+
type: null,
|
|
231
|
+
details: err.message,
|
|
232
|
+
};
|
|
229
233
|
}
|
|
230
234
|
}
|
|
231
235
|
}
|
|
@@ -281,10 +285,10 @@ async function normalizeRemotePath(client, aPath) {
|
|
|
281
285
|
try {
|
|
282
286
|
if (aPath.startsWith('..')) {
|
|
283
287
|
let root = await client.realPath('..');
|
|
284
|
-
return root + client.remotePathSep + aPath.
|
|
288
|
+
return root + client.remotePathSep + aPath.slice(3);
|
|
285
289
|
} else if (aPath.startsWith('.')) {
|
|
286
290
|
let root = await client.realPath('.');
|
|
287
|
-
return root + client.remotePathSep + aPath.
|
|
291
|
+
return root + client.remotePathSep + aPath.slice(2);
|
|
288
292
|
}
|
|
289
293
|
return aPath;
|
|
290
294
|
} catch (err) {
|