@electerm/ssh2 0.8.11 → 1.5.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 +8 -1
- package/install.js +20 -0
- package/lib/Channel.js +236 -450
- package/lib/agent.js +1080 -376
- package/lib/client.js +1698 -1258
- package/lib/http-agents.js +72 -51
- package/lib/index.js +43 -0
- package/lib/protocol/Protocol.js +2077 -0
- package/lib/protocol/SFTP.js +3778 -0
- package/lib/protocol/constants.js +342 -0
- package/lib/protocol/crypto/binding.gyp +14 -0
- package/lib/protocol/crypto/poly1305.js +43 -0
- package/lib/protocol/crypto/src/binding.cc +2003 -0
- package/lib/protocol/crypto.js +1602 -0
- package/lib/protocol/handlers.js +16 -0
- package/lib/protocol/handlers.misc.js +1214 -0
- package/lib/protocol/kex.js +1831 -0
- package/lib/protocol/keyParser.js +1481 -0
- package/lib/protocol/node-fs-compat.js +115 -0
- package/lib/protocol/utils.js +356 -0
- package/lib/protocol/zlib.js +255 -0
- package/lib/server.js +1226 -1019
- package/lib/utils.js +336 -0
- package/package.json +42 -9
- package/lib/SFTPWrapper.js +0 -145
- package/lib/buffer-helpers.js +0 -22
- package/lib/keepalivemgr.js +0 -80
package/lib/utils.js
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { SFTP } = require('./protocol/SFTP.js');
|
|
4
|
+
|
|
5
|
+
const MAX_CHANNEL = 2 ** 32 - 1;
|
|
6
|
+
|
|
7
|
+
function onChannelOpenFailure(self, recipient, info, cb) {
|
|
8
|
+
self._chanMgr.remove(recipient);
|
|
9
|
+
if (typeof cb !== 'function')
|
|
10
|
+
return;
|
|
11
|
+
|
|
12
|
+
let err;
|
|
13
|
+
if (info instanceof Error) {
|
|
14
|
+
err = info;
|
|
15
|
+
} else if (typeof info === 'object' && info !== null) {
|
|
16
|
+
err = new Error(`(SSH) Channel open failure: ${info.description}`);
|
|
17
|
+
err.reason = info.reason;
|
|
18
|
+
} else {
|
|
19
|
+
err = new Error(
|
|
20
|
+
'(SSH) Channel open failure: server closed channel unexpectedly'
|
|
21
|
+
);
|
|
22
|
+
err.reason = '';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
cb(err);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function onCHANNEL_CLOSE(self, recipient, channel, err, dead) {
|
|
29
|
+
if (typeof channel === 'function') {
|
|
30
|
+
// We got CHANNEL_CLOSE instead of CHANNEL_OPEN_FAILURE when
|
|
31
|
+
// requesting to open a channel
|
|
32
|
+
onChannelOpenFailure(self, recipient, err, channel);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (typeof channel !== 'object' || channel === null)
|
|
37
|
+
return;
|
|
38
|
+
|
|
39
|
+
if (channel.incoming && channel.incoming.state === 'closed')
|
|
40
|
+
return;
|
|
41
|
+
|
|
42
|
+
self._chanMgr.remove(recipient);
|
|
43
|
+
|
|
44
|
+
if (channel.server && channel.constructor.name === 'Session')
|
|
45
|
+
return;
|
|
46
|
+
|
|
47
|
+
channel.incoming.state = 'closed';
|
|
48
|
+
|
|
49
|
+
if (channel.readable)
|
|
50
|
+
channel.push(null);
|
|
51
|
+
if (channel.server) {
|
|
52
|
+
if (channel.stderr.writable)
|
|
53
|
+
channel.stderr.end();
|
|
54
|
+
} else if (channel.stderr.readable) {
|
|
55
|
+
channel.stderr.push(null);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (channel.constructor !== SFTP
|
|
59
|
+
&& (channel.outgoing.state === 'open'
|
|
60
|
+
|| channel.outgoing.state === 'eof')
|
|
61
|
+
&& !dead) {
|
|
62
|
+
channel.close();
|
|
63
|
+
}
|
|
64
|
+
if (channel.outgoing.state === 'closing')
|
|
65
|
+
channel.outgoing.state = 'closed';
|
|
66
|
+
|
|
67
|
+
const readState = channel._readableState;
|
|
68
|
+
const writeState = channel._writableState;
|
|
69
|
+
if (writeState && !writeState.ending && !writeState.finished && !dead)
|
|
70
|
+
channel.end();
|
|
71
|
+
|
|
72
|
+
// Take care of any outstanding channel requests
|
|
73
|
+
const chanCallbacks = channel._callbacks;
|
|
74
|
+
channel._callbacks = [];
|
|
75
|
+
for (let i = 0; i < chanCallbacks.length; ++i)
|
|
76
|
+
chanCallbacks[i](true);
|
|
77
|
+
|
|
78
|
+
if (channel.server) {
|
|
79
|
+
if (!channel.readable
|
|
80
|
+
|| channel.destroyed
|
|
81
|
+
|| (readState && readState.endEmitted)) {
|
|
82
|
+
channel.emit('close');
|
|
83
|
+
} else {
|
|
84
|
+
channel.once('end', () => channel.emit('close'));
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
let doClose;
|
|
88
|
+
switch (channel.type) {
|
|
89
|
+
case 'direct-streamlocal@openssh.com':
|
|
90
|
+
case 'direct-tcpip':
|
|
91
|
+
doClose = () => channel.emit('close');
|
|
92
|
+
break;
|
|
93
|
+
default: {
|
|
94
|
+
// Align more with node child processes, where the close event gets
|
|
95
|
+
// the same arguments as the exit event
|
|
96
|
+
const exit = channel._exit;
|
|
97
|
+
doClose = () => {
|
|
98
|
+
if (exit.code === null)
|
|
99
|
+
channel.emit('close', exit.code, exit.signal, exit.dump, exit.desc);
|
|
100
|
+
else
|
|
101
|
+
channel.emit('close', exit.code);
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (!channel.readable
|
|
106
|
+
|| channel.destroyed
|
|
107
|
+
|| (readState && readState.endEmitted)) {
|
|
108
|
+
doClose();
|
|
109
|
+
} else {
|
|
110
|
+
channel.once('end', doClose);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const errReadState = channel.stderr._readableState;
|
|
114
|
+
if (!channel.stderr.readable
|
|
115
|
+
|| channel.stderr.destroyed
|
|
116
|
+
|| (errReadState && errReadState.endEmitted)) {
|
|
117
|
+
channel.stderr.emit('close');
|
|
118
|
+
} else {
|
|
119
|
+
channel.stderr.once('end', () => channel.stderr.emit('close'));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
class ChannelManager {
|
|
125
|
+
constructor(client) {
|
|
126
|
+
this._client = client;
|
|
127
|
+
this._channels = {};
|
|
128
|
+
this._cur = -1;
|
|
129
|
+
this._count = 0;
|
|
130
|
+
}
|
|
131
|
+
add(val) {
|
|
132
|
+
// Attempt to reserve an id
|
|
133
|
+
|
|
134
|
+
let id;
|
|
135
|
+
// Optimized paths
|
|
136
|
+
if (this._cur < MAX_CHANNEL) {
|
|
137
|
+
id = ++this._cur;
|
|
138
|
+
} else if (this._count === 0) {
|
|
139
|
+
// Revert and reset back to fast path once we no longer have any channels
|
|
140
|
+
// open
|
|
141
|
+
this._cur = 0;
|
|
142
|
+
id = 0;
|
|
143
|
+
} else {
|
|
144
|
+
// Slower lookup path
|
|
145
|
+
|
|
146
|
+
// This path is triggered we have opened at least MAX_CHANNEL channels
|
|
147
|
+
// while having at least one channel open at any given time, so we have
|
|
148
|
+
// to search for a free id.
|
|
149
|
+
const channels = this._channels;
|
|
150
|
+
for (let i = 0; i < MAX_CHANNEL; ++i) {
|
|
151
|
+
if (channels[i] === undefined) {
|
|
152
|
+
id = i;
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (id === undefined)
|
|
159
|
+
return -1;
|
|
160
|
+
|
|
161
|
+
this._channels[id] = (val || true);
|
|
162
|
+
++this._count;
|
|
163
|
+
|
|
164
|
+
return id;
|
|
165
|
+
}
|
|
166
|
+
update(id, val) {
|
|
167
|
+
if (typeof id !== 'number' || id < 0 || id >= MAX_CHANNEL || !isFinite(id))
|
|
168
|
+
throw new Error(`Invalid channel id: ${id}`);
|
|
169
|
+
|
|
170
|
+
if (val && this._channels[id])
|
|
171
|
+
this._channels[id] = val;
|
|
172
|
+
}
|
|
173
|
+
get(id) {
|
|
174
|
+
if (typeof id !== 'number' || id < 0 || id >= MAX_CHANNEL || !isFinite(id))
|
|
175
|
+
throw new Error(`Invalid channel id: ${id}`);
|
|
176
|
+
|
|
177
|
+
return this._channels[id];
|
|
178
|
+
}
|
|
179
|
+
remove(id) {
|
|
180
|
+
if (typeof id !== 'number' || id < 0 || id >= MAX_CHANNEL || !isFinite(id))
|
|
181
|
+
throw new Error(`Invalid channel id: ${id}`);
|
|
182
|
+
|
|
183
|
+
if (this._channels[id]) {
|
|
184
|
+
delete this._channels[id];
|
|
185
|
+
if (this._count)
|
|
186
|
+
--this._count;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
cleanup(err) {
|
|
190
|
+
const channels = this._channels;
|
|
191
|
+
this._channels = {};
|
|
192
|
+
this._cur = -1;
|
|
193
|
+
this._count = 0;
|
|
194
|
+
|
|
195
|
+
const chanIDs = Object.keys(channels);
|
|
196
|
+
const client = this._client;
|
|
197
|
+
for (let i = 0; i < chanIDs.length; ++i) {
|
|
198
|
+
const id = +chanIDs[i];
|
|
199
|
+
const channel = channels[id];
|
|
200
|
+
onCHANNEL_CLOSE(client, id, channel._channel || channel, err, true);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const isRegExp = (() => {
|
|
206
|
+
const toString = Object.prototype.toString;
|
|
207
|
+
return (val) => toString.call(val) === '[object RegExp]';
|
|
208
|
+
})();
|
|
209
|
+
|
|
210
|
+
function generateAlgorithmList(algoList, defaultList, supportedList) {
|
|
211
|
+
if (Array.isArray(algoList) && algoList.length > 0) {
|
|
212
|
+
// Exact list
|
|
213
|
+
for (let i = 0; i < algoList.length; ++i) {
|
|
214
|
+
if (supportedList.indexOf(algoList[i]) === -1)
|
|
215
|
+
throw new Error(`Unsupported algorithm: ${algoList[i]}`);
|
|
216
|
+
}
|
|
217
|
+
return algoList;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (typeof algoList === 'object' && algoList !== null) {
|
|
221
|
+
// Operations based on the default list
|
|
222
|
+
const keys = Object.keys(algoList);
|
|
223
|
+
let list = defaultList;
|
|
224
|
+
for (let i = 0; i < keys.length; ++i) {
|
|
225
|
+
const key = keys[i];
|
|
226
|
+
let val = algoList[key];
|
|
227
|
+
switch (key) {
|
|
228
|
+
case 'append':
|
|
229
|
+
if (!Array.isArray(val))
|
|
230
|
+
val = [val];
|
|
231
|
+
if (Array.isArray(val)) {
|
|
232
|
+
for (let j = 0; j < val.length; ++j) {
|
|
233
|
+
const append = val[j];
|
|
234
|
+
if (typeof append === 'string') {
|
|
235
|
+
if (!append || list.indexOf(append) !== -1)
|
|
236
|
+
continue;
|
|
237
|
+
if (supportedList.indexOf(append) === -1)
|
|
238
|
+
throw new Error(`Unsupported algorithm: ${append}`);
|
|
239
|
+
if (list === defaultList)
|
|
240
|
+
list = list.slice();
|
|
241
|
+
list.push(append);
|
|
242
|
+
} else if (isRegExp(append)) {
|
|
243
|
+
for (let k = 0; k < supportedList.length; ++k) {
|
|
244
|
+
const algo = supportedList[k];
|
|
245
|
+
if (append.test(algo)) {
|
|
246
|
+
if (list.indexOf(algo) !== -1)
|
|
247
|
+
continue;
|
|
248
|
+
if (list === defaultList)
|
|
249
|
+
list = list.slice();
|
|
250
|
+
list.push(algo);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
break;
|
|
257
|
+
case 'prepend':
|
|
258
|
+
if (!Array.isArray(val))
|
|
259
|
+
val = [val];
|
|
260
|
+
if (Array.isArray(val)) {
|
|
261
|
+
for (let j = val.length; j >= 0; --j) {
|
|
262
|
+
const prepend = val[j];
|
|
263
|
+
if (typeof prepend === 'string') {
|
|
264
|
+
if (!prepend || list.indexOf(prepend) !== -1)
|
|
265
|
+
continue;
|
|
266
|
+
if (supportedList.indexOf(prepend) === -1)
|
|
267
|
+
throw new Error(`Unsupported algorithm: ${prepend}`);
|
|
268
|
+
if (list === defaultList)
|
|
269
|
+
list = list.slice();
|
|
270
|
+
list.unshift(prepend);
|
|
271
|
+
} else if (isRegExp(prepend)) {
|
|
272
|
+
for (let k = supportedList.length; k >= 0; --k) {
|
|
273
|
+
const algo = supportedList[k];
|
|
274
|
+
if (prepend.test(algo)) {
|
|
275
|
+
if (list.indexOf(algo) !== -1)
|
|
276
|
+
continue;
|
|
277
|
+
if (list === defaultList)
|
|
278
|
+
list = list.slice();
|
|
279
|
+
list.unshift(algo);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
break;
|
|
286
|
+
case 'remove':
|
|
287
|
+
if (!Array.isArray(val))
|
|
288
|
+
val = [val];
|
|
289
|
+
if (Array.isArray(val)) {
|
|
290
|
+
for (let j = 0; j < val.length; ++j) {
|
|
291
|
+
const search = val[j];
|
|
292
|
+
if (typeof search === 'string') {
|
|
293
|
+
if (!search)
|
|
294
|
+
continue;
|
|
295
|
+
const idx = list.indexOf(search);
|
|
296
|
+
if (idx === -1)
|
|
297
|
+
continue;
|
|
298
|
+
if (list === defaultList)
|
|
299
|
+
list = list.slice();
|
|
300
|
+
list.splice(idx, 1);
|
|
301
|
+
} else if (isRegExp(search)) {
|
|
302
|
+
for (let k = 0; k < list.length; ++k) {
|
|
303
|
+
if (search.test(list[k])) {
|
|
304
|
+
if (list === defaultList)
|
|
305
|
+
list = list.slice();
|
|
306
|
+
list.splice(k, 1);
|
|
307
|
+
--k;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return list;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return defaultList;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
module.exports = {
|
|
324
|
+
ChannelManager,
|
|
325
|
+
generateAlgorithmList,
|
|
326
|
+
onChannelOpenFailure,
|
|
327
|
+
onCHANNEL_CLOSE,
|
|
328
|
+
isWritable: (stream) => {
|
|
329
|
+
// XXX: hack to workaround regression in node
|
|
330
|
+
// See: https://github.com/nodejs/node/issues/36029
|
|
331
|
+
return (stream
|
|
332
|
+
&& stream.writable
|
|
333
|
+
&& stream._readableState
|
|
334
|
+
&& stream._readableState.ended === false);
|
|
335
|
+
},
|
|
336
|
+
};
|
package/package.json
CHANGED
|
@@ -1,16 +1,49 @@
|
|
|
1
|
-
{
|
|
2
|
-
"
|
|
1
|
+
{
|
|
2
|
+
"name": "@electerm/ssh2",
|
|
3
|
+
"version": "1.5.0",
|
|
3
4
|
"author": "Brian White <mscdex@mscdex.net>",
|
|
4
5
|
"description": "SSH2 client and server modules written in pure JavaScript for node.js",
|
|
5
|
-
"main": "./lib/
|
|
6
|
-
"engines": {
|
|
6
|
+
"main": "./lib/index.js",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=10.16.0"
|
|
9
|
+
},
|
|
7
10
|
"dependencies": {
|
|
8
|
-
"
|
|
11
|
+
"asn1": "^0.2.4",
|
|
12
|
+
"bcrypt-pbkdf": "^1.0.2"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@mscdex/eslint-config": "^1.0.0",
|
|
16
|
+
"eslint": "^7.0.0"
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"cpu-features": "0.0.2",
|
|
20
|
+
"nan": "^2.15.0"
|
|
9
21
|
},
|
|
10
22
|
"scripts": {
|
|
11
|
-
"
|
|
23
|
+
"install": "node install.js",
|
|
24
|
+
"rebuild": "node install.js",
|
|
25
|
+
"test": "node test/test.js",
|
|
26
|
+
"lint": "eslint --cache --report-unused-disable-directives --ext=.js .eslintrc.js examples lib test",
|
|
27
|
+
"lint:fix": "npm run lint -- --fix"
|
|
12
28
|
},
|
|
13
|
-
"keywords": [
|
|
14
|
-
|
|
15
|
-
|
|
29
|
+
"keywords": [
|
|
30
|
+
"ssh",
|
|
31
|
+
"ssh2",
|
|
32
|
+
"sftp",
|
|
33
|
+
"secure",
|
|
34
|
+
"shell",
|
|
35
|
+
"exec",
|
|
36
|
+
"remote",
|
|
37
|
+
"client"
|
|
38
|
+
],
|
|
39
|
+
"licenses": [
|
|
40
|
+
{
|
|
41
|
+
"type": "MIT",
|
|
42
|
+
"url": "http://github.com/mscdex/ssh2/raw/master/LICENSE"
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "http://github.com/mscdex/ssh2.git"
|
|
48
|
+
}
|
|
16
49
|
}
|
package/lib/SFTPWrapper.js
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
// This wrapper class is used to retain backwards compatibility with
|
|
2
|
-
// pre-v0.4 ssh2. If it weren't for `read()` and `write()` being used by the
|
|
3
|
-
// streams2/3 API, we could just pass the SFTPStream directly to the end user...
|
|
4
|
-
|
|
5
|
-
var inherits = require('util').inherits;
|
|
6
|
-
var EventEmitter = require('events').EventEmitter;
|
|
7
|
-
|
|
8
|
-
function SFTPWrapper(stream) {
|
|
9
|
-
var self = this;
|
|
10
|
-
|
|
11
|
-
EventEmitter.call(this);
|
|
12
|
-
|
|
13
|
-
this._stream = stream;
|
|
14
|
-
|
|
15
|
-
stream.on('error', function(err) {
|
|
16
|
-
self.emit('error', err);
|
|
17
|
-
}).on('end', function() {
|
|
18
|
-
self.emit('end');
|
|
19
|
-
}).on('close', function() {
|
|
20
|
-
self.emit('close');
|
|
21
|
-
}).on('continue', function() {
|
|
22
|
-
self.emit('continue');
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
inherits(SFTPWrapper, EventEmitter);
|
|
26
|
-
|
|
27
|
-
// stream-related methods to pass on
|
|
28
|
-
SFTPWrapper.prototype.end = function() {
|
|
29
|
-
return this._stream.end();
|
|
30
|
-
};
|
|
31
|
-
// SFTPStream client methods
|
|
32
|
-
SFTPWrapper.prototype.createReadStream = function(path, options) {
|
|
33
|
-
return this._stream.createReadStream(path, options);
|
|
34
|
-
};
|
|
35
|
-
SFTPWrapper.prototype.createWriteStream = function(path, options) {
|
|
36
|
-
return this._stream.createWriteStream(path, options);
|
|
37
|
-
};
|
|
38
|
-
SFTPWrapper.prototype.open = function(path, flags, attrs, cb) {
|
|
39
|
-
return this._stream.open(path, flags, attrs, cb);
|
|
40
|
-
};
|
|
41
|
-
SFTPWrapper.prototype.close = function(handle, cb) {
|
|
42
|
-
return this._stream.close(handle, cb);
|
|
43
|
-
};
|
|
44
|
-
SFTPWrapper.prototype.read = function(handle, buf, off, len, position, cb) {
|
|
45
|
-
return this._stream.readData(handle, buf, off, len, position, cb);
|
|
46
|
-
};
|
|
47
|
-
SFTPWrapper.prototype.write = function(handle, buf, off, len, position, cb) {
|
|
48
|
-
return this._stream.writeData(handle, buf, off, len, position, cb);
|
|
49
|
-
};
|
|
50
|
-
SFTPWrapper.prototype.fastGet = function(remotePath, localPath, opts, cb) {
|
|
51
|
-
return this._stream.fastGet(remotePath, localPath, opts, cb);
|
|
52
|
-
};
|
|
53
|
-
SFTPWrapper.prototype.fastPut = function(localPath, remotePath, opts, cb) {
|
|
54
|
-
return this._stream.fastPut(localPath, remotePath, opts, cb);
|
|
55
|
-
};
|
|
56
|
-
SFTPWrapper.prototype.readFile = function(path, options, callback_) {
|
|
57
|
-
return this._stream.readFile(path, options, callback_);
|
|
58
|
-
};
|
|
59
|
-
SFTPWrapper.prototype.writeFile = function(path, data, options, callback_) {
|
|
60
|
-
return this._stream.writeFile(path, data, options, callback_);
|
|
61
|
-
};
|
|
62
|
-
SFTPWrapper.prototype.appendFile = function(path, data, options, callback_) {
|
|
63
|
-
return this._stream.appendFile(path, data, options, callback_);
|
|
64
|
-
};
|
|
65
|
-
SFTPWrapper.prototype.exists = function(path, cb) {
|
|
66
|
-
return this._stream.exists(path, cb);
|
|
67
|
-
};
|
|
68
|
-
SFTPWrapper.prototype.unlink = function(filename, cb) {
|
|
69
|
-
return this._stream.unlink(filename, cb);
|
|
70
|
-
};
|
|
71
|
-
SFTPWrapper.prototype.rename = function(oldPath, newPath, cb) {
|
|
72
|
-
return this._stream.rename(oldPath, newPath, cb);
|
|
73
|
-
};
|
|
74
|
-
SFTPWrapper.prototype.mkdir = function(path, attrs, cb) {
|
|
75
|
-
return this._stream.mkdir(path, attrs, cb);
|
|
76
|
-
};
|
|
77
|
-
SFTPWrapper.prototype.rmdir = function(path, cb) {
|
|
78
|
-
return this._stream.rmdir(path, cb);
|
|
79
|
-
};
|
|
80
|
-
SFTPWrapper.prototype.readdir = function(where, opts, cb) {
|
|
81
|
-
return this._stream.readdir(where, opts, cb);
|
|
82
|
-
};
|
|
83
|
-
SFTPWrapper.prototype.fstat = function(handle, cb) {
|
|
84
|
-
return this._stream.fstat(handle, cb);
|
|
85
|
-
};
|
|
86
|
-
SFTPWrapper.prototype.stat = function(path, cb) {
|
|
87
|
-
return this._stream.stat(path, cb);
|
|
88
|
-
};
|
|
89
|
-
SFTPWrapper.prototype.lstat = function(path, cb) {
|
|
90
|
-
return this._stream.lstat(path, cb);
|
|
91
|
-
};
|
|
92
|
-
SFTPWrapper.prototype.opendir = function(path, cb) {
|
|
93
|
-
return this._stream.opendir(path, cb);
|
|
94
|
-
};
|
|
95
|
-
SFTPWrapper.prototype.setstat = function(path, attrs, cb) {
|
|
96
|
-
return this._stream.setstat(path, attrs, cb);
|
|
97
|
-
};
|
|
98
|
-
SFTPWrapper.prototype.fsetstat = function(handle, attrs, cb) {
|
|
99
|
-
return this._stream.fsetstat(handle, attrs, cb);
|
|
100
|
-
};
|
|
101
|
-
SFTPWrapper.prototype.futimes = function(handle, atime, mtime, cb) {
|
|
102
|
-
return this._stream.futimes(handle, atime, mtime, cb);
|
|
103
|
-
};
|
|
104
|
-
SFTPWrapper.prototype.utimes = function(path, atime, mtime, cb) {
|
|
105
|
-
return this._stream.utimes(path, atime, mtime, cb);
|
|
106
|
-
};
|
|
107
|
-
SFTPWrapper.prototype.fchown = function(handle, uid, gid, cb) {
|
|
108
|
-
return this._stream.fchown(handle, uid, gid, cb);
|
|
109
|
-
};
|
|
110
|
-
SFTPWrapper.prototype.chown = function(path, uid, gid, cb) {
|
|
111
|
-
return this._stream.chown(path, uid, gid, cb);
|
|
112
|
-
};
|
|
113
|
-
SFTPWrapper.prototype.fchmod = function(handle, mode, cb) {
|
|
114
|
-
return this._stream.fchmod(handle, mode, cb);
|
|
115
|
-
};
|
|
116
|
-
SFTPWrapper.prototype.chmod = function(path, mode, cb) {
|
|
117
|
-
return this._stream.chmod(path, mode, cb);
|
|
118
|
-
};
|
|
119
|
-
SFTPWrapper.prototype.readlink = function(path, cb) {
|
|
120
|
-
return this._stream.readlink(path, cb);
|
|
121
|
-
};
|
|
122
|
-
SFTPWrapper.prototype.symlink = function(targetPath, linkPath, cb) {
|
|
123
|
-
return this._stream.symlink(targetPath, linkPath, cb);
|
|
124
|
-
};
|
|
125
|
-
SFTPWrapper.prototype.realpath = function(path, cb) {
|
|
126
|
-
return this._stream.realpath(path, cb);
|
|
127
|
-
};
|
|
128
|
-
// extended requests
|
|
129
|
-
SFTPWrapper.prototype.ext_openssh_rename = function(oldPath, newPath, cb) {
|
|
130
|
-
return this._stream.ext_openssh_rename(oldPath, newPath, cb);
|
|
131
|
-
};
|
|
132
|
-
SFTPWrapper.prototype.ext_openssh_statvfs = function(path, cb) {
|
|
133
|
-
return this._stream.ext_openssh_statvfs(path, cb);
|
|
134
|
-
};
|
|
135
|
-
SFTPWrapper.prototype.ext_openssh_fstatvfs = function(handle, cb) {
|
|
136
|
-
return this._stream.ext_openssh_fstatvfs(handle, cb);
|
|
137
|
-
};
|
|
138
|
-
SFTPWrapper.prototype.ext_openssh_hardlink = function(oldPath, newPath, cb) {
|
|
139
|
-
return this._stream.ext_openssh_hardlink(oldPath, newPath, cb);
|
|
140
|
-
};
|
|
141
|
-
SFTPWrapper.prototype.ext_openssh_fsync = function(handle, cb) {
|
|
142
|
-
return this._stream.ext_openssh_fsync(handle, cb);
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
module.exports = SFTPWrapper;
|
package/lib/buffer-helpers.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
readUInt32BE: function readUInt32BE(buf, offset) {
|
|
3
|
-
return buf[offset++] * 16777216
|
|
4
|
-
+ buf[offset++] * 65536
|
|
5
|
-
+ buf[offset++] * 256
|
|
6
|
-
+ buf[offset];
|
|
7
|
-
},
|
|
8
|
-
writeUInt32BE: function writeUInt32BE(buf, value, offset) {
|
|
9
|
-
buf[offset++] = (value >>> 24);
|
|
10
|
-
buf[offset++] = (value >>> 16);
|
|
11
|
-
buf[offset++] = (value >>> 8);
|
|
12
|
-
buf[offset++] = value;
|
|
13
|
-
return offset;
|
|
14
|
-
},
|
|
15
|
-
writeUInt32LE: function writeUInt32LE(buf, value, offset) {
|
|
16
|
-
buf[offset++] = value;
|
|
17
|
-
buf[offset++] = (value >>> 8);
|
|
18
|
-
buf[offset++] = (value >>> 16);
|
|
19
|
-
buf[offset++] = (value >>> 24);
|
|
20
|
-
return offset;
|
|
21
|
-
}
|
|
22
|
-
};
|
package/lib/keepalivemgr.js
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
function spliceOne(list, index) {
|
|
2
|
-
for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1)
|
|
3
|
-
list[i] = list[k];
|
|
4
|
-
list.pop();
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
function Manager(interval, streamInterval, kaCountMax) {
|
|
8
|
-
var streams = this._streams = [];
|
|
9
|
-
this._timer = undefined;
|
|
10
|
-
this._timerInterval = interval;
|
|
11
|
-
this._timerfn = function() {
|
|
12
|
-
var now = Date.now();
|
|
13
|
-
for (var i = 0, len = streams.length, s, last; i < len; ++i) {
|
|
14
|
-
s = streams[i];
|
|
15
|
-
last = s._kalast;
|
|
16
|
-
if (last && (now - last) >= streamInterval) {
|
|
17
|
-
if (++s._kacnt > kaCountMax) {
|
|
18
|
-
var err = new Error('Keepalive timeout');
|
|
19
|
-
err.level = 'client-timeout';
|
|
20
|
-
s.emit('error', err);
|
|
21
|
-
s.disconnect();
|
|
22
|
-
spliceOne(streams, i);
|
|
23
|
-
--i;
|
|
24
|
-
len = streams.length;
|
|
25
|
-
} else {
|
|
26
|
-
s._kalast = now;
|
|
27
|
-
// XXX: if the server ever starts sending real global requests to the
|
|
28
|
-
// client, we will need to add a dummy callback here to keep the
|
|
29
|
-
// correct reply order
|
|
30
|
-
s.ping();
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
Manager.prototype.start = function() {
|
|
38
|
-
if (this._timer)
|
|
39
|
-
this.stop();
|
|
40
|
-
this._timer = setInterval(this._timerfn, this._timerInterval);
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
Manager.prototype.stop = function() {
|
|
44
|
-
if (this._timer) {
|
|
45
|
-
clearInterval(this._timer);
|
|
46
|
-
this._timer = undefined;
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
Manager.prototype.add = function(stream) {
|
|
51
|
-
var streams = this._streams,
|
|
52
|
-
self = this;
|
|
53
|
-
|
|
54
|
-
stream.once('end', function() {
|
|
55
|
-
self.remove(stream);
|
|
56
|
-
}).on('packet', resetKA);
|
|
57
|
-
|
|
58
|
-
streams[streams.length] = stream;
|
|
59
|
-
|
|
60
|
-
resetKA();
|
|
61
|
-
|
|
62
|
-
if (!this._timer)
|
|
63
|
-
this.start();
|
|
64
|
-
|
|
65
|
-
function resetKA() {
|
|
66
|
-
stream._kalast = Date.now();
|
|
67
|
-
stream._kacnt = 0;
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
Manager.prototype.remove = function(stream) {
|
|
72
|
-
var streams = this._streams,
|
|
73
|
-
index = streams.indexOf(stream);
|
|
74
|
-
if (index > -1)
|
|
75
|
-
spliceOne(streams, index);
|
|
76
|
-
if (!streams.length)
|
|
77
|
-
this.stop();
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
module.exports = Manager;
|