Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1028,7 +1028,7 @@ You can find more examples in the `examples` directory of this repository.

* **setNoDelay**([< _boolean_ >noDelay]) - _Client_ - Calls [`setNoDelay()`](https://nodejs.org/docs/latest/api/net.html#socketsetnodelaynodelay) on the underlying socket. Disabling Nagle's algorithm improves latency at the expense of lower throughput.

* **sftp**(< _function_ >callback) - _(void)_ - Starts an SFTP session. `callback` has 2 parameters: < _Error_ >err, < _SFTP_ >sftp. For methods available on `sftp`, see the [`SFTP` client documentation](https://github.com/mscdex/ssh2/blob/master/SFTP.md).
* **sftp**([< _object_ >env, ]< _function_ >callback) - _(void)_ - Starts an SFTP session. `env` is an environment to use when executing `sftp` methods. `callback` has 2 parameters: < _Error_ >err, < _SFTP_ >sftp. For methods available on `sftp`, see the [`SFTP` client documentation](https://github.com/mscdex/ssh2/blob/master/SFTP.md).

* **shell**([[< _mixed_ >window,] < _object_ >options]< _function_ >callback) - _(void)_ - Starts an interactive shell session on the server, with an optional `window` object containing pseudo-tty settings (see 'Pseudo-TTY settings'). If `window === false`, then no pseudo-tty is allocated. `options` supports the `x11` and `env` options as described in `exec()`. `callback` has 2 parameters: < _Error_ >err, < _Channel_ >stream.

Expand Down
47 changes: 41 additions & 6 deletions lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -1552,17 +1552,22 @@ class Client extends EventEmitter {
return this;
}

sftp(cb) {
sftp(env, cb) {
if (!this._sock || !isWritable(this._sock))
throw new Error('Not connected');

if (typeof env === 'function') {
cb = env;
env = undefined;
}

openChannel(this, 'sftp', (err, sftp) => {
if (err) {
cb(err);
return;
}

reqSubsystem(sftp, 'sftp', (err, sftp_) => {
const reqSubsystemCb = (err, sftp_) => {
if (err) {
cb(err);
return;
Expand Down Expand Up @@ -1608,7 +1613,20 @@ class Client extends EventEmitter {
.on('close', onExit);

sftp._init();
});
};

if (typeof env === 'object' && env !== null) {
reqEnv(sftp, env, (err) => {
if (err) {
cb(err);
return;
}

reqSubsystem(sftp, 'sftp', reqSubsystemCb);
});
} else {
reqSubsystem(sftp, 'sftp', reqSubsystemCb);
}
});

return this;
Expand Down Expand Up @@ -1842,16 +1860,33 @@ function reqExec(chan, cmd, opts, cb) {
chan._client._protocol.exec(chan.outgoing.id, cmd, true);
}

function reqEnv(chan, env) {
if (chan.outgoing.state !== 'open')
function reqEnv(chan, env, cb) {
const wantReply = (typeof cb === 'function');

if (chan.outgoing.state !== 'open') {
if (wantReply)
cb(new Error('Channel is not open'));
return;
}

if (wantReply) {
chan._callbacks.push((had_err) => {
if (had_err) {
cb(had_err !== true
? had_err
: new Error('Unable to set environment'));
return;
}
cb();
});
}

const keys = Object.keys(env || {});

for (let i = 0; i < keys.length; ++i) {
const key = keys[i];
const val = env[key];
chan._client._protocol.env(chan.outgoing.id, key, val, false);
chan._client._protocol.env(chan.outgoing.id, key, val, wantReply);
}
}

Expand Down
36 changes: 36 additions & 0 deletions test/test-sftp.js
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,42 @@ setup('WriteStream', mustCall((client, server) => {
}));
}

{
const { client, server } = setup_(
'SFTP client sets environment',
{
client: { username: 'foo', password: 'bar' },
server: { hostKeys: [ fixture('ssh_host_rsa_key') ] },
},
);

const env = { SSH2NODETEST: 'foo' };

server.on('connection', mustCall((conn) => {
conn.on('authentication', mustCall((ctx) => {
ctx.accept();
})).on('ready', mustCall(() => {
conn.on('session', mustCall((accept, reject) => {
accept().on('env', mustCall((accept, reject, info) => {
accept && accept();
assert(info.key === Object.keys(env)[0], 'Wrong env key');
assert(info.val === Object.values(env)[0], 'Wrong env value');
})).on('sftp', mustCall((accept, reject) => {
accept();
}));
}));
}));
}));

client.on('ready', mustCall(() => {
const timeout = setTimeout(mustNotCall(), 1000);
client.sftp(env, mustCall((err, sftp) => {
clearTimeout(timeout);
assert(!err, `Unexpected exec error: ${err}`);
client.end();
}));
}));
}

// =============================================================================
function setup(title, cb) {
Expand Down