ssh2-sftp-client 7.0.1 → 7.1.0

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 CHANGED
@@ -6,7 +6,7 @@
6
6
  - [Specifying Paths](#sec-5-1)
7
7
  - [Methods](#sec-5-2)
8
8
  - [new SftpClient(name) ===> SFTP client object](#sec-5-2-1)
9
- - [connect(config) ===> SFTPstream](#sec-5-2-2)
9
+ - [connect(config) ===> SFTP object](#sec-5-2-2)
10
10
  - [list(path, pattern) ==> Array[object]](#sec-5-2-3)
11
11
  - [exists(path) ==> boolean](#sec-5-2-4)
12
12
  - [stat(path) ==> object](#sec-5-2-5)
@@ -40,6 +40,7 @@
40
40
  - [How can I connect through a Socks Proxy](#sec-7-4)
41
41
  - [Timeout while waiting for handshake or handshake errors](#sec-7-5)
42
42
  - [How can I limit upload/download speed](#sec-7-6)
43
+ - [Connection hangs or fails for larger files](#sec-7-7)
43
44
  - [Examples](#sec-8)
44
45
  - [Troubleshooting](#sec-9)
45
46
  - [Common Errors](#sec-9-1)
@@ -60,9 +61,9 @@ an SFTP client for node.js, a wrapper around [SSH2](https://github.com/mscdex/ss
60
61
 
61
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.
62
63
 
63
- Current stable release is **v7.0.0**.
64
+ Current stable release is **v7.1.0**.
64
65
 
65
- Code has been tested against Node versions 12.22.1, 14.17.0 and 16.2.0
66
+ Code has been tested against Node versions 12.22.6, 14.17.6 and 16.10.0
66
67
 
67
68
  Node versions < 10.x are not supported.
68
69
 
@@ -94,10 +95,12 @@ sftp.connect({
94
95
 
95
96
  # Version 7.x Changes<a id="sec-4"></a>
96
97
 
97
- - This version is based on version 1.1.0 of `ssh2`. This version of `ssh2` is a complete re-write of the `ssh2` library. This re-write addresses issues encountered when using node v14 as well as some design weaknesses in the previous 0.8.x version.
98
+ - This version is based on version 1.x.x of `ssh2`. This version of `ssh2` is a complete re-write of the `ssh2` library. This re-write addresses issues encountered when using node v14 as well as some design weaknesses in the previous 0.8.x version.
98
99
 
99
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.
100
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
+
101
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.
102
105
 
103
106
  # Documentation<a id="sec-5"></a>
@@ -174,7 +177,7 @@ Constructor to create a new `ssh2-sftp-client` object. An optional `name` string
174
177
  });
175
178
  ```
176
179
 
177
- ### connect(config) ===> SFTPstream<a id="sec-5-2-2"></a>
180
+ ### connect(config) ===> SFTP object<a id="sec-5-2-2"></a>
178
181
 
179
182
  Connect to an sftp server. Full documentation for connection options is available [here](https://github.com/mscdex/ssh2#user-content-client-methods)
180
183
 
@@ -496,11 +499,12 @@ Upload data from local system to remote server. If the `src` argument is a strin
496
499
  flags: 'w', // w - write and a - append
497
500
  encoding: null, // use null for binary files
498
501
  mode: 0o666, // mode to use for created file (rwx)
499
- autoClose: true // automatically close the write stream when finished
500
502
  }}
501
503
  ```
502
504
 
503
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.
504
508
 
505
509
  2. Example Use
506
510
 
@@ -1106,6 +1110,8 @@ client.connect({
1106
1110
 
1107
1111
  Some users have encountered the error 'Timeout while waiting for handshake' or 'Handshake failed, no matching client->server ciphers. This is often due to the client not having the correct configuration for the transport layer algorithms used by ssh2. One of the connect options provided by the ssh2 module is `algorithm`, which is an object that allows you to explicitly set the key exchange, ciphers, hmac and compression algorithms as well as server host key used to establish the initial secure connection. See the SSH2 documentation for details. Getting these parameters correct usually resolves the issue.
1108
1112
 
1113
+ When encountering this type of problem, one worthwhile approach is to use openSSH's CLI sftp program with the `-v` switch to raise loggin levels. This will show you what algorithms the CLI is using. You can then use this information to match the names with the accepted algorithm names documented in the `ssh2` README to set the properties in the `algorithms` object.
1114
+
1109
1115
  ## How can I limit upload/download speed<a id="sec-7-6"></a>
1110
1116
 
1111
1117
  If you want to limit the amount of bandwidth used during upload/download of data, you can use a stream to limit throughput. The following example was provided by *kennylbj*. Note that there is a caveat that we must set the `autoClose` flag to false to avoid calling an extra `_read()` on a closed stream that may cause \_get Permission Denied error in ssh2-streams.
@@ -1143,6 +1149,14 @@ try {
1143
1149
  }
1144
1150
  ```
1145
1151
 
1152
+ ## Connection hangs or fails for larger files<a id="sec-7-7"></a>
1153
+
1154
+ This was contributed by Ladislav Jacho. Thanks.
1155
+
1156
+ A symptom of this issue is that you are able to upload small files, but uploading larger ones fail. You probably have an MTU/fragmentation problem. For each network interface on both client and server set the MTU to 576, e.g. `ifconfig eth0 mtu 576`. If that works, you need to find the largest MTU which will work for your network. An MTU which is too small will adversely affect throughput speed. A common value to use is an MTU of 1400.
1157
+
1158
+ For more explanation, see [issue #342](https://github.com/theophilusx/ssh2-sftp-client/issues/342).
1159
+
1146
1160
  # Examples<a id="sec-8"></a>
1147
1161
 
1148
1162
  I have started collecting example scripts in the example directory of the repository. These are mainly scripts I have put together in order to investigate issues or provide samples for users. They are not robust, lack adequate error handling and may contain errors. However, I think they are still useful for helping developers see how the module and API can be used.
@@ -1320,6 +1334,8 @@ Perhaps the best assistance is a minimal reproducible example of the issue. Once
1320
1334
 
1321
1335
  Pull requests are always welcomed. However, please ensure your changes pass all tests and if your adding a new feature, that tests for that feature are included. Likewise, for new features or enhancements, please include any relevant documentation updates.
1322
1336
 
1337
+ **Note**: The `README.md` file is generated from the `README.org` file. Therefore, any documentation updates or fixes need to be made to the `README.org` file. This file is *tangled* using `Emacs` org mode. If you don't use Emacs or org-mode, don't be too concerned. The org-mode syntax is straight-forward and similar to *markdown*. I will verify any updates to `README.org` and generate a new `README.md` when necessary. The main point to note is that any changes made directly to `README.md` will not persist and will be lost when a new version is generated, so don't modify that file.
1338
+
1323
1339
  This module will adopt a standard semantic versioning policy. Please indicate in your pull request what level of change it represents i.e.
1324
1340
 
1325
1341
  - **Major:** Change to API or major change in functionality which will require an increase in major version number.
@@ -1341,3 +1357,7 @@ Thanks to the following for their contributions -
1341
1357
  - **teenangst:** Contributed fix for error code 4 in stat() method
1342
1358
  - **kennylbj:** Contributed example of using a throttle stream to limit upload/download bandwidth.
1343
1359
  - **anton-erofeev:** Documentation fix
1360
+ - **Ladislav Jacho:** Contributed solution explanation for connections hanging when transferring larger files.
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.
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.0.0*.
12
+ Current stable release is *v7.1.0*.
13
13
 
14
- Code has been tested against Node versions 12.22.1, 14.17.0 and 16.2.0
14
+ Code has been tested against Node versions 12.22.6, 14.17.6 and 16.10.0
15
15
 
16
16
  Node versions < 10.x are not supported.
17
17
 
@@ -43,7 +43,7 @@ npm install ssh2-sftp-client
43
43
 
44
44
  * Version 7.x Changes
45
45
 
46
- - This version is based on version 1.1.0 of ~ssh2~. This version of ~ssh2~ is a
46
+ - This version is based on version 1.x.x of ~ssh2~. This version of ~ssh2~ is a
47
47
  complete re-write of the ~ssh2~ library. This re-write addresses issues
48
48
  encountered when using node v14 as well as some design weaknesses in the
49
49
  previous 0.8.x version.
@@ -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
@@ -175,7 +183,7 @@ client has thrown the error.
175
183
  });
176
184
  #+end_src
177
185
 
178
- *** connect(config) ===> SFTPstream
186
+ *** connect(config) ===> SFTP object
179
187
 
180
188
  Connect to an sftp server. Full documentation for connection options is
181
189
  available [[https://github.com/mscdex/ssh2#user-content-client-methods][here]]
@@ -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
@@ -1355,6 +1370,12 @@ host key used to establish the initial secure connection. See the SSH2
1355
1370
  documentation for details. Getting these parameters correct usually resolves the
1356
1371
  issue.
1357
1372
 
1373
+ When encountering this type of problem, one worthwhile approach is to use
1374
+ openSSH's CLI sftp program with the =-v= switch to raise loggin levels. This
1375
+ will show you what algorithms the CLI is using. You can then use this
1376
+ information to match the names with the accepted algorithm names documented in
1377
+ the =ssh2= README to set the properties in the =algorithms= object.
1378
+
1358
1379
  ** How can I limit upload/download speed
1359
1380
 
1360
1381
  If you want to limit the amount of bandwidth used during upload/download of
@@ -1396,6 +1417,19 @@ issue.
1396
1417
  await client.end();
1397
1418
  }
1398
1419
  #+end_src
1420
+ ** Connection hangs or fails for larger files
1421
+
1422
+ This was contributed by Ladislav Jacho. Thanks.
1423
+
1424
+ A symptom of this issue is that you are able to upload small files, but
1425
+ uploading larger ones fail. You probably have an MTU/fragmentation problem. For
1426
+ each network interface on both client and server set the MTU to 576, e.g.
1427
+ =ifconfig eth0 mtu 576=. If that works, you need to find the largest MTU which
1428
+ will work for your network. An MTU which is too small will adversely affect
1429
+ throughput speed. A common value to use is an MTU of 1400.
1430
+
1431
+ For more explanation, see [[https://github.com/theophilusx/ssh2-sftp-client/issues/342][issue #342]].
1432
+
1399
1433
  * Examples
1400
1434
 
1401
1435
  I have started collecting example scripts in the example directory of the
@@ -1705,6 +1739,15 @@ tests and if your adding a new feature, that tests for that feature are
1705
1739
  included. Likewise, for new features or enhancements, please include any
1706
1740
  relevant documentation updates.
1707
1741
 
1742
+ *Note*: The =README.md= file is generated from the =README.org= file. Therefore,
1743
+ any documentation updates or fixes need to be made to the =README.org= file.
1744
+ This file is /tangled/ using =Emacs= org mode. If you don't use Emacs or
1745
+ org-mode, don't be too concerned. The org-mode syntax is straight-forward and
1746
+ similar to /markdown/. I will verify any updates to =README.org= and generate a
1747
+ new =README.md= when necessary. The main point to note is that any changes made
1748
+ directly to =README.md= will not persist and will be lost when a new version is
1749
+ generated, so don't modify that file.
1750
+
1708
1751
  This module will adopt a standard semantic versioning policy. Please indicate in
1709
1752
  your pull request what level of change it represents i.e.
1710
1753
 
@@ -1734,3 +1777,10 @@ Thanks to the following for their contributions -
1734
1777
  - kennylbj :: Contributed example of using a throttle stream to limit
1735
1778
  upload/download bandwidth.
1736
1779
  - anton-erofeev :: Documentation fix
1780
+ - Ladislav Jacho :: Contributed solution explanation for connections hanging
1781
+ when transferring larger files.
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.
package/package.json CHANGED
@@ -1,17 +1,24 @@
1
1
  {
2
2
  "name": "ssh2-sftp-client",
3
- "version": "7.0.1",
3
+ "version": "7.1.0",
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": ["sftp", "nodejs", "promises"],
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\" --quiet"
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,17 +32,24 @@
25
32
  "dependencies": {
26
33
  "concat-stream": "^2.0.0",
27
34
  "promise-retry": "^2.0.1",
28
- "ssh2": "^1.2.0"
35
+ "ssh2": "^1.5.0"
29
36
  },
30
37
  "devDependencies": {
31
- "chai": "^4.2.0",
38
+ "chai": "^4.3.4",
32
39
  "chai-as-promised": "^7.1.1",
33
40
  "chai-subset": "^1.6.0",
34
- "checksum": "^0.1.1",
41
+ "checksum": "^1.0.0",
35
42
  "dotenv": "^10.0.0",
36
- "mocha": "^9.0.2",
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": "^36.0.0",
49
+ "mocha": "^9.1.2",
37
50
  "moment": "^2.29.1",
38
51
  "nyc": "^15.1.0",
52
+ "prettier": "^2.4.1",
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
@@ -114,7 +114,7 @@ class SftpClient {
114
114
  *
115
115
  * @param {Object} config - an SFTP configuration object
116
116
  *
117
- * @return {Promise} which will resolve to an sftp client object
117
+ * @return {Promise<Object>} which will resolve to an sftp client object
118
118
  *
119
119
  */
120
120
  getConnection(config) {
@@ -133,13 +133,12 @@ class SftpClient {
133
133
  // .catch((err) => {
134
134
  // return Promise.reject(err);
135
135
  // })
136
- .finally(async (resp) => {
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((resp) => {
160
+ }).finally(() => {
162
161
  this.debugMsg('getSftpChannel: finally clause fired');
163
162
  removeTempListeners(this, 'getSftpChannel');
164
163
  this._resetEventFlags();
@@ -174,7 +173,7 @@ class SftpClient {
174
173
  *
175
174
  * @param {Object} config - an SFTP configuration object
176
175
  *
177
- * @return {Promise} which will resolve to an sftp client object
176
+ * @return {Promise<Object>} which will resolve to an sftp client object
178
177
  *
179
178
  */
180
179
  async connect(config) {
@@ -205,7 +204,7 @@ class SftpClient {
205
204
  minTimeout: config.retry_minTimeout || 1000,
206
205
  }
207
206
  );
208
- await this.getSftpChannel();
207
+ return this.getSftpChannel();
209
208
  } catch (err) {
210
209
  this.debugMsg(`connect: Error ${err.message}`);
211
210
  this._resetEventFlags();
@@ -222,7 +221,7 @@ class SftpClient {
222
221
  * Returns undefined if the path does not exists.
223
222
  *
224
223
  * @param {String} remotePath - remote path, may be relative
225
- * @returns {Promise} - remote absolute path or undefined
224
+ * @returns {Promise<String>} - remote absolute path or ''
226
225
  */
227
226
  realPath(remotePath) {
228
227
  return new Promise((resolve, reject) => {
@@ -244,13 +243,19 @@ class SftpClient {
244
243
  resolve(absPath);
245
244
  });
246
245
  }
247
- }).finally((rsp) => {
246
+ }).finally(() => {
248
247
  removeTempListeners(this, 'realPath');
249
248
  this._resetEventFlags();
250
- return rsp;
251
249
  });
252
250
  }
253
251
 
252
+ /**
253
+ * @async
254
+ *
255
+ * Return the current workding directory path
256
+ *
257
+ * @returns {Promise<String>} - current remote working directory
258
+ */
254
259
  cwd() {
255
260
  return this.realPath('.');
256
261
  }
@@ -259,7 +264,7 @@ class SftpClient {
259
264
  * Retrieves attributes for path
260
265
  *
261
266
  * @param {String} remotePath - a string containing the path to a file
262
- * @return {Promise} stats - attributes info
267
+ * @return {Promise<Object>} stats - attributes info
263
268
  */
264
269
  async stat(remotePath) {
265
270
  const _stat = (aPath) => {
@@ -302,9 +307,8 @@ class SftpClient {
302
307
  resolve(result);
303
308
  }
304
309
  });
305
- }).finally((rsp) => {
310
+ }).finally(() => {
306
311
  removeTempListeners(this, 'stat');
307
- return rsp;
308
312
  });
309
313
  };
310
314
 
@@ -314,11 +318,7 @@ class SftpClient {
314
318
  return _stat(absPath);
315
319
  } catch (err) {
316
320
  this._resetEventFlags();
317
- if (err.custom) {
318
- throw err;
319
- } else {
320
- throw fmtError(err, 'stat', err.code);
321
- }
321
+ throw err.custom ? err : fmtError(err, 'stat', err.code);
322
322
  }
323
323
  }
324
324
 
@@ -330,7 +330,7 @@ class SftpClient {
330
330
  *
331
331
  * @param {string} remotePath - path to the object on the sftp server.
332
332
  *
333
- * @return {Promise} returns false if object does not exist. Returns type of
333
+ * @return {Promise<Boolean|String>} returns false if object does not exist. Returns type of
334
334
  * object if it does
335
335
  */
336
336
  async exists(remotePath) {
@@ -369,17 +369,12 @@ class SftpClient {
369
369
  this.debugMsg(`exists: throw error ${err.message} ${err.code}`);
370
370
  throw err;
371
371
  }
372
- } else {
373
- this.debugMsg(`exists: ${remotePath} = false`);
374
- return false;
375
372
  }
373
+ this.debugMsg(`exists: default ${remotePath} = false`);
374
+ return false;
376
375
  } catch (err) {
377
376
  this._resetEventFlags();
378
- if (err.custom) {
379
- throw err;
380
- } else {
381
- throw fmtError(err, 'exists', err.code);
382
- }
377
+ throw err.custom ? err : fmtError(err, 'exists', err.code);
383
378
  }
384
379
  }
385
380
 
@@ -394,8 +389,7 @@ class SftpClient {
394
389
  *
395
390
  * @param {String} remotePath - path to remote directory
396
391
  * @param {RegExp} pattern - regular expression to match filenames
397
- * @returns {Promise} array of file description objects
398
- * @throws {Error}
392
+ * @returns {Promise<Array>} array of file description objects
399
393
  */
400
394
  list(remotePath, pattern = /.*/) {
401
395
  return new Promise((resolve, reject) => {
@@ -413,15 +407,15 @@ class SftpClient {
413
407
  if (fileList) {
414
408
  newList = fileList.map((item) => {
415
409
  return {
416
- type: item.longname.substr(0, 1),
410
+ type: item.longname.slice(0, 1),
417
411
  name: item.filename,
418
412
  size: item.attrs.size,
419
413
  modifyTime: item.attrs.mtime * 1000,
420
414
  accessTime: item.attrs.atime * 1000,
421
415
  rights: {
422
- user: item.longname.substr(1, 3).replace(reg, ''),
423
- group: item.longname.substr(4, 3).replace(reg, ''),
424
- other: item.longname.substr(7, 3).replace(reg, ''),
416
+ user: item.longname.slice(1, 4).replace(reg, ''),
417
+ group: item.longname.slice(4, 7).replace(reg, ''),
418
+ other: item.longname.slice(7, 10).replace(reg, ''),
425
419
  },
426
420
  owner: item.attrs.uid,
427
421
  group: item.attrs.gid,
@@ -442,10 +436,9 @@ class SftpClient {
442
436
  }
443
437
  });
444
438
  }
445
- }).finally((rsp) => {
439
+ }).finally(() => {
446
440
  removeTempListeners(this, 'list');
447
441
  this._resetEventFlags();
448
- return rsp;
449
442
  });
450
443
  }
451
444
 
@@ -462,7 +455,7 @@ class SftpClient {
462
455
  * @param {Object} options - options object with supported properties of readStreamOptions,
463
456
  * writeStreamOptions and pipeOptions.
464
457
  *
465
- * @return {Promise}
458
+ * @return {Promise<String|Stream|Buffer>}
466
459
  */
467
460
  get(
468
461
  remotePath,
@@ -530,7 +523,7 @@ class SftpClient {
530
523
  }
531
524
  rdr.pipe(wtr, options.pipeOptions ? options.pipeOptions : {});
532
525
  }
533
- }).finally((rsp) => {
526
+ }).finally(() => {
534
527
  removeTempListeners(this, 'get');
535
528
  this._resetEventFlags();
536
529
  if (
@@ -548,7 +541,6 @@ class SftpClient {
548
541
  ) {
549
542
  wtr.destroy();
550
543
  }
551
- return rsp;
552
544
  });
553
545
  }
554
546
 
@@ -563,7 +555,7 @@ class SftpClient {
563
555
  * @param {String} remotePath
564
556
  * @param {String} localPath
565
557
  * @param {Object} options
566
- * @return {Promise} the result of downloading the file
558
+ * @return {Promise<String>} the result of downloading the file
567
559
  */
568
560
  async fastGet(remotePath, localPath, options) {
569
561
  try {
@@ -583,7 +575,7 @@ class SftpClient {
583
575
  err.code = errorCode.badPath;
584
576
  throw err;
585
577
  }
586
- await new Promise((resolve, reject) => {
578
+ let rslt = await new Promise((resolve, reject) => {
587
579
  if (haveConnection(this, 'fastGet', reject)) {
588
580
  this.debugMsg(
589
581
  `fastGet -> remote: ${remotePath} local: ${localPath} `,
@@ -598,10 +590,10 @@ class SftpClient {
598
590
  resolve(`${remotePath} was successfully download to ${localPath}!`);
599
591
  });
600
592
  }
601
- }).finally((rsp) => {
593
+ }).finally(() => {
602
594
  removeTempListeners(this, 'fastGet');
603
- return rsp;
604
595
  });
596
+ return rslt;
605
597
  } catch (err) {
606
598
  this._resetEventFlags();
607
599
  throw fmtError(err, 'fastGet');
@@ -619,7 +611,7 @@ class SftpClient {
619
611
  * @param {String} localPath
620
612
  * @param {String} remotePath
621
613
  * @param {Object} options
622
- * @return {Promise} the result of downloading the file
614
+ * @return {Promise<String>} the result of downloading the file
623
615
  */
624
616
  fastPut(localPath, remotePath, options) {
625
617
  this.debugMsg(`fastPut -> local ${localPath} remote ${remotePath}`);
@@ -663,10 +655,9 @@ class SftpClient {
663
655
  resolve(`${localPath} was successfully uploaded to ${remotePath}!`);
664
656
  });
665
657
  }
666
- }).finally((rsp) => {
658
+ }).finally(() => {
667
659
  removeTempListeners(this, 'fastPut');
668
660
  this._resetEventFlags();
669
- return rsp;
670
661
  });
671
662
  }
672
663
 
@@ -680,12 +671,16 @@ class SftpClient {
680
671
  * @param {Object} options - options used for read, write stream and pipe configuration
681
672
  * value supported by node. Allowed properties are readStreamOptions,
682
673
  * writeStreamOptions and pipeOptions.
683
- * @return {Promise}
674
+ * @return {Promise<String>}
684
675
  */
685
676
  put(
686
677
  localSrc,
687
678
  remotePath,
688
- options = { readStreamOptions: {}, writeStreamOptions: {}, pipeOptions: {} }
679
+ options = {
680
+ readStreamOptions: {},
681
+ writeStreamOptions: { autoClose: true },
682
+ pipeOptions: {},
683
+ }
689
684
  ) {
690
685
  let wtr, rdr;
691
686
 
@@ -693,6 +688,7 @@ class SftpClient {
693
688
  if (typeof localSrc === 'string') {
694
689
  const localCheck = haveLocalAccess(localSrc);
695
690
  if (!localCheck.status) {
691
+ this.debugMsg(`put: local source check error ${localCheck.details}`);
696
692
  return reject(
697
693
  fmtError(
698
694
  `Bad path: ${localSrc}: ${localCheck.details}`,
@@ -706,12 +702,16 @@ class SftpClient {
706
702
  addTempListeners(this, 'put', reject);
707
703
  wtr = this.sftp.createWriteStream(
708
704
  remotePath,
709
- options.writeStreamOptions ? options.writeStreamOptions : {}
705
+ options.writeStreamOptions
706
+ ? { ...options.writeStreamOptions, autoClose: true }
707
+ : {}
710
708
  );
711
709
  wtr.once('error', (err) => {
710
+ this.debugMsg(`put: write stream error ${err.message}`);
712
711
  reject(fmtError(`${err.message} ${remotePath}`, 'put', err.code));
713
712
  });
714
- wtr.once('finish', () => {
713
+ wtr.once('close', () => {
714
+ this.debugMsg('put: promise resolved');
715
715
  resolve(`Uploaded data stream to ${remotePath}`);
716
716
  });
717
717
  if (localSrc instanceof Buffer) {
@@ -722,13 +722,14 @@ class SftpClient {
722
722
  this.debugMsg(`put source is a file path: ${localSrc}`);
723
723
  rdr = fs.createReadStream(
724
724
  localSrc,
725
- options.readStreamOptions ? options.readStreamOptons : {}
725
+ options.readStreamOptions ? options.readStreamOptions : {}
726
726
  );
727
727
  } else {
728
728
  this.debugMsg('put source is a stream');
729
729
  rdr = localSrc;
730
730
  }
731
731
  rdr.once('error', (err) => {
732
+ this.debugMsg(`put: read stream error ${err.message}`);
732
733
  reject(
733
734
  fmtError(
734
735
  `${err.message} ${
@@ -742,7 +743,7 @@ class SftpClient {
742
743
  rdr.pipe(wtr, options.pipeOptions ? options.pipeOptions : {});
743
744
  }
744
745
  }
745
- }).finally((resp) => {
746
+ }).finally(() => {
746
747
  removeTempListeners(this, 'put');
747
748
  this._resetEventFlags();
748
749
  if (
@@ -753,14 +754,6 @@ class SftpClient {
753
754
  ) {
754
755
  rdr.destroy();
755
756
  }
756
- if (
757
- wtr &&
758
- options.writeStreamOptions &&
759
- options.writeStreamOptions.autoClose === false
760
- ) {
761
- wtr.destroy();
762
- }
763
- return resp;
764
757
  });
765
758
  }
766
759
 
@@ -770,49 +763,45 @@ class SftpClient {
770
763
  * @param {Buffer|stream} input
771
764
  * @param {String} remotePath
772
765
  * @param {Object} options
773
- * @return {Promise}
766
+ * @return {Promise<String>}
774
767
  */
775
- append(input, remotePath, options = {}) {
776
- return this.exists(remotePath).then((fileType) => {
777
- if (fileType && fileType === 'd') {
778
- return Promise.reject(
779
- fmtError(
780
- `Bad path: ${remotePath}: cannot append to a directory`,
781
- 'append',
782
- errorCode.badPath
783
- )
784
- );
785
- }
786
- return new Promise((resolve, reject) => {
787
- if (haveConnection(this, 'append', reject)) {
788
- if (typeof input === 'string') {
789
- reject(fmtError('Cannot append one file to another', 'append'));
768
+ async append(input, remotePath, options = {}) {
769
+ const fileType = await this.exists(remotePath);
770
+ if (fileType && fileType === 'd') {
771
+ throw fmtError(
772
+ `Bad path: ${remotePath}: cannot append to a directory`,
773
+ 'append',
774
+ errorCode.badPath
775
+ );
776
+ }
777
+ return await new Promise((resolve, reject) => {
778
+ if (haveConnection(this, 'append', reject)) {
779
+ if (typeof input === 'string') {
780
+ reject(fmtError('Cannot append one file to another', 'append'));
781
+ } else {
782
+ this.debugMsg(`append -> remote: ${remotePath} `, options);
783
+ addTempListeners(this, 'append', reject);
784
+ options.flags = 'a';
785
+ let stream = this.sftp.createWriteStream(remotePath, options);
786
+ stream.on('error', (err_1) => {
787
+ reject(
788
+ fmtError(`${err_1.message} ${remotePath}`, 'append', err_1.code)
789
+ );
790
+ });
791
+ stream.on('finish', () => {
792
+ resolve(`Appended data to ${remotePath}`);
793
+ });
794
+ if (input instanceof Buffer) {
795
+ stream.write(input);
796
+ stream.end();
790
797
  } else {
791
- this.debugMsg(`append -> remote: ${remotePath} `, options);
792
- addTempListeners(this, 'append', reject);
793
- options.flags = 'a';
794
- let stream = this.sftp.createWriteStream(remotePath, options);
795
- stream.on('error', (err) => {
796
- reject(
797
- fmtError(`${err.message} ${remotePath}`, 'append', err.code)
798
- );
799
- });
800
- stream.on('finish', () => {
801
- resolve(`Appended data to ${remotePath}`);
802
- });
803
- if (input instanceof Buffer) {
804
- stream.write(input);
805
- stream.end();
806
- } else {
807
- input.pipe(stream);
808
- }
798
+ input.pipe(stream);
809
799
  }
810
800
  }
811
- }).finally((rsp) => {
812
- removeTempListeners(this, 'append');
813
- this._resetEventFlags();
814
- return rsp;
815
- });
801
+ }
802
+ }).finally(() => {
803
+ removeTempListeners(this, 'append');
804
+ this._resetEventFlags();
816
805
  });
817
806
  }
818
807
 
@@ -823,7 +812,7 @@ class SftpClient {
823
812
  *
824
813
  * @param {string} remotePath - remote directory path.
825
814
  * @param {boolean} recursive - if true, recursively create directories
826
- * @return {Promise}
815
+ * @return {Promise<String>}
827
816
  */
828
817
  async mkdir(remotePath, recursive = false) {
829
818
  const _mkdir = (p) => {
@@ -838,6 +827,12 @@ class SftpClient {
838
827
  let error = new Error(`Bad path: ${p} permission denied`);
839
828
  error.code = errorCode.badPath;
840
829
  reject(error);
830
+ } else if (err.code === 2) {
831
+ let error = new Error(
832
+ `Bad path: ${p} parent not a directory or not exist`
833
+ );
834
+ error.code = errorCode.badPath;
835
+ reject(error);
841
836
  } else {
842
837
  reject(err);
843
838
  }
@@ -846,10 +841,9 @@ class SftpClient {
846
841
  resolve(`${p} directory created`);
847
842
  }
848
843
  });
849
- }).finally((rsp) => {
844
+ }).finally(() => {
850
845
  removeTempListeners(this, '_mkdir');
851
846
  this._resetEventFlags();
852
- return rsp;
853
847
  });
854
848
  };
855
849
 
@@ -884,7 +878,7 @@ class SftpClient {
884
878
  * @param {string} remotePath - path to directory to be removed
885
879
  * @param {boolean} recursive - if true, remove directories/files in target
886
880
  * directory
887
- * @return {Promise}
881
+ * @return {Promise<String>}
888
882
  */
889
883
  async rmdir(remotePath, recursive = false) {
890
884
  const _rmdir = (p) => {
@@ -898,9 +892,8 @@ class SftpClient {
898
892
  }
899
893
  resolve('Successfully removed directory');
900
894
  });
901
- }).finally((rsp) => {
895
+ }).finally(() => {
902
896
  removeTempListeners(this, 'rmdir');
903
- return rsp;
904
897
  });
905
898
  };
906
899
 
@@ -926,11 +919,7 @@ class SftpClient {
926
919
  return _rmdir(absPath);
927
920
  } catch (err) {
928
921
  this._resetEventFlags();
929
- if (err.custom) {
930
- throw err;
931
- } else {
932
- throw fmtError(err, 'rmdir', err.code);
933
- }
922
+ throw err.custom ? err : fmtError(err, 'rmdir', err.code);
934
923
  }
935
924
  }
936
925
 
@@ -942,7 +931,7 @@ class SftpClient {
942
931
  * @param {string} remotePath - path to the file to delete
943
932
  * @param {boolean} notFoundOK - if true, ignore errors for missing target.
944
933
  * Default is false.
945
- * @return {Promise} with string 'Successfully deleted file' once resolved
934
+ * @return {Promise<String>} with string 'Successfully deleted file' once resolved
946
935
  *
947
936
  */
948
937
  delete(remotePath, notFoundOK = false) {
@@ -965,10 +954,9 @@ class SftpClient {
965
954
  resolve(`Successfully deleted ${remotePath}`);
966
955
  });
967
956
  }
968
- }).finally((rsp) => {
957
+ }).finally(() => {
969
958
  removeTempListeners(this, 'delete');
970
959
  this._resetEventFlags();
971
- return rsp;
972
960
  });
973
961
  }
974
962
 
@@ -980,7 +968,7 @@ class SftpClient {
980
968
  * @param {string} fromPath - path to the file to be renamed.
981
969
  * @param {string} toPath - path to the new name.
982
970
  *
983
- * @return {Promise}
971
+ * @return {Promise<String>}
984
972
  *
985
973
  */
986
974
  rename(fromPath, toPath) {
@@ -1002,10 +990,9 @@ class SftpClient {
1002
990
  resolve(`Successfully renamed ${fromPath} to ${toPath}`);
1003
991
  });
1004
992
  }
1005
- }).finally((rsp) => {
993
+ }).finally(() => {
1006
994
  removeTempListeners(this, 'rename');
1007
995
  this._resetEventFlags();
1008
- return rsp;
1009
996
  });
1010
997
  }
1011
998
 
@@ -1018,7 +1005,7 @@ class SftpClient {
1018
1005
  * @param {string} fromPath - path to the file to be renamed.
1019
1006
  * @param {string} toPath - path the new name.
1020
1007
  *
1021
- * @return {Promise}
1008
+ * @return {Promise<String>}
1022
1009
  *
1023
1010
  */
1024
1011
  posixRename(fromPath, toPath) {
@@ -1040,10 +1027,9 @@ class SftpClient {
1040
1027
  resolve(`Successful POSIX rename ${fromPath} to ${toPath}`);
1041
1028
  });
1042
1029
  }
1043
- }).finally((rsp) => {
1030
+ }).finally(() => {
1044
1031
  removeTempListeners(this, 'posixRename');
1045
1032
  this._resetEventFlags();
1046
- return rsp;
1047
1033
  });
1048
1034
  }
1049
1035
 
@@ -1055,7 +1041,7 @@ class SftpClient {
1055
1041
  * @param {string} remotePath - path to the remote target object.
1056
1042
  * @param {number | string} mode - the new octal mode to set
1057
1043
  *
1058
- * @return {Promise}
1044
+ * @return {Promise<String>}
1059
1045
  */
1060
1046
  chmod(remotePath, mode) {
1061
1047
  return new Promise((resolve, reject) => {
@@ -1067,10 +1053,9 @@ class SftpClient {
1067
1053
  }
1068
1054
  resolve('Successfully change file mode');
1069
1055
  });
1070
- }).finally((rsp) => {
1056
+ }).finally(() => {
1071
1057
  removeTempListeners(this, 'chmod');
1072
1058
  this._resetEventFlags();
1073
- return rsp;
1074
1059
  });
1075
1060
  }
1076
1061
 
@@ -1084,8 +1069,7 @@ class SftpClient {
1084
1069
  * @param {String} dstDir - remote destination directory
1085
1070
  * @param {RegExp} filter - (Optional) a regular expression used to select
1086
1071
  * files and directories to upload
1087
- * @returns {String}
1088
- * @throws {Error}
1072
+ * @returns {Promise<String>}
1089
1073
  */
1090
1074
  async uploadDir(srcDir, dstDir, filter = /.*/) {
1091
1075
  try {
@@ -1130,11 +1114,7 @@ class SftpClient {
1130
1114
  return `${srcDir} uploaded to ${dstDir}`;
1131
1115
  } catch (err) {
1132
1116
  this._resetEventFlags();
1133
- if (err.custom) {
1134
- throw err;
1135
- } else {
1136
- throw fmtError(err, 'uploadDir');
1137
- }
1117
+ throw err.custom ? err : fmtError(err, 'uploadDir');
1138
1118
  }
1139
1119
  }
1140
1120
 
@@ -1148,8 +1128,7 @@ class SftpClient {
1148
1128
  * @param {String} dstDir - local destination directory
1149
1129
  * @param {RegExp} filter - (Optional) a regular expression used to select
1150
1130
  * files and directories to upload
1151
- * @returns {Promise}
1152
- * @throws {Error}
1131
+ * @returns {Promise<String>}
1153
1132
  */
1154
1133
  async downloadDir(srcDir, dstDir, filter = /.*/) {
1155
1134
  try {
@@ -1191,11 +1170,7 @@ class SftpClient {
1191
1170
  return `${srcDir} downloaded to ${dstDir}`;
1192
1171
  } catch (err) {
1193
1172
  this._resetEventFlags();
1194
- if (err.custom) {
1195
- throw err;
1196
- } else {
1197
- throw fmtError(err, 'downloadDir', err.code);
1198
- }
1173
+ throw err.custom ? err : fmtError(err, 'downloadDir', err.code);
1199
1174
  }
1200
1175
  }
1201
1176
 
@@ -1204,6 +1179,7 @@ class SftpClient {
1204
1179
  *
1205
1180
  * End the SFTP connection
1206
1181
  *
1182
+ * @returns {Promise<Boolean>}
1207
1183
  */
1208
1184
  end() {
1209
1185
  let endCloseHandler;
@@ -1220,13 +1196,12 @@ class SftpClient {
1220
1196
  this.debugMsg('end: Have connection - calling end()');
1221
1197
  this.client.end();
1222
1198
  }
1223
- }).finally((resp) => {
1199
+ }).finally(() => {
1224
1200
  this.debugMsg('end: finally clause fired');
1225
1201
  removeTempListeners(this, 'end');
1226
1202
  this.removeListener('close', endCloseHandler);
1227
1203
  this.endCalled = false;
1228
1204
  this._resetEventFlags();
1229
- return resp;
1230
1205
  });
1231
1206
  }
1232
1207
  }
package/src/utils.js CHANGED
@@ -99,7 +99,7 @@ function endListener(client, name, reject) {
99
99
  client.debugMsg(`${name}: handling end event with reject'`);
100
100
  reject(fmtError('Unexpected end event raised', name));
101
101
  } else {
102
- client.debugMst(`${name}: handling end event with throw`);
102
+ client.debugMsg(`${name}: handling end event with throw`);
103
103
  throw fmtError('Unexpected end event raised', name);
104
104
  }
105
105
  }
@@ -157,26 +157,16 @@ function removeTempListeners(obj, name) {
157
157
  * @returns {string | boolean} returns a string for object type if it exists, false otherwise
158
158
  */
159
159
  function localExists(filePath) {
160
- try {
161
- const stats = fs.statSync(filePath);
162
- if (stats.isSymbolicLink()) {
163
- return 'l';
164
- } else if (stats.isDirectory()) {
165
- return 'd';
166
- } else if (stats.isSocket()) {
167
- return 's';
168
- } else if (stats.isFile()) {
169
- return '-';
170
- } else {
171
- throw fmtError(
172
- `Bad path: ${filePath}: unsupported file type`,
173
- 'localExists',
174
- errorCode.badPath
175
- );
176
- }
177
- } catch (err) {
160
+ const stats = fs.statSync(filePath, { throwIfNoEntry: false });
161
+ if (!stats) {
162
+ return false;
163
+ } else if (stats.isDirectory()) {
164
+ return 'd';
165
+ } else if (stats.isFile()) {
166
+ return '-';
167
+ } else {
178
168
  throw fmtError(
179
- `Bad path: ${filePath}: not exist`,
169
+ `Bad path: ${filePath}: target must be a file or directory`,
180
170
  'localExists',
181
171
  errorCode.badPath
182
172
  );
@@ -213,23 +203,33 @@ function haveLocalAccess(filePath, mode = 'r') {
213
203
  code: 0,
214
204
  };
215
205
  } catch (err) {
216
- if (err.errno === -2) {
217
- return {
218
- status: false,
219
- type: null,
220
- details: 'not exist',
221
- code: -2,
222
- };
223
- } else if (err.errno === -13) {
224
- const type = localExists(filePath);
225
- return {
226
- status: false,
227
- type: type,
228
- details: 'permission denied',
229
- code: -13,
230
- };
231
- } else {
232
- throw err;
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
+ };
233
233
  }
234
234
  }
235
235
  }
@@ -246,14 +246,29 @@ function haveLocalAccess(filePath, mode = 'r') {
246
246
  function haveLocalCreate(filePath) {
247
247
  const { status, details, type } = haveLocalAccess(filePath, 'w');
248
248
  if (!status && details === 'permission denied') {
249
- throw new Error(`Bad path: ${filePath}: permission denied`);
249
+ //throw new Error(`Bad path: ${filePath}: permission denied`);
250
+ return {
251
+ status,
252
+ details,
253
+ type,
254
+ };
250
255
  } else if (!status) {
251
256
  const dirPath = path.dirname(filePath);
252
257
  const localCheck = haveLocalAccess(dirPath, 'w');
253
258
  if (localCheck.status && localCheck.type !== 'd') {
254
- throw new Error(`Bad path: ${dirPath}: not a directory`);
259
+ //throw new Error(`Bad path: ${dirPath}: not a directory`);
260
+ return {
261
+ status: false,
262
+ details: `${dirPath}: not a directory`,
263
+ type: null,
264
+ };
255
265
  } else if (!localCheck.status) {
256
- throw new Error(`Bad path: ${dirPath}: ${localCheck.details}`);
266
+ //throw new Error(`Bad path: ${dirPath}: ${localCheck.details}`);
267
+ return {
268
+ status: localCheck.status,
269
+ details: `${dirPath}: ${localCheck.details}`,
270
+ type: null,
271
+ };
257
272
  } else {
258
273
  return {
259
274
  status: true,
@@ -270,10 +285,10 @@ async function normalizeRemotePath(client, aPath) {
270
285
  try {
271
286
  if (aPath.startsWith('..')) {
272
287
  let root = await client.realPath('..');
273
- return root + client.remotePathSep + aPath.substring(3);
288
+ return root + client.remotePathSep + aPath.slice(3);
274
289
  } else if (aPath.startsWith('.')) {
275
290
  let root = await client.realPath('.');
276
- return root + client.remotePathSep + aPath.substring(2);
291
+ return root + client.remotePathSep + aPath.slice(2);
277
292
  }
278
293
  return aPath;
279
294
  } catch (err) {
package/CHANGELOG.org DELETED
@@ -1,245 +0,0 @@
1
- * Change Logging
2
-
3
- ** v7.0.1
4
- - New version based on ssh2 version 1.2.0
5
- - Added some additional path checks to deal with poor error messages and
6
- unreliable behaviour when connecting to an sftp server on Windows based
7
- platforms.
8
- - Added additional tests for local resources (files/directories) to provide
9
- better error handling and informative error messages.
10
- - Updated README to provide more info on managing exceptions
11
- - Changed behaviour of global error handler. This error handler now just logs
12
- the error to the console and sets the client.sftp property to undefined to
13
- ensure any subsequent calls to client methods fail immediately with an
14
- error indicating there is no active connection.
15
-
16
- ** V7.0.0
17
- - New version based on new SSH2 version 1.1.0.
18
- - Expand option handling for get() and put() methods *Breaking Change*
19
- - Re-factored the retry code in the connect() method
20
- - Improve error reporting for adding/removing listeners
21
- - Extend localExists() method to also verify read or write access
22
-
23
- ** V6.0.1
24
- - Fix issue with connect retry not releasing 'ready' listeners
25
- - Add finally clauses to all promises to ensure temporary listeners are deleted
26
- - Add nyc module to report on code test coverage
27
- - Add additional utils tests to increase test coverage
28
- - Removed some dead code and unused utility functions to reduce download size
29
- - Cleanup tests and reduce inter-test dependencies
30
-
31
- ** V6.0.0.0
32
- - Update connection retry code to use the promise-retry module instead of
33
- plain rety module
34
- - Added optional filter argument for uploadDir/downlDir to select which files
35
- and directories are included
36
- - Added an optional boolean argument to delete to turn off raising an error
37
- when delete target does not exists
38
- - Reduced/simplified argument verification code to reduce package size and
39
- increase performance
40
- - Refactored handling of events and add default close and error listeners to
41
- catch connections closed abruptly without an error being raised.
42
-
43
- ** V5.3.2
44
- - Minor README typo fixes
45
- - Fix error in local file path checks (#294)
46
-
47
- ** V5.3.1
48
- - Fix bug in handling of relative local paths
49
- - Change handling of stream closures in ~get()~ and ~put()~ methods
50
-
51
- ** v5.3.0
52
- - Refine event handler management
53
- - Fix path processing for win32 based sftp servers
54
- - Update documentation
55
- ** v5.2.2
56
- - Bug fix release. Add error code 4 check to stat() method.
57
- - bump Mocha version for tests
58
-
59
- ** v5.2.1
60
- - Move some dependencies into dev-Dependencies
61
- ** v5.2.0
62
- - Add new method posixRename() which uses the openSSH POSIX rename extension.
63
- ** v5.1.3
64
- - Fix bug when writing to root directory and failure due to not being able to
65
- determine parent
66
- - Refactor some tests to eliminate need to have artificial delays between
67
- tests
68
- - Bumped some dependency versions to latest version
69
- ** v5.1.2
70
- - Added back global close handler
71
- - Added dumpListeners() method
72
-
73
- ** v5.1.1
74
- - Added separate close handlers to each method.
75
- - Added missing return statement in connect method
76
- - Added additional troubleshooting documentation for
77
- common errors.
78
-
79
- ** v5.1.0
80
- - Fix bug in checkRemotePath() relating to handling of badly
81
- specified paths (issue #213)
82
- - Added additional debugging support
83
- - Add missing test for valid connection in end() method.
84
- - Bump ssh2 version to v0.8.8
85
-
86
- ** v5.0.2
87
- - Fix bugs related to win32 platform and local tests for valid directories
88
- - Fix problem with parsing of file paths
89
-
90
- ** v5.0.1
91
- - Turn down error checking to be less stringent and handle situations
92
- where user does not have read permission on parent directory.
93
-
94
- ** v5.0.0
95
- - Added two new methods ~uploadDir()~ and ~downloadDir()~
96
- - Removed deprecated ~auxList()~ method
97
- - Improved error message consistency
98
- - Added additional error checking to enable more accurate and useful error
99
- messages.
100
- - Added default error handler to deal with event errors which fire outside of
101
- active SftpClient methods (i.e. connection unexpectedly reset by remote host).
102
- - Modified event handlers to ensure that only event handlers added by the
103
- module are removed by the module (users now responsible for removing any
104
- custom event handlers they add).
105
- - Module error handlers added using ~prependListener~ to ensure they are
106
- called before any additional custom handlers added by client code.
107
- - Any error events fired during an ~end()~ call are now ignored.
108
-
109
- ** v4.3.1
110
- - Updated end() method to resolve once close event fires
111
- - Added errorListener to error event in each promise to catch error events
112
- and reject the promise. This should resolve the issue of some error events
113
- causing uncaughtException erros and causing the process to exit.
114
-
115
- ** v4.3.0
116
- - Ensure errors include an err.code property and pass through the error code
117
- from the originating error
118
- - Change tests for error type to use ~error.code~ instead of matching on
119
- ~error.message~.
120
-
121
- ** v4.2.4
122
- - Bumped ssh2 to v0.8.6
123
- - Added exists() usage example to examples directory
124
- - Clarify documentation on get() method
125
- ** v4.2.3
126
- - Fix bug in ~exist()~ where tests on root directory returned false
127
- - Minor documentation fixes
128
- - Clean up mkdir example
129
-
130
- ** v4.2.2
131
- - Minor documentation fixes
132
- - Added additional examples in the ~example~ directory
133
-
134
- ** v4.2.1
135
- - Remove default close listener. changes in ssh2 API removed the utility of a
136
- default close listener
137
- - Fix path handling. Under mixed environments (where client platform and
138
- server platform were different i.e. one windows the other unix), path
139
- handling was broken due tot he use of path.join().
140
- - Ensure error messages include path details. Instead of errors such as "No
141
- such file" now report "No such file /path/to/missing/file" to help with
142
- debugging
143
-
144
- ** v4.2.0
145
- - Work-around for SSH2 =end= event bug
146
- - Added ability to set client name in constructor method
147
- - Added additional error checking to prevent ~connect()~ being called on
148
- already connected client
149
- - Added additional examples in =example= directory
150
-
151
- ** v4.1.0
152
- - move ~end()~ call to resolve into close hook
153
- - Prevent ~put()~ and ~get()~ from creating empty files in destination when
154
- unable to read source
155
- - Expand tests for operations when lacking required permissions
156
- - Add additional data checks for ~append()~
157
- - Verify file exists
158
- - Verify file is writeable
159
- - Verify file is a regular file
160
- - Fix handling of relative paths
161
- - Add ~realPath()~ method
162
- - Add ~cwd()~ method
163
-
164
- ** v4.0.4
165
- - Minor documentation fix
166
- - Fix return value from ~get()~
167
-
168
- ** v4.0.3
169
- - Fix bug in mkdir() relating to handling of relative paths
170
- - Modify exists() to always return 'd' if path is '.'
171
-
172
- ** v4.0.2
173
- - Fix some minor packaging issues
174
-
175
- ** v4.0.0
176
- - Remove support for node < 8.x
177
- - Fix connection retry feature
178
- - sftp connection object set to null when 'end' signal is raised
179
- - Removed 'connectMethod' argument from connect method.
180
- - Refined adding/removing of listeners in connect() and end() methods to enable
181
- errors to be adequately caught and reported.
182
- - Deprecate auxList() and add pattern/regexp filter option to list()
183
- - Refactored handling of event signals to provide better feedback to clients
184
- - Removed pointless 'permissions' property from objects returned by ~stat()~
185
- (same as mode property). Added additional properties describing the type of
186
- object.
187
- - Added the ~removeListener()~ method to compliment the existing ~on()~ method.
188
-
189
- ** Older Versions
190
- *** v2.5.2
191
- - Repository transferred to theophilusx
192
- - Fix error in package.json pointing to wrong repository
193
-
194
- *** v2.5.1
195
- - Apply 4 pull requests to address minor issues prior to transfer
196
-
197
- *** v2.5.0
198
- - ???
199
-
200
- *** v2.4.3
201
- - merge #108, #110
202
- - fix connect promise if connection ends
203
-
204
- *** v2.4.2
205
- - merge #105
206
- - fix windows path
207
-
208
- *** v2.4.1
209
- - merge pr #99, #100
210
- - bug fix
211
-
212
- *** v2.4.0
213
- - Requires node.js v7.5.0 or above.
214
- - merge pr #97, thanks for @theophilusx
215
- - Remove emitter.maxListener warnings
216
- - Upgraded ssh2 dependency from 0.5.5 to 0.6.1
217
- - Enhanced error messages to provide more context and to be more consistent
218
- - re-factored test
219
- - Added new 'exists' method and re-factored mkdir/rmdir
220
-
221
- *** v2.3.0
222
- - add: ~stat~ method
223
- - add ~fastGet~ and ~fastPut~ method.
224
- - fix: ~mkdir~ file exists decision logic
225
-
226
- *** v3.0.0 -- deprecate this version
227
- - change: ~sftp.get~ will return chunk not stream anymore
228
- - fix: get readable not emitting data events in node 10.0.0
229
-
230
- *** v2.1.1
231
- - add: event listener. [[https://github.com/jyu213/ssh2-sftp-client#Event][doc]]
232
- - add: ~get~ or ~put~ method add extra options [[https://github.com/jyu213/ssh2-sftp-client/pull/52][pr#52]]
233
-
234
- *** v2.0.1
235
- - add: ~chmod~ method [[https://github.com/jyu213/ssh2-sftp-client/pull/33][pr#33]]
236
- - update: upgrade ssh2 to V0.5.0 [[https://github.com/jyu213/ssh2-sftp-client/pull/30][pr#30]]
237
- - fix: get method stream error reject unwork [[https://github.com/jyu213/ssh2-sftp-client/issues/22][#22]]
238
- - fix: return Error object on promise rejection [[https://github.com/jyu213/ssh2-sftp-client/pull/20][pr#20]]
239
-
240
- *** v1.1.0
241
- - fix: add encoding control support for binary stream
242
-
243
- *** v1.0.5:
244
- - fix: multi image upload
245
- - change: remove ~this.client.sftp~ to ~connect~ function