first commit
This commit is contained in:
506
build/node_modules/ssh2/lib/Channel.js
generated
vendored
Normal file
506
build/node_modules/ssh2/lib/Channel.js
generated
vendored
Normal file
@@ -0,0 +1,506 @@
|
||||
var inherits = require('util').inherits;
|
||||
var DuplexStream = require('stream').Duplex;
|
||||
var ReadableStream = require('stream').Readable;
|
||||
var WritableStream = require('stream').Writable;
|
||||
|
||||
var STDERR = require('ssh2-streams').constants.CHANNEL_EXTENDED_DATATYPE.STDERR;
|
||||
|
||||
var PACKET_SIZE = 32 * 1024;
|
||||
var MAX_WINDOW = 1 * 1024 * 1024;
|
||||
var WINDOW_THRESHOLD = MAX_WINDOW / 2;
|
||||
var CUSTOM_EVENTS = [
|
||||
'CHANNEL_EOF',
|
||||
'CHANNEL_CLOSE',
|
||||
'CHANNEL_DATA',
|
||||
'CHANNEL_EXTENDED_DATA',
|
||||
'CHANNEL_WINDOW_ADJUST',
|
||||
'CHANNEL_SUCCESS',
|
||||
'CHANNEL_FAILURE',
|
||||
'CHANNEL_REQUEST'
|
||||
];
|
||||
var CUSTOM_EVENTS_LEN = CUSTOM_EVENTS.length;
|
||||
|
||||
function Channel(info, client, opts) {
|
||||
var streamOpts = {
|
||||
highWaterMark: MAX_WINDOW,
|
||||
allowHalfOpen: (!opts || (opts && opts.allowHalfOpen !== false))
|
||||
};
|
||||
|
||||
this.allowHalfOpen = streamOpts.allowHalfOpen;
|
||||
|
||||
DuplexStream.call(this, streamOpts);
|
||||
|
||||
var self = this;
|
||||
var server = opts && opts.server;
|
||||
|
||||
this.server = server;
|
||||
this.type = info.type;
|
||||
this.subtype = undefined;
|
||||
/*
|
||||
incoming and outgoing contain these properties:
|
||||
{
|
||||
id: undefined,
|
||||
window: undefined,
|
||||
packetSize: undefined,
|
||||
state: 'closed'
|
||||
}
|
||||
*/
|
||||
var incoming = this.incoming = info.incoming;
|
||||
var incomingId = incoming.id;
|
||||
var outgoing = this.outgoing = info.outgoing;
|
||||
var callbacks = this._callbacks = [];
|
||||
var exitCode;
|
||||
var exitSignal;
|
||||
var exitDump;
|
||||
var exitDesc;
|
||||
var exitLang;
|
||||
|
||||
this._client = client;
|
||||
this._hasX11 = false;
|
||||
|
||||
var channels = client._channels;
|
||||
var sshstream = client._sshstream;
|
||||
|
||||
function ondrain() {
|
||||
if (self._waitClientDrain) {
|
||||
self._waitClientDrain = false;
|
||||
if (!self._waitWindow) {
|
||||
if (self._chunk)
|
||||
self._write(self._chunk, null, self._chunkcb);
|
||||
else if (self._chunkcb)
|
||||
self._chunkcb();
|
||||
else if (self._chunkErr)
|
||||
self.stderr._write(self._chunkErr, null, self._chunkcbErr);
|
||||
else if (self._chunkcbErr)
|
||||
self._chunkcbErr();
|
||||
}
|
||||
}
|
||||
}
|
||||
client._sock.on('drain', ondrain);
|
||||
|
||||
sshstream.once('CHANNEL_EOF:' + incomingId, function() {
|
||||
if (incoming.state === 'closed' || incoming.state === 'eof')
|
||||
return;
|
||||
incoming.state = 'eof';
|
||||
|
||||
if (self.readable)
|
||||
self.push(null);
|
||||
if (!server && self.stderr.readable)
|
||||
self.stderr.push(null);
|
||||
}).once('CHANNEL_CLOSE:' + incomingId, function() {
|
||||
if (incoming.state === 'closed')
|
||||
return;
|
||||
incoming.state = 'closed';
|
||||
|
||||
if (self.readable)
|
||||
self.push(null);
|
||||
if (server && self.stderr.writable)
|
||||
self.stderr.end();
|
||||
else if (!server && self.stderr.readable)
|
||||
self.stderr.push(null);
|
||||
|
||||
if (outgoing.state === 'open' || outgoing.state === 'eof')
|
||||
self.close();
|
||||
if (outgoing.state === 'closing')
|
||||
outgoing.state = 'closed';
|
||||
|
||||
delete channels[incomingId];
|
||||
|
||||
var state = self._writableState;
|
||||
client._sock.removeListener('drain', ondrain);
|
||||
if (!state.ending && !state.finished)
|
||||
self.end();
|
||||
|
||||
// Take care of any outstanding channel requests
|
||||
self._callbacks = [];
|
||||
for (var i = 0; i < callbacks.length; ++i)
|
||||
callbacks[i](true);
|
||||
callbacks = self._callbacks;
|
||||
|
||||
if (!server) {
|
||||
// align more with node child processes, where the close event gets the
|
||||
// same arguments as the exit event
|
||||
if (!self.readable) {
|
||||
if (exitCode === null) {
|
||||
self.emit('close', exitCode, exitSignal, exitDump, exitDesc,
|
||||
exitLang);
|
||||
} else
|
||||
self.emit('close', exitCode);
|
||||
} else {
|
||||
self.once('end', function() {
|
||||
if (exitCode === null) {
|
||||
self.emit('close', exitCode, exitSignal, exitDump, exitDesc,
|
||||
exitLang);
|
||||
} else
|
||||
self.emit('close', exitCode);
|
||||
});
|
||||
}
|
||||
|
||||
if (!self.stderr.readable)
|
||||
self.stderr.emit('close');
|
||||
else {
|
||||
self.stderr.once('end', function() {
|
||||
self.stderr.emit('close');
|
||||
});
|
||||
}
|
||||
} else { // Server mode
|
||||
if (!self.readable)
|
||||
self.emit('close');
|
||||
else {
|
||||
self.once('end', function() {
|
||||
self.emit('close');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < CUSTOM_EVENTS_LEN; ++i)
|
||||
sshstream.removeAllListeners(CUSTOM_EVENTS[i] + ':' + incomingId);
|
||||
}).on('CHANNEL_DATA:' + incomingId, function(data) {
|
||||
// the remote party should not be sending us data if there is no window
|
||||
// space available ...
|
||||
if (incoming.window === 0)
|
||||
return;
|
||||
|
||||
incoming.window -= data.length;
|
||||
|
||||
if (!self.push(data)) {
|
||||
self._waitChanDrain = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (incoming.window <= WINDOW_THRESHOLD)
|
||||
windowAdjust(self);
|
||||
}).on('CHANNEL_WINDOW_ADJUST:' + incomingId, function(amt) {
|
||||
// the server is allowing us to send `amt` more bytes of data
|
||||
outgoing.window += amt;
|
||||
|
||||
if (self._waitWindow) {
|
||||
self._waitWindow = false;
|
||||
if (!self._waitClientDrain) {
|
||||
if (self._chunk)
|
||||
self._write(self._chunk, null, self._chunkcb);
|
||||
else if (self._chunkcb)
|
||||
self._chunkcb();
|
||||
else if (self._chunkErr)
|
||||
self.stderr._write(self._chunkErr, null, self._chunkcbErr);
|
||||
else if (self._chunkcbErr)
|
||||
self._chunkcbErr();
|
||||
}
|
||||
}
|
||||
}).on('CHANNEL_SUCCESS:' + incomingId, function() {
|
||||
if (server) {
|
||||
sshstream._kalast = Date.now();
|
||||
sshstream._kacnt = 0;
|
||||
} else
|
||||
client._resetKA();
|
||||
if (callbacks.length)
|
||||
callbacks.shift()(false);
|
||||
}).on('CHANNEL_FAILURE:' + incomingId, function() {
|
||||
if (server) {
|
||||
sshstream._kalast = Date.now();
|
||||
sshstream._kacnt = 0;
|
||||
} else
|
||||
client._resetKA();
|
||||
if (callbacks.length)
|
||||
callbacks.shift()(true);
|
||||
}).on('CHANNEL_REQUEST:' + incomingId, function(info) {
|
||||
if (!server) {
|
||||
if (info.request === 'exit-status') {
|
||||
self.emit('exit', exitCode = info.code);
|
||||
return;
|
||||
} else if (info.request === 'exit-signal') {
|
||||
self.emit('exit',
|
||||
exitCode = null,
|
||||
exitSignal = 'SIG' + info.signal,
|
||||
exitDump = info.coredump,
|
||||
exitDesc = info.description,
|
||||
exitLang = info.lang);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// keepalive request? OpenSSH will send one as a channel request if there
|
||||
// is a channel open
|
||||
|
||||
if (info.wantReply)
|
||||
sshstream.channelFailure(outgoing.id);
|
||||
});
|
||||
|
||||
this.stdin = this.stdout = this;
|
||||
|
||||
if (server)
|
||||
this.stderr = new ServerStderr(this);
|
||||
else {
|
||||
this.stderr = new ReadableStream(streamOpts);
|
||||
this.stderr._read = function(n) {
|
||||
if (self._waitChanDrain) {
|
||||
self._waitChanDrain = false;
|
||||
if (incoming.window <= WINDOW_THRESHOLD)
|
||||
windowAdjust(self);
|
||||
}
|
||||
};
|
||||
|
||||
sshstream.on('CHANNEL_EXTENDED_DATA:' + incomingId,
|
||||
function(type, data) {
|
||||
// the remote party should not be sending us data if there is no window
|
||||
// space available ...
|
||||
if (incoming.window === 0)
|
||||
return;
|
||||
|
||||
incoming.window -= data.length;
|
||||
|
||||
if (!self.stderr.push(data)) {
|
||||
self._waitChanDrain = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (incoming.window <= WINDOW_THRESHOLD)
|
||||
windowAdjust(self);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// outgoing data
|
||||
this._waitClientDrain = false; // Client stream-level backpressure
|
||||
this._waitWindow = false; // SSH-level backpressure
|
||||
|
||||
// incoming data
|
||||
this._waitChanDrain = false; // Channel Readable side backpressure
|
||||
|
||||
this._chunk = undefined;
|
||||
this._chunkcb = undefined;
|
||||
this._chunkErr = undefined;
|
||||
this._chunkcbErr = undefined;
|
||||
|
||||
function onFinish() {
|
||||
self.eof();
|
||||
if (server || (!server && !self.allowHalfOpen))
|
||||
self.close();
|
||||
self.writable = false;
|
||||
}
|
||||
this.on('finish', onFinish)
|
||||
.on('prefinish', onFinish); // for node v0.11+
|
||||
function onEnd() {
|
||||
self.readable = false;
|
||||
}
|
||||
this.on('end', onEnd)
|
||||
.on('close', onEnd);
|
||||
}
|
||||
inherits(Channel, DuplexStream);
|
||||
|
||||
Channel.prototype.eof = function() {
|
||||
var ret = true;
|
||||
var outgoing = this.outgoing;
|
||||
|
||||
if (outgoing.state === 'open') {
|
||||
outgoing.state = 'eof';
|
||||
ret = this._client._sshstream.channelEOF(outgoing.id);
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
Channel.prototype.close = function() {
|
||||
var ret = true;
|
||||
var outgoing = this.outgoing;
|
||||
|
||||
if (outgoing.state === 'open' || outgoing.state === 'eof') {
|
||||
outgoing.state = 'closing';
|
||||
ret = this._client._sshstream.channelClose(outgoing.id);
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
Channel.prototype._read = function(n) {
|
||||
if (this._waitChanDrain) {
|
||||
this._waitChanDrain = false;
|
||||
if (this.incoming.window <= WINDOW_THRESHOLD)
|
||||
windowAdjust(this);
|
||||
}
|
||||
};
|
||||
|
||||
Channel.prototype._write = function(data, encoding, cb) {
|
||||
var sshstream = this._client._sshstream;
|
||||
var outgoing = this.outgoing;
|
||||
var packetSize = outgoing.packetSize;
|
||||
var id = outgoing.id;
|
||||
var window = outgoing.window;
|
||||
var len = data.length;
|
||||
var p = 0;
|
||||
var ret;
|
||||
var buf;
|
||||
var sliceLen;
|
||||
|
||||
if (outgoing.state !== 'open')
|
||||
return;
|
||||
|
||||
while (len - p > 0 && window > 0) {
|
||||
sliceLen = len - p;
|
||||
if (sliceLen > window)
|
||||
sliceLen = window;
|
||||
if (sliceLen > packetSize)
|
||||
sliceLen = packetSize;
|
||||
|
||||
ret = sshstream.channelData(id, data.slice(p, p + sliceLen));
|
||||
|
||||
p += sliceLen;
|
||||
window -= sliceLen;
|
||||
|
||||
if (!ret) {
|
||||
this._waitClientDrain = true;
|
||||
this._chunk = undefined;
|
||||
this._chunkcb = cb;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
outgoing.window = window;
|
||||
|
||||
if (len - p > 0) {
|
||||
if (window === 0)
|
||||
this._waitWindow = true;
|
||||
if (p > 0) {
|
||||
// partial
|
||||
buf = new Buffer(len - p);
|
||||
data.copy(buf, 0, p);
|
||||
this._chunk = buf;
|
||||
} else
|
||||
this._chunk = data;
|
||||
this._chunkcb = cb;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._waitClientDrain)
|
||||
cb();
|
||||
};
|
||||
|
||||
Channel.prototype.destroy = function() {
|
||||
this.end();
|
||||
};
|
||||
|
||||
// session type-specific methods
|
||||
Channel.prototype.setWindow = function(rows, cols, height, width) {
|
||||
if (this.server)
|
||||
throw new Error('Client-only method called in server mode');
|
||||
|
||||
if (this.type === 'session'
|
||||
&& (this.subtype === 'shell' || this.subtype === 'exec')
|
||||
&& this.writable
|
||||
&& this.outgoing.state === 'open') {
|
||||
return this._client._sshstream.windowChange(this.outgoing.id,
|
||||
rows,
|
||||
cols,
|
||||
height,
|
||||
width);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
Channel.prototype.signal = function(signalName) {
|
||||
if (this.server)
|
||||
throw new Error('Client-only method called in server mode');
|
||||
|
||||
if (this.type === 'session'
|
||||
&& this.writable
|
||||
&& this.outgoing.state === 'open')
|
||||
return this._client._sshstream.signal(this.outgoing.id, signalName);
|
||||
|
||||
return true;
|
||||
};
|
||||
Channel.prototype.exit = function(name, coreDumped, msg) {
|
||||
if (!this.server)
|
||||
throw new Error('Server-only method called in client mode');
|
||||
|
||||
if (this.type === 'session'
|
||||
&& this.writable
|
||||
&& this.outgoing.state === 'open') {
|
||||
if (typeof name === 'number')
|
||||
return this._client._sshstream.exitStatus(this.outgoing.id, name);
|
||||
else {
|
||||
return this._client._sshstream.exitSignal(this.outgoing.id,
|
||||
name,
|
||||
coreDumped,
|
||||
msg);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
Channel.MAX_WINDOW = MAX_WINDOW;
|
||||
Channel.PACKET_SIZE = PACKET_SIZE;
|
||||
|
||||
function windowAdjust(self) {
|
||||
if (self.outgoing.state !== 'open')
|
||||
return true;
|
||||
var amt = MAX_WINDOW - self.incoming.window;
|
||||
if (amt <= 0)
|
||||
return true;
|
||||
self.incoming.window += amt;
|
||||
return self._client._sshstream.channelWindowAdjust(self.outgoing.id, amt);
|
||||
}
|
||||
|
||||
function ServerStderr(channel) {
|
||||
WritableStream.call(this, { highWaterMark: MAX_WINDOW });
|
||||
this._channel = channel;
|
||||
}
|
||||
inherits(ServerStderr, WritableStream);
|
||||
|
||||
ServerStderr.prototype._write = function(data, encoding, cb) {
|
||||
var channel = this._channel;
|
||||
var sshstream = channel._client._sshstream;
|
||||
var outgoing = channel.outgoing;
|
||||
var packetSize = outgoing.packetSize;
|
||||
var id = outgoing.id;
|
||||
var window = outgoing.window;
|
||||
var len = data.length;
|
||||
var p = 0;
|
||||
var ret;
|
||||
var buf;
|
||||
var sliceLen;
|
||||
|
||||
if (channel.outgoing.state !== 'open')
|
||||
return;
|
||||
|
||||
while (len - p > 0 && window > 0) {
|
||||
sliceLen = len - p;
|
||||
if (sliceLen > window)
|
||||
sliceLen = window;
|
||||
if (sliceLen > packetSize)
|
||||
sliceLen = packetSize;
|
||||
|
||||
ret = sshstream.channelExtData(id, data.slice(p, p + sliceLen), STDERR);
|
||||
|
||||
p += sliceLen;
|
||||
window -= sliceLen;
|
||||
|
||||
if (!ret) {
|
||||
channel._waitClientDrain = true;
|
||||
channel._chunkErr = undefined;
|
||||
channel._chunkcbErr = cb;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
outgoing.window = window;
|
||||
|
||||
if (len - p > 0) {
|
||||
if (window === 0)
|
||||
channel._waitWindow = true;
|
||||
if (p > 0) {
|
||||
// partial
|
||||
buf = new Buffer(len - p);
|
||||
data.copy(buf, 0, p);
|
||||
channel._chunkErr = buf;
|
||||
} else
|
||||
channel._chunkErr = data;
|
||||
channel._chunkcbErr = cb;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!channel._waitClientDrain)
|
||||
cb();
|
||||
};
|
||||
|
||||
module.exports = Channel;
|
||||
145
build/node_modules/ssh2/lib/SFTPWrapper.js
generated
vendored
Normal file
145
build/node_modules/ssh2/lib/SFTPWrapper.js
generated
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
// This wrapper class is used to retain backwards compatibility with
|
||||
// pre-v0.4 ssh2. If it weren't for `read()` and `write()` being used by the
|
||||
// streams2/3 API, we could just pass the SFTPStream directly to the end user...
|
||||
|
||||
var inherits = require('util').inherits,
|
||||
EventEmitter = require('events').EventEmitter;
|
||||
|
||||
function SFTPWrapper(stream) {
|
||||
var self = this;
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
this._stream = stream;
|
||||
|
||||
stream.on('error', function(err) {
|
||||
self.emit('error', err);
|
||||
}).on('end', function() {
|
||||
self.emit('end');
|
||||
}).on('close', function() {
|
||||
self.emit('close');
|
||||
}).on('continue', function() {
|
||||
self.emit('continue');
|
||||
});
|
||||
}
|
||||
inherits(SFTPWrapper, EventEmitter);
|
||||
|
||||
// stream-related methods to pass on
|
||||
SFTPWrapper.prototype.end = function() {
|
||||
return this._stream.end();
|
||||
};
|
||||
// SFTPStream client methods
|
||||
SFTPWrapper.prototype.createReadStream = function(path, options) {
|
||||
return this._stream.createReadStream(path, options);
|
||||
};
|
||||
SFTPWrapper.prototype.createWriteStream = function(path, options) {
|
||||
return this._stream.createWriteStream(path, options);
|
||||
};
|
||||
SFTPWrapper.prototype.open = function(path, flags, attrs, cb) {
|
||||
return this._stream.open(path, flags, attrs, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.close = function(handle, cb) {
|
||||
return this._stream.close(handle, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.read = function(handle, buf, off, len, position, cb) {
|
||||
return this._stream.readData(handle, buf, off, len, position, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.write = function(handle, buf, off, len, position, cb) {
|
||||
return this._stream.writeData(handle, buf, off, len, position, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.fastGet = function(remotePath, localPath, opts, cb) {
|
||||
return this._stream.fastGet(remotePath, localPath, opts, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.fastPut = function(localPath, remotePath, opts, cb) {
|
||||
return this._stream.fastPut(localPath, remotePath, opts, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.readFile = function(path, options, callback_) {
|
||||
return this._stream.readFile(path, options, callback_);
|
||||
};
|
||||
SFTPWrapper.prototype.writeFile = function(path, data, options, callback_) {
|
||||
return this._stream.writeFile(path, data, options, callback_);
|
||||
};
|
||||
SFTPWrapper.prototype.appendFile = function(path, data, options, callback_) {
|
||||
return this._stream.appendFile(path, data, options, callback_);
|
||||
};
|
||||
SFTPWrapper.prototype.exists = function(path, cb) {
|
||||
return this._stream.exists(path, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.unlink = function(filename, cb) {
|
||||
return this._stream.unlink(filename, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.rename = function(oldPath, newPath, cb) {
|
||||
return this._stream.rename(oldPath, newPath, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.mkdir = function(path, attrs, cb) {
|
||||
return this._stream.mkdir(path, attrs, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.rmdir = function(path, cb) {
|
||||
return this._stream.rmdir(path, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.readdir = function(where, opts, cb) {
|
||||
return this._stream.readdir(where, opts, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.fstat = function(handle, cb) {
|
||||
return this._stream.fstat(handle, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.stat = function(path, cb) {
|
||||
return this._stream.stat(path, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.lstat = function(path, cb) {
|
||||
return this._stream.lstat(path, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.opendir = function(path, cb) {
|
||||
return this._stream.opendir(path, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.setstat = function(path, attrs, cb) {
|
||||
return this._stream.setstat(path, attrs, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.fsetstat = function(handle, attrs, cb) {
|
||||
return this._stream.fsetstat(handle, attrs, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.futimes = function(handle, atime, mtime, cb) {
|
||||
return this._stream.futimes(handle, atime, mtime, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.utimes = function(path, atime, mtime, cb) {
|
||||
return this._stream.utimes(path, atime, mtime, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.fchown = function(handle, uid, gid, cb) {
|
||||
return this._stream.fchown(handle, uid, gid, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.chown = function(path, uid, gid, cb) {
|
||||
return this._stream.chown(path, uid, gid, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.fchmod = function(handle, mode, cb) {
|
||||
return this._stream.fchmod(handle, mode, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.chmod = function(path, mode, cb) {
|
||||
return this._stream.chmod(path, mode, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.readlink = function(path, cb) {
|
||||
return this._stream.readlink(path, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.symlink = function(targetPath, linkPath, cb) {
|
||||
return this._stream.symlink(targetPath, linkPath, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.realpath = function(path, cb) {
|
||||
return this._stream.realpath(path, cb);
|
||||
};
|
||||
// extended requests
|
||||
SFTPWrapper.prototype.ext_openssh_rename = function(oldPath, newPath, cb) {
|
||||
return this._stream.ext_openssh_rename(oldPath, newPath, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.ext_openssh_statvfs = function(path, cb) {
|
||||
return this._stream.ext_openssh_statvfs(path, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.ext_openssh_fstatvfs = function(handle, cb) {
|
||||
return this._stream.ext_openssh_fstatvfs(handle, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.ext_openssh_hardlink = function(oldPath, newPath, cb) {
|
||||
return this._stream.ext_openssh_hardlink(oldPath, newPath, cb);
|
||||
};
|
||||
SFTPWrapper.prototype.ext_openssh_fsync = function(handle, cb) {
|
||||
return this._stream.ext_openssh_fsync(handle, cb);
|
||||
};
|
||||
|
||||
module.exports = SFTPWrapper;
|
||||
412
build/node_modules/ssh2/lib/agent.js
generated
vendored
Normal file
412
build/node_modules/ssh2/lib/agent.js
generated
vendored
Normal file
@@ -0,0 +1,412 @@
|
||||
var Socket = require('net').Socket;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var inherits = require('util').inherits;
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var cp = require('child_process');
|
||||
|
||||
var REQUEST_IDENTITIES = 11;
|
||||
var IDENTITIES_ANSWER = 12;
|
||||
var SIGN_REQUEST = 13;
|
||||
var SIGN_RESPONSE = 14;
|
||||
var FAILURE = 5;
|
||||
|
||||
var RE_CYGWIN_SOCK = /^\!<socket >(\d+) s ([A-Z0-9]{8}\-[A-Z0-9]{8}\-[A-Z0-9]{8}\-[A-Z0-9]{8})/;
|
||||
|
||||
module.exports = function(sockPath, key, keyType, data, cb) {
|
||||
var sock;
|
||||
var error;
|
||||
var sig;
|
||||
var datalen;
|
||||
var keylen = 0;
|
||||
var isSigning = Buffer.isBuffer(key);
|
||||
var type;
|
||||
var count = 0;
|
||||
var siglen = 0;
|
||||
var nkeys = 0;
|
||||
var keys;
|
||||
var comlen = 0;
|
||||
var comment = false;
|
||||
var accept;
|
||||
var reject;
|
||||
|
||||
if (typeof key === 'function' && typeof keyType === 'function') {
|
||||
// agent forwarding
|
||||
accept = key;
|
||||
reject = keyType;
|
||||
} else if (isSigning) {
|
||||
keylen = key.length;
|
||||
datalen = data.length;
|
||||
} else {
|
||||
cb = key;
|
||||
key = undefined;
|
||||
}
|
||||
|
||||
function onconnect() {
|
||||
var buf;
|
||||
if (isSigning) {
|
||||
/*
|
||||
byte SSH2_AGENTC_SIGN_REQUEST
|
||||
string key_blob
|
||||
string data
|
||||
uint32 flags
|
||||
*/
|
||||
var p = 9;
|
||||
buf = new Buffer(4 + 1 + 4 + keylen + 4 + datalen + 4);
|
||||
buf.writeUInt32BE(buf.length - 4, 0, true);
|
||||
buf[4] = SIGN_REQUEST;
|
||||
buf.writeUInt32BE(keylen, 5, true);
|
||||
key.copy(buf, p);
|
||||
buf.writeUInt32BE(datalen, p += keylen, true);
|
||||
data.copy(buf, p += 4);
|
||||
buf.writeUInt32BE(0, p += datalen, true);
|
||||
sock.write(buf);
|
||||
} else {
|
||||
/*
|
||||
byte SSH2_AGENTC_REQUEST_IDENTITIES
|
||||
*/
|
||||
sock.write(new Buffer([0, 0, 0, 1, REQUEST_IDENTITIES]));
|
||||
}
|
||||
}
|
||||
function ondata(chunk) {
|
||||
for (var i = 0, len = chunk.length; i < len; ++i) {
|
||||
if (type === undefined) {
|
||||
// skip over packet length
|
||||
if (++count === 5) {
|
||||
type = chunk[i];
|
||||
count = 0;
|
||||
}
|
||||
} else if (type === SIGN_RESPONSE) {
|
||||
/*
|
||||
byte SSH2_AGENT_SIGN_RESPONSE
|
||||
string signature_blob
|
||||
*/
|
||||
if (!sig) {
|
||||
siglen <<= 8;
|
||||
siglen += chunk[i];
|
||||
if (++count === 4) {
|
||||
sig = new Buffer(siglen);
|
||||
count = 0;
|
||||
}
|
||||
} else {
|
||||
sig[count] = chunk[i];
|
||||
if (++count === siglen) {
|
||||
sock.removeAllListeners('data');
|
||||
return sock.destroy();
|
||||
}
|
||||
}
|
||||
} else if (type === IDENTITIES_ANSWER) {
|
||||
/*
|
||||
byte SSH2_AGENT_IDENTITIES_ANSWER
|
||||
uint32 num_keys
|
||||
|
||||
Followed by zero or more consecutive keys, encoded as:
|
||||
|
||||
string public key blob
|
||||
string public key comment
|
||||
*/
|
||||
if (keys === undefined) {
|
||||
nkeys <<= 8;
|
||||
nkeys += chunk[i];
|
||||
if (++count === 4) {
|
||||
keys = new Array(nkeys);
|
||||
count = 0;
|
||||
if (nkeys === 0) {
|
||||
sock.removeAllListeners('data');
|
||||
return sock.destroy();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!key) {
|
||||
keylen <<= 8;
|
||||
keylen += chunk[i];
|
||||
if (++count === 4) {
|
||||
key = new Buffer(keylen);
|
||||
count = 0;
|
||||
}
|
||||
} else if (comment === false) {
|
||||
key[count] = chunk[i];
|
||||
if (++count === keylen) {
|
||||
keys[nkeys - 1] = key;
|
||||
keylen = 0;
|
||||
count = 0;
|
||||
comment = true;
|
||||
if (--nkeys === 0) {
|
||||
key = undefined;
|
||||
sock.removeAllListeners('data');
|
||||
return sock.destroy();
|
||||
}
|
||||
}
|
||||
} else if (comment === true) {
|
||||
comlen <<= 8;
|
||||
comlen += chunk[i];
|
||||
if (++count === 4) {
|
||||
count = 0;
|
||||
if (comlen > 0)
|
||||
comment = comlen;
|
||||
else {
|
||||
key = undefined;
|
||||
comment = false;
|
||||
}
|
||||
comlen = 0;
|
||||
}
|
||||
} else {
|
||||
// skip comments
|
||||
if (++count === comment) {
|
||||
comment = false;
|
||||
count = 0;
|
||||
key = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (type === FAILURE) {
|
||||
if (isSigning)
|
||||
error = new Error('Agent unable to sign data');
|
||||
else
|
||||
error = new Error('Unable to retrieve list of keys from agent');
|
||||
sock.removeAllListeners('data');
|
||||
return sock.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
function onerror(err) {
|
||||
error = err;
|
||||
}
|
||||
function onclose() {
|
||||
if (error)
|
||||
cb(error);
|
||||
else if ((isSigning && !sig) || (!isSigning && !keys))
|
||||
cb(new Error('Unexpected disconnection from agent'));
|
||||
else if (isSigning && sig)
|
||||
cb(undefined, sig);
|
||||
else if (!isSigning && keys)
|
||||
cb(undefined, keys);
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
if (sockPath === 'pageant') {
|
||||
// Pageant (PuTTY authentication agent)
|
||||
sock = new PageantSock();
|
||||
} else {
|
||||
// cygwin ssh-agent instance
|
||||
var triedCygpath = false;
|
||||
fs.readFile(sockPath, function readCygsocket(err, data) {
|
||||
if (err) {
|
||||
if (triedCygpath)
|
||||
return cb(new Error('Invalid cygwin unix socket path'));
|
||||
// try using `cygpath` to convert a possible *nix-style path to the
|
||||
// real Windows path before giving up ...
|
||||
cp.exec('cygpath -w "' + sockPath + '"',
|
||||
function(err, stdout, stderr) {
|
||||
if (err || stdout.length === 0)
|
||||
return cb(new Error('Invalid cygwin unix socket path'));
|
||||
triedCygpath = true;
|
||||
sockPath = stdout.toString().replace(/[\r\n]/g, '');
|
||||
fs.readFile(sockPath, readCygsocket);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var m;
|
||||
if (m = RE_CYGWIN_SOCK.exec(data.toString('ascii'))) {
|
||||
var port;
|
||||
var secret;
|
||||
var secretbuf;
|
||||
var state;
|
||||
var bc = 0;
|
||||
var isRetrying = false;
|
||||
var inbuf = [];
|
||||
var credsbuf = new Buffer(12);
|
||||
var i;
|
||||
var j;
|
||||
|
||||
// use 0 for pid, uid, and gid to ensure we get an error and also
|
||||
// a valid uid and gid from cygwin so that we don't have to figure it
|
||||
// out ourselves
|
||||
credsbuf.fill(0);
|
||||
|
||||
// parse cygwin unix socket file contents
|
||||
port = parseInt(m[1], 10);
|
||||
secret = m[2].replace(/\-/g, '');
|
||||
secretbuf = new Buffer(16);
|
||||
for (i = 0, j = 0; j < 32; ++i,j+=2)
|
||||
secretbuf[i] = parseInt(secret.substring(j, j + 2), 16);
|
||||
|
||||
// convert to host order (always LE for Windows)
|
||||
for (i = 0; i < 16; i += 4)
|
||||
secretbuf.writeUInt32LE(secretbuf.readUInt32BE(i, true), i, true);
|
||||
|
||||
function _onconnect() {
|
||||
bc = 0;
|
||||
state = 'secret';
|
||||
sock.write(secretbuf);
|
||||
}
|
||||
function _ondata(data) {
|
||||
bc += data.length;
|
||||
if (state === 'secret') {
|
||||
// the secret we sent is echoed back to us by cygwin, not sure of
|
||||
// the reason for that, but we ignore it nonetheless ...
|
||||
if (bc === 16) {
|
||||
bc = 0;
|
||||
state = 'creds';
|
||||
sock.write(credsbuf);
|
||||
}
|
||||
} else if (state === 'creds') {
|
||||
// if this is the first attempt, make sure to gather the valid
|
||||
// uid and gid for our next attempt
|
||||
if (!isRetrying)
|
||||
inbuf.push(data);
|
||||
|
||||
if (bc === 12) {
|
||||
sock.removeListener('connect', _onconnect);
|
||||
sock.removeListener('data', _ondata);
|
||||
sock.removeListener('close', _onclose);
|
||||
if (isRetrying) {
|
||||
addSockListeners();
|
||||
sock.emit('connect');
|
||||
} else {
|
||||
isRetrying = true;
|
||||
credsbuf = Buffer.concat(inbuf);
|
||||
credsbuf.writeUInt32LE(process.pid, 0, true);
|
||||
sock.destroy();
|
||||
tryConnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function _onclose() {
|
||||
cb(new Error('Problem negotiating cygwin unix socket security'));
|
||||
}
|
||||
function tryConnect() {
|
||||
sock = new Socket();
|
||||
sock.once('connect', _onconnect);
|
||||
sock.on('data', _ondata);
|
||||
sock.once('close', _onclose);
|
||||
sock.connect(port);
|
||||
}
|
||||
tryConnect();
|
||||
} else
|
||||
cb(new Error('Malformed cygwin unix socket file'));
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else
|
||||
sock = new Socket();
|
||||
|
||||
function addSockListeners() {
|
||||
if (!accept && !reject) {
|
||||
sock.once('connect', onconnect);
|
||||
sock.on('data', ondata);
|
||||
sock.once('error', onerror);
|
||||
sock.once('close', onclose);
|
||||
} else {
|
||||
var chan;
|
||||
sock.once('connect', function() {
|
||||
chan = accept();
|
||||
var isDone = false;
|
||||
function onDone() {
|
||||
if (isDone)
|
||||
return;
|
||||
sock.destroy();
|
||||
isDone = true;
|
||||
}
|
||||
chan.once('end', onDone)
|
||||
.once('close', onDone)
|
||||
.on('data', function(data) {
|
||||
sock.write(data);
|
||||
});
|
||||
sock.on('data', function(data) {
|
||||
chan.write(data);
|
||||
});
|
||||
});
|
||||
sock.once('close', function() {
|
||||
if (!chan)
|
||||
reject();
|
||||
});
|
||||
}
|
||||
}
|
||||
addSockListeners();
|
||||
sock.connect(sockPath);
|
||||
};
|
||||
|
||||
|
||||
// win32 only ------------------------------------------------------------------
|
||||
if (process.platform === 'win32') {
|
||||
var RET_ERR_BADARGS = 10;
|
||||
var RET_ERR_UNAVAILABLE = 11;
|
||||
var RET_ERR_NOMAP = 12;
|
||||
var RET_ERR_BINSTDIN = 13;
|
||||
var RET_ERR_BINSTDOUT = 14;
|
||||
var RET_ERR_BADLEN = 15;
|
||||
|
||||
var ERROR = {};
|
||||
var EXEPATH = path.resolve(__dirname, '..', 'util/pagent.exe');
|
||||
ERROR[RET_ERR_BADARGS] = new Error('Invalid pagent.exe arguments');
|
||||
ERROR[RET_ERR_UNAVAILABLE] = new Error('Pageant is not running');
|
||||
ERROR[RET_ERR_NOMAP] = new Error('pagent.exe could not create an mmap');
|
||||
ERROR[RET_ERR_BINSTDIN] = new Error('pagent.exe could not set mode for stdin');
|
||||
ERROR[RET_ERR_BINSTDOUT] = new Error('pagent.exe could not set mode for stdout');
|
||||
ERROR[RET_ERR_BADLEN] = new Error('pagent.exe did not get expected input payload');
|
||||
|
||||
function PageantSock() {
|
||||
this.proc = undefined;
|
||||
this.buffer = null;
|
||||
}
|
||||
inherits(PageantSock, EventEmitter);
|
||||
|
||||
PageantSock.prototype.write = function(buf) {
|
||||
if (this.buffer === null)
|
||||
this.buffer = buf;
|
||||
else {
|
||||
this.buffer = Buffer.concat([this.buffer, buf],
|
||||
this.buffer.length + buf.length);
|
||||
}
|
||||
// Wait for at least all length bytes
|
||||
if (this.buffer.length < 4)
|
||||
return;
|
||||
|
||||
var len = this.buffer.readUInt32BE(0, true);
|
||||
// Make sure we have a full message before querying pageant
|
||||
if ((this.buffer.length - 4) < len)
|
||||
return;
|
||||
|
||||
buf = this.buffer.slice(0, 4 + len);
|
||||
if (this.buffer.length > (4 + len))
|
||||
this.buffer = this.buffer.slice(4 + len);
|
||||
else
|
||||
this.buffer = null;
|
||||
|
||||
var self = this;
|
||||
var proc;
|
||||
var hadError = false;
|
||||
proc = this.proc = cp.spawn(EXEPATH, [ buf.length ]);
|
||||
proc.stdout.on('data', function(data) {
|
||||
self.emit('data', data);
|
||||
});
|
||||
proc.once('error', function(err) {
|
||||
if (!hadError) {
|
||||
hadError = true;
|
||||
self.emit('error', err);
|
||||
}
|
||||
});
|
||||
proc.once('close', function(code) {
|
||||
self.proc = undefined;
|
||||
if (ERROR[code] && !hadError) {
|
||||
hadError = true;
|
||||
self.emit('error', ERROR[code]);
|
||||
}
|
||||
self.emit('close', hadError);
|
||||
});
|
||||
proc.stdin.end(buf);
|
||||
};
|
||||
PageantSock.prototype.end = PageantSock.prototype.destroy = function() {
|
||||
this.buffer = null;
|
||||
if (this.proc) {
|
||||
this.proc.kill();
|
||||
this.proc = undefined;
|
||||
}
|
||||
};
|
||||
PageantSock.prototype.connect = function() {
|
||||
this.emit('connect');
|
||||
};
|
||||
}
|
||||
1540
build/node_modules/ssh2/lib/client.js
generated
vendored
Normal file
1540
build/node_modules/ssh2/lib/client.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
76
build/node_modules/ssh2/lib/keepalivemgr.js
generated
vendored
Normal file
76
build/node_modules/ssh2/lib/keepalivemgr.js
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
var spliceOne = require('./utils').spliceOne;
|
||||
|
||||
function Manager(interval, streamInterval, kaCountMax) {
|
||||
var streams = this._streams = [];
|
||||
this._timer = undefined;
|
||||
this._timerInterval = interval;
|
||||
this._timerfn = function() {
|
||||
var now = Date.now();
|
||||
for (var i = 0, len = streams.length, s, last; i < len; ++i) {
|
||||
s = streams[i];
|
||||
last = s._kalast;
|
||||
if (last && (now - last) >= streamInterval) {
|
||||
if (++s._kacnt > kaCountMax) {
|
||||
var err = new Error('Keepalive timeout');
|
||||
err.level = 'client-timeout';
|
||||
s.emit('error', err);
|
||||
s.disconnect();
|
||||
spliceOne(streams, i);
|
||||
--i;
|
||||
len = streams.length;
|
||||
} else {
|
||||
s._kalast = now;
|
||||
// XXX: if the server ever starts sending real global requests to the
|
||||
// client, we will need to add a dummy callback here to keep the
|
||||
// correct reply order
|
||||
s.ping();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Manager.prototype.start = function() {
|
||||
if (this._timer)
|
||||
this.stop();
|
||||
this._timer = setInterval(this._timerfn, this._timerInterval);
|
||||
};
|
||||
|
||||
Manager.prototype.stop = function() {
|
||||
if (this._timer) {
|
||||
clearInterval(this._timer);
|
||||
this._timer = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
Manager.prototype.add = function(stream) {
|
||||
var streams = this._streams,
|
||||
self = this;
|
||||
|
||||
stream.once('end', function() {
|
||||
self.remove(stream);
|
||||
}).on('packet', resetKA);
|
||||
|
||||
streams[streams.length] = stream;
|
||||
|
||||
resetKA();
|
||||
|
||||
if (!this._timer)
|
||||
this.start();
|
||||
|
||||
function resetKA() {
|
||||
stream._kalast = Date.now();
|
||||
stream._kacnt = 0;
|
||||
}
|
||||
};
|
||||
|
||||
Manager.prototype.remove = function(stream) {
|
||||
var streams = this._streams,
|
||||
index = streams.indexOf(stream);
|
||||
if (index > -1)
|
||||
spliceOne(streams, index);
|
||||
if (!streams.length)
|
||||
this.stop();
|
||||
};
|
||||
|
||||
module.exports = Manager;
|
||||
1157
build/node_modules/ssh2/lib/server.js
generated
vendored
Normal file
1157
build/node_modules/ssh2/lib/server.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5
build/node_modules/ssh2/lib/utils.js
generated
vendored
Normal file
5
build/node_modules/ssh2/lib/utils.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
exports.spliceOne = function(list, index) {
|
||||
for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1)
|
||||
list[i] = list[k];
|
||||
list.pop();
|
||||
};
|
||||
Reference in New Issue
Block a user