Socket
Socket
Sign inDemoInstall

ssh2-sftp-client

Package Overview
Dependencies
18
Maintainers
1
Versions
73
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 5.3.2 to 6.0.0

8

package.json
{
"name": "ssh2-sftp-client",
"version": "5.3.2",
"version": "6.0.0",
"description": "ssh2 sftp client for node",

@@ -25,3 +25,3 @@ "main": "src/index.js",

"concat-stream": "^2.0.0",
"retry": "^0.12.0",
"promise-retry": "^2.0.1",
"ssh2": "^0.8.9"

@@ -35,6 +35,6 @@ },

"dotenv": "^8.2.0",
"mocha": "^8.1.3",
"moment": "^2.29.0",
"mocha": "^8.2.1",
"moment": "^2.29.1",
"through2": "^4.0.2"
}
}

@@ -7,9 +7,10 @@ /**

const Client = require('ssh2').Client;
const {Client} = require('ssh2');
const fs = require('fs');
const concat = require('concat-stream');
const retry = require('retry');
//const retry = require('retry');
const promiseRetry = require('promise-retry');
const {join, parse} = require('path');
const utils = require('./utils');
const {errorCode, targetType} = require('./constants');
const {errorCode} = require('./constants');

@@ -22,5 +23,5 @@ class SftpClient {

this.endCalled = false;
this.errorHandled = false;
this.remotePathSep = '/';
this.remotePlatform = 'unix';
this.errorHandled = false;
this.debug = undefined;

@@ -30,5 +31,23 @@

if (!this.endCalled) {
this.debugMsg('Unexpected close event raised by server');
this.sftp = undefined;
}
});
this.client.on('end', () => {
if (!this.endCalled) {
this.debugMsg('Unexpected end event raised by server');
this.sftp = undefined;
}
});
this.client.on('error', (err) => {
if (!this.errorHandled) {
throw utils.formatError(
`Unexpected error: ${err.message}`,
'global-error-handler',
err.code
);
} else {
this.errorHandled = false;
}
});
}

@@ -78,71 +97,40 @@

*/
connect(config) {
let connectErrorListener, onceReady;
let operation = retry.operation({
retries: config.retries || 1,
factor: config.retry_factor || 2,
minTimeout: config.retry_minTimeout || 1000
sftpConnect(config) {
return new Promise((resolve, reject) => {
let connectReady = () => {
this.client.sftp((err, sftp) => {
if (err) {
this.debugMsg(`SFTP channel error: ${err.message} ${err.code}`);
reject(utils.formatError(err, 'sftpConnect', err.code));
} else {
//this.sftp = sftp;
resolve(sftp);
}
this.client.removeListener('ready', connectReady);
});
};
utils.addTempListeners(this, 'sftpConnect', reject);
this.client.on('ready', connectReady).connect(config);
});
}
const retryConnect = (config, callback) => {
try {
operation.attempt((attemptCount) => {
connectErrorListener = (err) => {
//this.client.removeListener('ready', onceReady);
if (operation.retry(err)) {
// failed to connect, but not yet reached max attempt count
// remove the listeners and try again
this.debugMsg(
`Connection attempt ${attemptCount} failed. Trying again.`
);
return;
}
// exhausted retries - do callback with error
this.debugMsg('Exhausted all connection attempts. Giving up');
callback(
utils.formatError(err, 'connect', err.code, attemptCount),
null
);
};
onceReady = () => {
this.client.sftp((err, sftp) => {
//this.client.removeListener('error', connectErrorListener);
if (err) {
if (operation.retry(err)) {
// failed to connect, but not yet reached max attempt count
// remove the listeners and try again
return;
}
// exhausted retries - do callback with error
callback(
utils.formatError(err, 'connect', err.code, attemptCount),
null
);
return;
}
this.debugMsg('SFTP connection established');
this.sftp = sftp;
callback(null, sftp);
return;
});
};
if (!utils.hasListener(this.client, 'ready', 'onceReady')) {
this.client.on('ready', onceReady);
}
if (
!utils.hasListener(this.client, 'error', 'connectErrorListener')
) {
this.client.on('error', connectErrorListener);
}
this.client.connect(config);
retryConnect(config) {
return promiseRetry(
(retry, attempt) => {
this.debugMsg(`Connect attempt ${attempt}`);
return this.sftpConnect(config).catch((err) => {
utils.removeTempListeners(this.client);
retry(err);
});
} catch (err) {
utils.removeListeners(this.client);
callback(utils.formatError(err, 'connect'), null);
},
{
retries: config.retries || 1,
factor: config.retry_factor || 2,
minTimeout: config.retry_minTimeout || 1000
}
};
);
}
return new Promise((resolve, reject) => {
async connect(config) {
try {
if (config.debug) {

@@ -152,48 +140,17 @@ this.debug = config.debug;

}
if (this.sftp) {
this.debugMsg('Already connected - reject');
reject(
utils.formatError(
'An existing SFTP connection is already defined',
'connect',
errorCode.connect
)
throw utils.formatError(
'An existing SFTP connection is already defined',
'connect',
errorCode.connect
);
} else {
retryConnect(config, (err, sftp) => {
if (err) {
this.debugMsg('Connection failed - reject');
reject(err);
} else {
this.debugMsg('Connected - do stage 2 setup');
sftp.realpath('.', (err, absPath) => {
if (err) {
this.debugMsg('Failed to get remote path');
reject(
utils.formatError(
`Failed to determine remote server type: ${err.message}`,
'connect',
errorCode.generic
)
);
} else {
this.debugMsg(`absPath = ${absPath}`);
if (absPath.match(/^\/[A-Z]:.*/)) {
this.remotePlatform = 'win32';
this.debugMsg('remote platform win32 like');
} else {
this.remotePlatform = 'unix';
this.debugMsg('Remote platform unix like');
}
this.debugMsg('Connection and setup completed successfully');
resolve(sftp);
}
this.client.removeListener('error', connectErrorListener);
});
}
this.client.removeListener('ready', onceReady);
});
}
});
//this.sftp = await this.sftpConnect(config);
this.sftp = await this.retryConnect(config);
this.debugMsg('SFTP Connection established');
utils.removeTempListeners(this.client);
} catch (err) {
throw utils.formatError(err.message, 'connect', err.errorCode);
}
}

@@ -216,6 +173,3 @@

this.debugMsg(`realPath -> ${remotePath}`);
let closeListener = utils.makeCloseListener(this, reject, 'realPath');
this.client.prependListener('close', closeListener);
let errorListener = utils.makeErrorListener(reject, this, 'realPath');
this.client.prependListener('error', errorListener);
utils.addTempListeners(this, 'realPath', reject);
if (utils.haveConnection(this, 'realPath', reject)) {

@@ -239,4 +193,3 @@ this.sftp.realpath(remotePath, (err, absPath) => {

resolve(absPath);
this.removeListener('error', errorListener);
this.removeListener('close', closeListener);
utils.removeTempListeners(this.client);
});

@@ -261,6 +214,3 @@ }

this.debugMsg(`stat -> ${aPath}`);
let closeListener = utils.makeCloseListener(this, reject, 'stat');
this.client.prependListener('close', closeListener);
let errorListener = utils.makeErrorListener(reject, this, 'stat');
this.client.prependListener('error', errorListener);
utils.addTempListeners(this, 'stat', reject);
this.sftp.stat(aPath, (err, stats) => {

@@ -279,3 +229,7 @@ if (err) {

reject(
utils.formatError(`${err.message} ${remotePath}`, '_stat')
utils.formatError(
`${err.message} ${remotePath}`,
'_stat',
err.code
)
);

@@ -301,4 +255,3 @@ }

}
this.removeListener('error', errorListener);
this.removeListener('close', closeListener);
utils.removeTempListeners(this.client);
});

@@ -313,3 +266,3 @@ });

} catch (err) {
throw utils.formatError(err, 'stat');
return utils.handleError(err, 'stat');
}

@@ -360,3 +313,3 @@ }

} catch (err) {
throw utils.formatError(err, 'exists');
return utils.handleError(err, 'exists');
}

@@ -379,15 +332,18 @@ }

*/
async list(remotePath, pattern = /.*/) {
const _list = (aPath, filter) => {
return new Promise((resolve, reject) => {
list(remotePath, pattern = /.*/) {
return new Promise((resolve, reject) => {
if (utils.haveConnection(this, 'list', reject)) {
const reg = /-/gi;
this.debugMsg(`list -> ${aPath} filter -> ${filter}`);
let closeListener = utils.makeCloseListener(this, reject, 'list');
this.client.prependListener('close', closeListener);
let errorListener = utils.makeErrorListener(reject, this, 'list');
this.client.prependListener('error', errorListener);
this.sftp.readdir(aPath, (err, fileList) => {
this.debugMsg(`list -> ${remotePath} filter -> ${pattern}`);
utils.addTempListeners(this, 'list', reject);
this.sftp.readdir(remotePath, (err, fileList) => {
if (err) {
this.debugMsg(`list error ${err.message} code: ${err.code}`);
reject(utils.formatError(`${err.message} ${aPath}`, '_list'));
reject(
utils.formatError(
`${err.message} ${remotePath}`,
'list',
err.code
)
);
} else {

@@ -417,6 +373,6 @@ this.debugMsg('list <- ', fileList);

let regex;
if (filter instanceof RegExp) {
regex = filter;
if (pattern instanceof RegExp) {
regex = pattern;
} else {
let newPattern = filter.replace(/\*([^*])*?/gi, '.*');
let newPattern = pattern.replace(/\*([^*])*?/gi, '.*');
regex = new RegExp(newPattern);

@@ -426,23 +382,6 @@ }

}
this.removeListener('error', errorListener);
this.removeListener('close', closeListener);
utils.removeTempListeners(this.client);
});
});
};
try {
utils.haveConnection(this, 'list');
let pathInfo = await utils.checkRemotePath(
this,
remotePath,
targetType.readDir
);
if (!pathInfo.valid) {
let e = utils.formatError(pathInfo.msg, 'list', pathInfo.code);
throw e;
}
return _list(pathInfo.path, pattern);
} catch (err) {
throw utils.formatError(err, 'list');
}
});
}

@@ -464,18 +403,16 @@

*/
async get(remotePath, dst, options = {}) {
const _get = (sftpPath, localDst, options) => {
return new Promise((resolve, reject) => {
this.debugMsg(`get -> ${sftpPath} `, options);
let closeListener = utils.makeCloseListener(this, reject, 'get');
this.client.prependListener('close', closeListener);
let errorListener = utils.makeErrorListener(reject, this, 'get');
this.client.prependListener('error', errorListener);
let rdr = this.sftp.createReadStream(sftpPath, options);
get(remotePath, dst, options = {}) {
return new Promise((resolve, reject) => {
if (utils.haveConnection(this, 'get', reject)) {
this.debugMsg(`get -> ${remotePath} `, options);
utils.addTempListeners(this, 'get', reject);
let rdr = this.sftp.createReadStream(remotePath, options);
rdr.once('error', (err) => {
utils.removeListeners(rdr);
reject(utils.formatError(`${err.message} ${sftpPath}`, '_get'));
this.removeListener('error', errorListener);
this.removeListener('close', closeListener);
reject(
utils.formatError(`${err.message} ${remotePath}`, 'get', err.code)
);
utils.removeTempListeners(this.client);
});
if (localDst === undefined) {
if (dst === undefined) {
// no dst specified, return buffer of data

@@ -486,4 +423,3 @@ this.debugMsg('get returning buffer of data');

resolve(buff);
this.removeListener('error', errorListener);
this.removeListener('close', closeListener);
utils.removeTempListeners(this.client);
});

@@ -493,9 +429,9 @@ rdr.pipe(concatStream);

let wtr;
if (typeof localDst === 'string') {
if (typeof dst === 'string') {
// dst local file path
this.debugMsg('get returning local file');
wtr = fs.createWriteStream(localDst);
wtr = fs.createWriteStream(dst);
} else {
this.debugMsg('get returning data into supplied stream');
wtr = localDst;
wtr = dst;
}

@@ -506,4 +442,5 @@ wtr.once('error', (err) => {

utils.formatError(
`${err.message} ${typeof dst === 'string' ? localDst : ''}`,
'_get'
`${err.message} ${typeof dst === 'string' ? dst : ''}`,
'get',
err.code
)

@@ -514,4 +451,3 @@ );

}
this.removeListener('error', errorListener);
this.removeListener('close', closeListener);
utils.removeTempListeners(this.client);
});

@@ -523,41 +459,13 @@ wtr.once('finish', () => {

}
if (typeof localDst === 'string') {
resolve(localDst);
if (typeof dst === 'string') {
resolve(dst);
} else {
resolve(wtr);
}
this.removeListener('error', errorListener);
this.removeListener('close', closeListener);
utils.removeTempListeners(this.client);
});
rdr.pipe(wtr);
}
});
};
try {
utils.haveConnection(this, 'get');
let pathInfo = await utils.checkRemotePath(
this,
remotePath,
targetType.readFile
);
this.debugMsg('get remote path info ', pathInfo);
if (!pathInfo.valid) {
let e = utils.formatError(pathInfo.msg, 'get', pathInfo.code);
throw e;
}
if (typeof dst === 'string') {
let localInfo = await utils.checkLocalPath(dst, targetType.writeFile);
this.debugMsg('get local path info ', localInfo);
if (localInfo.valid) {
dst = localInfo.path;
} else {
let e = utils.formatError(localInfo.msg, 'get', localInfo.code);
throw e;
}
}
return _get(pathInfo.path, dst, options);
} catch (err) {
throw utils.formatError(err, 'get');
}
});
}

@@ -578,56 +486,36 @@

*/
async fastGet(remotePath, localPath, options) {
const _fastGet = (from, to, opts) => {
return new Promise((resolve, reject) => {
this.debugMsg(`fastGet -> ${from} ${to} `, opts);
let closeListener = utils.makeCloseListener(this, reject, 'fastGet');
this.client.prependListener('close', closeListener);
let errorListener = utils.makeErrorListener(reject, this, 'fastGet');
this.client.prependListener('error', errorListener);
this.sftp.fastGet(from, to, opts, (err) => {
if (err) {
this.debugMsg(`fastGet error ${err.message} code: ${err.code}`);
reject(
utils.formatError(
`${err.message} src: ${from} dst: ${to}`,
'fastGet'
)
fastGet(remotePath, localPath, options) {
return this.exists(remotePath)
.then((ftype) => {
if (ftype !== '-') {
let msg =
ftype === false
? `No such file ${remotePath}`
: `Not a regular file ${remotePath}`;
return Promise.reject(
utils.formatError(msg, 'fastGet', errorCode.badPath)
);
}
})
.then(() => {
return new Promise((resolve, reject) => {
if (utils.haveConnection(this, 'fastGet', reject)) {
this.debugMsg(
`fastGet -> remote: ${remotePath} local: ${localPath} `,
options
);
utils.addTempListeners(this, 'fastGet', reject);
this.sftp.fastGet(remotePath, localPath, options, (err) => {
if (err) {
this.debugMsg(`fastGet error ${err.message} code: ${err.code}`);
reject(utils.formatError(err, 'fastGet'));
}
resolve(
`${remotePath} was successfully download to ${localPath}!`
);
utils.removeTempListeners(this.client);
});
}
resolve(`${from} was successfully download to ${to}!`);
this.removeListener('error', errorListener);
this.removeListener('close', closeListener);
});
});
};
try {
utils.haveConnection(this, 'fastGet');
let pathInfo = await utils.checkRemotePath(
this,
remotePath,
targetType.readFile
);
this.debugMsg('fastGet remote path info ', pathInfo);
if (!pathInfo.valid) {
let e = utils.formatError(pathInfo.msg, 'fastGet', pathInfo.code);
throw e;
}
let localInfo = await utils.checkLocalPath(
localPath,
targetType.writeFile
);
this.debugMsg('fastGet local path info ', localInfo);
if (!localInfo.valid) {
let e = utils.formatError(
localInfo.msg,
'fastGet',
localInfo.code
);
throw e;
}
return _fastGet(pathInfo.path, localInfo.path, options);
} catch (err) {
throw utils.formatError(err, 'fastGet');
}
}

@@ -648,53 +536,72 @@

*/
async fastPut(localPath, remotePath, options) {
const _fastPut = (from, to, opts) => {
return new Promise((resolve, reject) => {
this.debugMsg(`fastPut -> ${localPath} ${remotePath} `, opts);
let closeListener = utils.makeCloseListener(this, reject, 'fastPut');
this.client.prependListener('close', closeListener);
let errorListener = utils.makeErrorListener(reject, this, 'fastPut');
this.client.prependListener('error', errorListener);
this.sftp.fastPut(from, to, opts, (err) => {
if (err) {
this.debugMsg(`fastPut error ${err.message} ${err.code}`);
reject(
utils.formatError(
`${err.message} Local: ${from} Remote: ${to}`,
'_fastPut'
)
fastPut(localPath, remotePath, options) {
this.debugMsg(`fastPut -> local ${localPath} remote ${remotePath}`);
return utils
.localExists(localPath)
.then((localStatus) => {
this.debugMsg(`fastPut <- localStatus ${localStatus}`);
if (localStatus !== '-') {
this.debugMsg('fastPut reject bad source path');
return Promise.reject(
utils.formatError(
`Bad path ${localPath}`,
'fastPut',
errorCode.badPath
)
);
}
return new Promise((resolve, reject) => {
fs.access(localPath, fs.constants.F_OK | fs.constants.R_OK, (err) => {
if (err) {
this.debugMsg('fastPut reject no access source');
reject(
utils.formatError(
`${err.message} ${localPath}`,
'fastPut',
err.code
)
);
} else {
this.debugMsg('fastPut source access ok');
resolve(true);
}
});
});
})
.then(() => {
return new Promise((resolve, reject) => {
if (utils.haveConnection(this, 'fastPut', reject)) {
this.debugMsg(
`fastPut -> local: ${localPath} remote: ${remotePath} opts: ${JSON.stringify(
options
)}`
);
utils.addTempListeners(this, 'fastPut', reject);
this.sftp.fastPut(localPath, remotePath, options, (err) => {
if (err) {
this.debugMsg(`fastPut error ${err.message} ${err.code}`);
reject(
utils.formatError(
`${err.message} Local: ${localPath} Remote: ${remotePath}`,
'fastPut',
err.code
)
);
}
this.debugMsg('fastPut file transferred');
resolve(
`${localPath} was successfully uploaded to ${remotePath}!`
);
});
}
resolve(`${from} was successfully uploaded to ${to}!`);
this.removeListener('error', errorListener);
this.removeListener('close', closeListener);
});
})
.then((msg) => {
utils.removeTempListeners(this.client);
return msg;
});
};
try {
utils.haveConnection(this, 'fastPut');
let localInfo = await utils.checkLocalPath(localPath);
this.debugMsg('fastPut local path info ', localInfo);
if (!localInfo.valid) {
let e = utils.formatError(localInfo.msg, 'fastPut', localInfo.code);
throw e;
}
let pathInfo = await utils.checkRemotePath(
this,
remotePath,
targetType.writeFile
);
this.debugMsg('fastPut remote path info ', pathInfo);
if (!pathInfo.valid) {
let e = utils.formatError(pathInfo.msg, 'fastPut', pathInfo.code);
throw e;
}
return _fastPut(localInfo.path, pathInfo.path, options);
} catch (err) {
throw utils.formatError(err, 'fastPut');
}
}
/**
* Create file a file on the remote server. The 'src' argument
* Create a file on the remote server. The 'src' argument
* can be a buffer, string or read stream. If 'src' is a string, it

@@ -709,83 +616,101 @@ * should be the path to a local file.

*/
async put(localSrc, remotePath, options = {}) {
const _put = (src, dst, options) => {
return new Promise((resolve, reject) => {
this.debugMsg(`put -> ${dst} `, options);
let closeListener = utils.makeCloseListener(this, reject, 'put');
this.client.prependListener('close', closeListener);
let errorListener = utils.makeErrorListener(reject, this, 'put');
this.client.prependListener('error', errorListener);
let stream = this.sftp.createWriteStream(dst, options);
stream.once('error', (err) => {
reject(utils.formatError(`${err.message} ${dst}`, 'put'));
this.removeListener('error', errorListener);
this.removeListener('close', closeListener);
put(localSrc, remotePath, options = {}) {
this.debugMsg(
`put ${
typeof localSrc === 'string' ? localSrc : '<buffer | stream>'
} -> ${remotePath}`,
options
);
return utils
.localExists(typeof localSrc === 'string' ? localSrc : 'dummy')
.then((localStatus) => {
if (typeof localSrc === 'string' && localStatus !== '-') {
this.debugMsg(`put: file does not exist ${localSrc} - rejecting`);
return Promise.reject(
utils.formatError(`Bad path ${localSrc}`, 'put', errorCode.badPath)
);
}
return new Promise((resolve, reject) => {
if (typeof localSrc === 'string') {
fs.access(
localSrc,
fs.constants.F_OK | fs.constants.R_OK,
(err) => {
if (err) {
this.debugMsg(`put: Cannot read ${localSrc} - rejecting`);
reject(
utils.formatError(
`Permission denied ${localSrc}`,
'put',
errorCode.permission
)
);
} else {
this.debugMsg('put: localSrc file OK');
resolve(true);
}
}
);
} else {
this.debugMsg('put: localSrc buffer or string OK');
resolve(true);
}
});
stream.once('finish', () => {
utils.removeListeners(stream);
if (options.autoClose === false) {
stream.destroy();
})
.then(() => {
return new Promise((resolve, reject) => {
if (utils.haveConnection(this, 'put', reject)) {
utils.addTempListeners(this, 'put', reject);
let stream = this.sftp.createWriteStream(remotePath, options);
stream.once('error', (err) => {
reject(
utils.formatError(
`${err.message} ${remotePath}`,
'put',
err.code
)
);
utils.removeTempListeners(this.client);
});
stream.once('finish', () => {
utils.removeListeners(stream);
if (options.autoClose === false) {
stream.destroy();
}
resolve(`Uploaded data stream to ${remotePath}`);
utils.removeTempListeners(this.client);
});
if (localSrc instanceof Buffer) {
this.debugMsg('put source is a buffer');
stream.end(localSrc);
} else {
let rdr;
if (typeof localSrc === 'string') {
this.debugMsg(`put source is a file path: ${localSrc}`);
rdr = fs.createReadStream(localSrc);
} else {
this.debugMsg('put source is a stream');
rdr = localSrc;
}
rdr.once('error', (err) => {
utils.removeListeners(stream);
reject(
utils.formatError(
`${err.message} ${
typeof localSrc === 'string' ? localSrc : ''
}`,
'put',
err.code
)
);
if (options.autoClose === false) {
stream.destroy();
}
utils.removeTempListeners(this.client);
});
rdr.pipe(stream);
}
}
resolve(`Uploaded data stream to ${dst}`);
this.removeListener('error', errorListener);
this.removeListener('close', closeListener);
});
if (src instanceof Buffer) {
this.debugMsg('put source is a buffer');
stream.end(src);
} else {
let rdr;
if (typeof src === 'string') {
this.debugMsg(`put source is a file path: ${src}`);
rdr = fs.createReadStream(src);
} else {
this.debugMsg('put source is a stream');
rdr = src;
}
rdr.once('error', (err) => {
utils.removeListeners(stream);
reject(
utils.formatError(
`${err.message} ${
typeof localSrc === 'string' ? localSrc : ''
}`,
'put'
)
);
if (options.autoClose === false) {
stream.destroy();
}
this.removeListener('error', errorListener);
this.removeListener('close', closeListener);
});
rdr.pipe(stream);
}
});
};
try {
utils.haveConnection(this, 'put');
if (typeof localSrc === 'string') {
let localInfo = await utils.checkLocalPath(localSrc);
this.debugMsg('put local path info ', localInfo);
if (!localInfo.valid) {
let e = utils.formatError(localInfo.msg, 'put', localInfo.code);
throw e;
}
localSrc = localInfo.path;
}
let pathInfo = await utils.checkRemotePath(
this,
remotePath,
targetType.writeFile
);
this.debugMsg('put remote path info ', pathInfo);
if (!pathInfo.valid) {
let e = utils.formatError(pathInfo.msg, 'put', pathInfo.code);
throw e;
}
return _put(localSrc, pathInfo.path, options);
} catch (err) {
throw utils.formatError(err, 'put');
}
}

@@ -801,73 +726,38 @@

*/
async append(input, remotePath, options) {
const _append = (data, aPath, opts) => {
return new Promise((resolve, reject) => {
this.debugMsg(`append -> ${aPath} `, opts);
let closeListener = utils.makeCloseListener(this, reject, 'append');
this.client.prependListener('close', closeListener);
let errorListener = utils.makeErrorListener(reject, this, 'append');
this.client.prependListener('error', errorListener);
let writerOptions;
if (opts) {
writerOptions = opts;
writerOptions.flags = 'a';
append(input, remotePath, options = {}) {
return new Promise((resolve, reject) => {
if (utils.haveConnection(this, 'append', reject)) {
if (typeof input === 'string') {
reject(
utils.formatError('Cannot append one file to another', 'append')
);
} else {
writerOptions = {
flags: 'a'
};
this.debugMsg(`append -> remote: ${remotePath} `, options);
utils.addTempListeners(this, 'append', reject);
options.flags = 'a';
let stream = this.sftp.createWriteStream(remotePath, options);
stream.once('error', (err) => {
utils.removeListeners(stream);
reject(
utils.formatError(
`${err.message} ${remotePath}`,
'append',
err.code
)
);
utils.removeTempListeners(this.client);
});
stream.once('finish', () => {
utils.removeListeners(stream);
resolve(`Appended data to ${remotePath}`);
utils.removeTempListeners(this.client);
});
if (input instanceof Buffer) {
stream.end(input);
} else {
input.pipe(stream);
}
}
let stream = this.sftp.createWriteStream(aPath, writerOptions);
stream.once('error', (err) => {
utils.removeListeners(stream);
reject(utils.formatError(`${err.message} ${aPath}`, '_append'));
this.removeListener('error', errorListener);
this.removeListener('close', closeListener);
});
stream.once('finish', () => {
utils.removeListeners(stream);
resolve(`Uploaded data stream to ${aPath}`);
this.removeListener('error', errorListener);
this.removeListener('close', closeListener);
});
if (data instanceof Buffer) {
stream.end(data);
} else {
data.pipe(stream);
}
});
};
try {
utils.haveConnection(this, 'append');
if (typeof input === 'string') {
let e = utils.formatError(
'Cannot append one file to another',
'append',
errorCode.badPath
);
throw e;
}
let pathInfo = await utils.checkRemotePath(
this,
remotePath,
targetType.writeFile
);
this.debugMsg('append remote path info ', pathInfo);
if (!pathInfo.valid) {
let e = utils.formatError(pathInfo.msg, 'append', pathInfo.code);
throw e;
}
let stats = await this.stat(pathInfo.path);
if ((stats.mode & 0o0444) === 0) {
let e = utils.formatError(
`Permission denied: ${remotePath}`,
'append',
errorCode.permission
);
throw e;
}
return _append(input, pathInfo.path, options);
} catch (err) {
throw utils.formatError(err, 'append');
}
});
}

@@ -888,6 +778,3 @@

this.debugMsg(`mkdir -> ${p}`);
let closeListener = utils.makeCloseListener(this, reject, 'mkdir');
this.client.prependListener('close', closeListener);
let errorListener = utils.makeErrorListener(reject, this, 'mkdir');
this.client.prependListener('error', errorListener);
utils.addTempListeners(this, 'mkdir', reject);
this.sftp.mkdir(p, (err) => {

@@ -901,4 +788,3 @@ if (err) {

resolve(`${p} directory created`);
this.removeListener('error', errorListener);
this.removeListener('close', closeListener);
utils.removeTempListeners(this.client);
});

@@ -910,26 +796,16 @@ });

utils.haveConnection(this, 'mkdir');
let pathInfo = await utils.checkRemotePath(
this,
remotePath,
targetType.writeDir
);
this.debugMsg('mkdir remote path info ', pathInfo);
if (!pathInfo.valid) {
throw utils.formatError(pathInfo.msg, 'mkdir', pathInfo.code);
}
if (pathInfo.type === 'd') {
return `${pathInfo.path} already exists`;
}
let rPath = await utils.normalizeRemotePath(this, remotePath);
if (!recursive) {
return _mkdir(pathInfo.path);
return _mkdir(rPath);
}
let dir = parse(pathInfo.path).dir;
let parent = await utils.checkRemotePath(this, dir, targetType.writeDir);
this.debugMsg('mkdir parent path info ', parent);
if (parent.valid && !parent.type) {
await this.mkdir(dir, true);
let dir = parse(rPath).dir;
if (dir) {
let dirExists = await this.exists(dir);
if (!dirExists) {
await this.mkdir(dir, true);
}
}
return _mkdir(pathInfo.path);
return _mkdir(rPath);
} catch (err) {
throw utils.formatError(err, 'mkdir');
return utils.handleError(`${err.message} ${remotePath}`, 'mkdir');
}

@@ -952,6 +828,3 @@ }

this.debugMsg(`rmdir -> ${p}`);
let closeListener = utils.makeCloseListener(this, reject, 'rmdir');
this.client.prependListener('close', closeListener);
let errorListener = utils.makeErrorListener(reject, this, 'rmdir');
this.client.prependListener('error', errorListener);
utils.addTempListeners(this, 'rmdir', reject);
this.sftp.rmdir(p, (err) => {

@@ -965,4 +838,3 @@ if (err) {

resolve('Successfully removed directory');
this.removeListener('error', errorListener);
this.removeListener('close', closeListener);
utils.removeTempListeners(this.client);
});

@@ -974,16 +846,7 @@ });

utils.haveConnection(this, 'rmdir');
let pathInfo = await utils.checkRemotePath(
this,
remotePath,
targetType.writeDir
);
this.debugMsg('rmdir remoe path info ', pathInfo);
if (!pathInfo.valid) {
let e = utils.formatError(pathInfo.msg, 'rmdir', pathInfo.code);
throw e;
}
let absPath = await utils.normalizeRemotePath(this, remotePath);
if (!recursive) {
return _rmdir(pathInfo.path);
return _rmdir(absPath);
}
let list = await this.list(pathInfo.path);
let list = await this.list(absPath);
if (list.length) {

@@ -995,11 +858,11 @@ let files = list.filter((item) => item.type !== 'd');

for (let f of files) {
await this.delete(pathInfo.path + this.remotePathSep + f.name);
await this.delete(`${absPath}${this.remotePathSep}${f.name}`);
}
for (let d of dirs) {
await this.rmdir(pathInfo.path + this.remotePathSep + d.name, true);
await this.rmdir(`${absPath}${this.remotePathSep}${d.name}`, true);
}
}
return _rmdir(pathInfo.path);
return _rmdir(absPath);
} catch (err) {
throw utils.formatError(err, 'rmdir');
return utils.handleError(err, 'rmdir');
}

@@ -1014,43 +877,33 @@ }

* @param {string} path - path to the file to delete
* @param {boolean} notFoundOK - if true, ignore errors for missing target.
* Default is false.
* @return {Promise} with string 'Successfully deleeted file' once resolved
*
*/
async delete(remotePath) {
const _delete = (p) => {
return new Promise((resolve, reject) => {
this.debugMsg(`delete -> ${p}`);
let closeListener = utils.makeCloseListener(this, reject, 'delete');
this.client.prependListener('close', closeListener);
let errorListener = utils.makeErrorListener(reject, this, 'delete');
this.client.prependListener('error', errorListener);
this.sftp.unlink(p, (err) => {
delete(remotePath, notFoundOK = false) {
return new Promise((resolve, reject) => {
if (utils.haveConnection(this, 'delete', reject)) {
this.debugMsg(`delete -> ${remotePath}`);
utils.addTempListeners(this, 'delete', reject);
this.sftp.unlink(remotePath, (err) => {
if (err) {
this.debugMsg(`delete error ${err.message} code: ${err.code}`);
reject(
utils.formatError(`${err.message} ${p}`, '_delete', err.code)
);
if (notFoundOK && err.code === 2) {
this.debugMsg('delete ignore missing target error');
resolve(`Successfully deleted ${remotePath}`);
} else {
reject(
utils.formatError(
`${err.message} ${remotePath}`,
'delete',
err.code
)
);
}
}
resolve('Successfully deleted file');
this.removeListener('error', errorListener);
this.removeListener('close', closeListener);
resolve(`Successfully deleted ${remotePath}`);
utils.removeTempListeners(this.client);
});
});
};
try {
utils.haveConnection(this, 'delete');
let pathInfo = await utils.checkRemotePath(
this,
remotePath,
targetType.writeFile
);
this.debugMsg('delete remote path info ', pathInfo);
if (!pathInfo.valid) {
let e = utils.formatError(pathInfo.msg, 'delete', pathInfo.code);
throw e;
}
return _delete(pathInfo.path);
} catch (err) {
throw utils.formatError(err, 'delete');
}
});
}

@@ -1069,11 +922,8 @@

*/
async rename(fromPath, toPath) {
const _rename = (from, to) => {
return new Promise((resolve, reject) => {
this.debugMsg(`rename -> ${from} ${to}`);
let closeListener = utils.makeCloseListener(this, reject, 'rename');
this.client.prependListener('close', closeListener);
let errorListener = utils.makeErrorListener(reject, this, 'rename');
this.client.prependListener('error', errorListener);
this.sftp.rename(from, to, (err) => {
rename(fromPath, toPath) {
return new Promise((resolve, reject) => {
if (utils.haveConnection(this, 'rename', reject)) {
this.debugMsg(`rename -> ${fromPath} ${toPath}`);
utils.addTempListeners(this, 'rename', reject);
this.sftp.rename(fromPath, toPath, (err) => {
if (err) {

@@ -1083,52 +933,13 @@ this.debugMsg(`rename error ${err.message} code: ${err.code}`);

utils.formatError(
`${err.message} From: ${from} To: ${to}`,
'_rename'
`${err.message} From: ${fromPath} To: ${toPath}`,
'rename',
err.code
)
);
}
resolve(`Successfully renamed ${from} to ${to}`);
this.removeListener('error', errorListener);
this.removeListener('close', closeListener);
resolve(`Successfully renamed ${fromPath} to ${toPath}`);
utils.removeTempListeners(this.client);
});
});
};
try {
utils.haveConnection(this, 'rename');
let fromInfo = await utils.checkRemotePath(
this,
fromPath,
targetType.readObj
);
this.debugMsg('rename from path info ', fromInfo);
if (!fromInfo.valid) {
let e = utils.formatError(fromInfo.msg, 'rename', fromInfo.code);
throw e;
}
let toInfo = await utils.checkRemotePath(
this,
toPath,
targetType.writeObj
);
this.debugMsg('rename to path info ', toInfo);
if (toInfo.type) {
let e = utils.formatError(
`Permission denied: ${toInfo.path} already exists`,
'rename',
errorCode.permission
);
throw e;
}
if (!toInfo.valid) {
let e = utils.formatError(
toInfo.parentMsg,
'rename',
toInfo.parentCode
);
throw e;
}
return _rename(fromInfo.path, toInfo.path);
} catch (err) {
throw utils.formatError(err, 'rename');
}
});
}

@@ -1143,3 +954,3 @@

* @param {string} fromPath - path to the file to be renamed.
* @param {string} toPath - path to the new name.
* @param {string} toPath - path the new name.
*

@@ -1149,19 +960,8 @@ * @return {Promise}

*/
async posixRename(fromPath, toPath) {
const _posixRename = (from, to) => {
return new Promise((resolve, reject) => {
this.debugMsg(`posixRename -> ${from} ${to}`);
let closeListener = utils.makeCloseListener(
this,
reject,
'posixRename'
);
this.client.prependListener('close', closeListener);
let errorListener = utils.makeErrorListener(
reject,
this,
'posixRename'
);
this.client.prependListener('error', errorListener);
this.sftp.ext_openssh_rename(from, to, (err) => {
posixRename(fromPath, toPath) {
return new Promise((resolve, reject) => {
if (utils.haveConnection(this, 'posixRename', reject)) {
this.debugMsg(`posixRename -> ${fromPath} ${toPath}`);
utils.addTempListeners(this, 'posixRename', reject);
this.sftp.ext_openssh_rename(fromPath, toPath, (err) => {
if (err) {

@@ -1171,44 +971,13 @@ this.debugMsg(`posixRename error ${err.message} code: ${err.code}`);

utils.formatError(
`${err.message} From: ${from} To: ${to}`,
'_posixRename'
`${err.message} From: ${fromPath} To: ${toPath}`,
'posixRename',
err.code
)
);
}
resolve(`Successful POSIX rename ${from} to ${to}`);
this.removeListener('error', errorListener);
this.removeListener('close', closeListener);
resolve(`Successful POSIX rename ${fromPath} to ${toPath}`);
utils.removeTempListeners(this.client);
});
});
};
try {
utils.haveConnection(this, 'posixRename');
let fromInfo = await utils.checkRemotePath(
this,
fromPath,
targetType.readObj
);
this.debugMsg('posixRename from path info ', fromInfo);
if (!fromInfo.valid) {
let e = utils.formatError(fromInfo.msg, 'posixRename', fromInfo.code);
throw e;
}
let toInfo = await utils.checkRemotePath(
this,
toPath,
targetType.writeObj
);
this.debugMsg('posixRename to path info ', toInfo);
if (!toInfo.valid) {
let e = utils.formatError(
toInfo.parentMsg,
'posixRename',
toInfo.parentCode
);
throw e;
}
return _posixRename(fromInfo.path, toInfo.path);
} catch (err) {
throw utils.formatError(err, 'posixRename');
}
});
}

@@ -1226,37 +995,16 @@

*/
async chmod(remotePath, mode) {
const _chmod = (p, m) => {
return new Promise((resolve, reject) => {
this.debugMsg(`chmod -> ${p} ${m}`);
let closeListener = utils.makeCloseListener(this, reject, 'chmod');
this.client.prependListener('close', closeListener);
let errorListener = utils.makeErrorListener(reject, this, 'chmod');
this.client.prependListener('error', errorListener);
this.sftp.chmod(p, m, (err) => {
if (err) {
reject(utils.formatError(`${err.message} ${p}`, '_chmod'));
}
resolve('Successfully change file mode');
this.removeListener('error', errorListener);
this.removeListener('close', closeListener);
});
chmod(remotePath, mode) {
return new Promise((resolve, reject) => {
this.debugMsg(`chmod -> ${remotePath} ${mode}`);
utils.addTempListeners(this, 'chmod', reject);
this.sftp.chmod(remotePath, mode, (err) => {
if (err) {
reject(
utils.formatError(`${err.message} ${remotePath}`, 'chmod', err.code)
);
}
resolve('Successfully change file mode');
utils.removeTempListeners(this.client);
});
};
try {
utils.haveConnection(this, 'chmod');
let pathInfo = await utils.checkRemotePath(
this,
remotePath,
targetType.readObj
);
this.debugMsg('chmod path info ', pathInfo);
if (!pathInfo.valid) {
let e = utils.formatError(pathInfo.msg, 'chmod', pathInfo.code);
throw e;
}
return _chmod(pathInfo.path, mode);
} catch (err) {
throw utils.formatError(err, 'chmod');
}
});
}

@@ -1272,47 +1020,44 @@

* @param {String} dstDir - remote destination directory
* @param {regex} filter - (Optional) a regular expression used to select
* files and directories to upload
* @returns {String}
* @throws {Error}
*/
async uploadDir(srcDir, dstDir) {
async uploadDir(srcDir, dstDir, filter = /.*/) {
try {
this.debugMsg(`uploadDir -> ${srcDir} ${dstDir}`);
utils.haveConnection(this, 'uploadDir');
let localInfo = await utils.checkLocalPath(srcDir, targetType.readDir);
if (!localInfo.valid) {
let e = utils.formatError(localInfo.msg, 'uploadDir', localInfo.code);
throw e;
let dstStatus = await this.exists(dstDir);
if (dstStatus && dstStatus !== 'd') {
throw utils.formatError(
`Bad path ${dstDir}`,
'uploadDir',
errorCode.badPath
);
}
this.debugMsg('uploadDir local path info ', localInfo);
let remoteInfo = await utils.checkRemotePath(
this,
dstDir,
targetType.writeDir
);
this.debugMsg('uploadDir remote path info ', remoteInfo);
if (!remoteInfo.valid) {
let e = utils.formatError(remoteInfo.msg, 'uploadDir', remoteInfo.code);
throw e;
if (!dstStatus) {
await this.mkdir(dstDir, true);
}
if (!remoteInfo.type) {
await this.mkdir(remoteInfo.path, true);
}
let dirEntries = fs.readdirSync(localInfo.path, {
let dirEntries = fs.readdirSync(srcDir, {
encoding: 'utf8',
withFileTypes: true
});
dirEntries = dirEntries.filter((item) => filter.test(item.name));
for (let e of dirEntries) {
if (e.isDirectory()) {
let newSrc = join(localInfo.path, e.name);
let newDst = remoteInfo.path + this.remotePathSep + e.name;
await this.uploadDir(newSrc, newDst);
let newSrc = join(srcDir, e.name);
let newDst = dstDir + this.remotePathSep + e.name;
await this.uploadDir(newSrc, newDst, filter);
} else if (e.isFile()) {
let src = join(localInfo.path, e.name);
let dst = remoteInfo.path + this.remotePathSep + e.name;
let src = join(srcDir, e.name);
let dst = dstDir + this.remotePathSep + e.name;
await this.fastPut(src, dst);
this.client.emit('upload', {source: src, destination: dst});
} else {
console.log(`uploadDir: File ignored: ${e.name} not a regular file`);
this.debugMsg(
`uploadDir: File ignored: ${e.name} not a regular file`
);
}
}
return `${localInfo.path} uploaded to ${remoteInfo.path}`;
return `${srcDir} uploaded to ${dstDir}`;
} catch (err) {

@@ -1323,45 +1068,48 @@ return utils.handleError(err, 'uploadDir');

async downloadDir(srcDir, dstDir) {
/**
* @async
*
* Download the specified source directory to the specified destination
* directory. All regular files and sub-directories are downloaded to the local
* file system.
* @param {String} srcDir - remote source directory
* @param {String} dstDir - local destination directory
* @param {regex} filter - (Optional) a regular expression used to select
* files and directories to upload
* @returns {String}
* @throws {Error}
*/
async downloadDir(srcDir, dstDir, filter = /.*/) {
try {
this.debugMsg(`downloadDir -> ${srcDir} ${dstDir}`);
utils.haveConnection(this, 'downloadDir');
let remoteInfo = await utils.checkRemotePath(
this,
srcDir,
targetType.readDir
);
this.debugMsg('downloadDir remote path info ', remoteInfo);
if (!remoteInfo.valid) {
let e = utils.formatError(
remoteInfo.msg,
let fileList = await this.list(srcDir, filter);
let dstStatus = await utils.localExists(dstDir);
if (dstStatus && dstStatus !== 'd') {
throw utils.formatError(
`Bad path ${dstDir}`,
'downloadDir',
remoteInfo.code
errorCode.badPath
);
throw e;
}
let localInfo = await utils.checkLocalPath(dstDir, targetType.writeDir);
this.debugMsg('downloadDir lcoal path info ', localInfo);
if (localInfo.valid && !localInfo.type) {
fs.mkdirSync(localInfo.path, {recursive: true});
if (!dstStatus) {
fs.mkdirSync(dstDir, {recursive: true});
}
if (!localInfo.valid) {
let e = utils.formatError(localInfo.msg, 'downloadDir', localInfo.code);
throw e;
}
let fileList = await this.list(remoteInfo.path);
for (let f of fileList) {
if (f.type === 'd') {
let newSrc = remoteInfo.path + this.remotePathSep + f.name;
let newDst = join(localInfo.path, f.name);
await this.downloadDir(newSrc, newDst);
let newSrc = srcDir + this.remotePathSep + f.name;
let newDst = join(dstDir, f.name);
await this.downloadDir(newSrc, newDst, filter);
} else if (f.type === '-') {
let src = remoteInfo.path + this.remotePathSep + f.name;
let dst = join(localInfo.path, f.name);
let src = srcDir + this.remotePathSep + f.name;
let dst = join(dstDir, f.name);
await this.fastGet(src, dst);
this.client.emit('download', {source: src, destination: dst});
} else {
console.log(`downloadDir: File ignored: ${f.name} not regular file`);
this.debugMsg(
`downloadDir: File ignored: ${f.name} not regular file`
);
}
}
return `${remoteInfo.path} downloaded to ${localInfo.path}`;
return `${srcDir} downloaded to ${dstDir}`;
} catch (err) {

@@ -1368,0 +1116,0 @@ return utils.handleError(err, 'downloadDir');

'use strict';
const {prependListener} = require('cluster');
const fs = require('fs');
const path = require('path');
const {errorCode, targetType} = require('./constants');
const {errorCode} = require('./constants');

@@ -16,8 +16,3 @@ /**

*/
function formatError(
err,
name = 'sftp',
eCode = errorCode.generic,
retryCount
) {
function formatError(err, name = 'sftp', eCode, retryCount) {
let msg = '';

@@ -34,3 +29,3 @@ let code = '';

msg = `${name}: ${err}${retry}`;
code = eCode;
code = eCode ? eCode : errorCode.generic;
} else if (err.custom) {

@@ -62,3 +57,3 @@ msg = `${name}->${err.message}${retry}`;

}
code = err.code ? err.code : eCode;
code = err.code ? err.code : errorCode.generic;
}

@@ -92,3 +87,3 @@ let newError = new Error(msg);

} else {
throw formatError(err, name);
throw formatError(err, name, err.code);
}

@@ -110,2 +105,4 @@ }

let tempListeners = [];
/**

@@ -118,32 +115,63 @@ * Simple default error listener. Will reformat the error message and

*/
function makeErrorListener(reject, self, name) {
return function (err) {
self.errorHandled = true;
reject(formatError(err, name));
function errorListener(client, name, reject) {
let fn = function (err) {
if (!client.errorHandled) {
client.errorHandled = true;
if (reject) {
reject(formatError(err, name, err.code));
} else {
throw formatError(err, name, err.code);
}
}
client.debugMsg(`Handled Error: ${err.message} ${err.code}`);
};
tempListeners.push(['error', fn]);
return fn;
}
function makeEndListener(client) {
return function () {
function endListener(client, name, reject) {
let fn = function () {
if (!client.endCalled) {
console.error(
`${client.clientName} End Listener: Connection ended unexpectedly`
);
if (reject) {
reject(formatError('Unexpected end event raised', name));
} else {
throw formatError('Unexpected end event raised', name);
}
}
client.debugMsg(`Handled end event for ${name}`);
client.sftp = undefined;
};
tempListeners.push(['end', fn]);
return fn;
}
function makeCloseListener(client, reject, name) {
return function () {
function closeListener(client, name, reject) {
let fn = function () {
if (!client.endCalled) {
if (reject) {
reject(formatError('Connection closed unexpectedly', name));
reject(formatError('Unexpected close event raised', name));
} else {
console.error(`${client.clientName}: Connection closed unexpectedly`);
throw formatError('Unexpected close event raised', name);
}
}
client.debugMsg(`handled close event for ${name}`);
client.sftp = undefined;
};
tempListeners.push(['close', fn]);
return fn;
}
function addTempListeners(obj, name, reject) {
obj.client.prependListener('end', endListener(obj, name, reject));
obj.client.prependListener('close', closeListener(obj, name, reject));
obj.client.prependListener('error', errorListener(obj, name, reject));
}
function removeTempListeners(client) {
tempListeners.forEach(([e, fn]) => {
client.removeListener(e, fn);
});
tempListeners = [];
}
/**

@@ -183,224 +211,2 @@ * @async

/**
* Used by checkRemotePath and checkLocalPath to help ensure consistent
* error messages.
*
* @param {Error} err - original error
* @param {String} testPath - path associated with the error
* @returns {Object} with properties of 'msg' and 'code'.
*/
function classifyError(err, testPath) {
switch (err.code) {
case 'EACCES':
return {
msg: `Permission denied: ${testPath}`,
code: errorCode.permission
};
case 'ENOENT':
return {
msg: `No such file: ${testPath}`,
code: errorCode.notexist
};
case 'ENOTDIR':
return {
msg: `Not a directory: ${testPath}`,
code: errorCode.notdir
};
default:
return {
msg: err.message,
code: err.code ? err.code : errorCode.generic
};
}
}
function localAccess(localPath, mode) {
return new Promise((resolve) => {
fs.access(localPath, mode, (err) => {
if (err) {
let {msg, code} = classifyError(err, localPath);
resolve({
path: localPath,
valid: false,
msg: msg,
code: code
});
} else {
resolve({
path: localPath,
valid: true
});
}
});
});
}
async function checkLocalReadFile(localPath, localType) {
try {
let rslt = {
path: localPath,
type: localType
};
if (localType === 'd') {
rslt.valid = false;
rslt.msg = `Bad path: ${localPath} must be a file`;
rslt.code = errorCode.badPath;
return rslt;
} else {
let access = await localAccess(localPath, fs.constants.R_OK);
if (access.valid) {
rslt.valid = true;
return rslt;
} else {
rslt.valid = false;
rslt.msg = access.msg;
rslt.code = access.code;
return rslt;
}
}
} catch (err) {
throw formatError(err, 'checkLocalReadFile');
}
}
async function checkLocalReadDir(localPath, localType) {
try {
let rslt = {
path: localPath,
type: localType
};
if (!localType) {
rslt.valid = false;
rslt.msg = `No such directory: ${localPath}`;
rslt.code = errorCode.notdir;
return rslt;
} else if (localType !== 'd') {
rslt.valid = false;
rslt.msg = `Bad path: ${localPath} must be a directory`;
rslt.code = errorCode.badPath;
return rslt;
} else {
let access = await localAccess(
localPath,
fs.constants.R_OK | fs.constants.X_OK
);
if (!access.valid) {
rslt.valid = false;
rslt.msg = access.msg;
rslt.code = access.code;
return rslt;
}
rslt.valid = true;
return rslt;
}
} catch (err) {
throw formatError(err, 'checkLocalReadDir');
}
}
async function checkLocalWriteFile(localPath, localType) {
try {
let rslt = {
path: localPath,
type: localType
};
if (localType === 'd') {
rslt.valid = false;
rslt.msg = `Bad path: ${localPath} must be a file`;
rslt.code = errorCode.badPath;
return rslt;
} else if (!localType) {
let dir = path.parse(localPath).dir;
let parent = await localAccess(dir, fs.constants.W_OK);
if (parent.valid) {
rslt.valid = true;
return rslt;
} else {
rslt.valid = false;
rslt.msg = parent.msg;
rslt.code = parent.code;
return rslt;
}
} else {
let access = await localAccess(localPath, fs.constants.W_OK);
if (access.valid) {
rslt.valid = true;
return rslt;
} else {
rslt.valid = false;
rslt.msg = access.msg;
rslt.code = access.code;
return rslt;
}
}
} catch (err) {
throw formatError(err, 'checkLocalWriteFile');
}
}
async function checkLocalWriteDir(localPath, localType) {
try {
let rslt = {
path: localPath,
type: localType
};
if (!localType) {
let parent = path.parse(localPath).dir;
let access = await localAccess(parent, fs.constants.W_OK);
if (access.valid) {
rslt.valid = true;
return rslt;
} else {
rslt.valid = false;
rslt.msg = access.msg;
rslt.code = access.code;
return rslt;
}
} else if (localType !== 'd') {
rslt.valid = false;
rslt.msg = `Bad path: ${localPath} must be a directory`;
rslt.code = errorCode.badPath;
return rslt;
} else {
let access = await localAccess(localPath, fs.constants.W_OK);
if (access.valid) {
rslt.valid = true;
return rslt;
} else {
rslt.valid = false;
rslt.msg = access.msg;
rslt.code = access.code;
return rslt;
}
}
} catch (err) {
throw formatError(err, 'checkLocalWriteDir');
}
}
async function checkLocalPath(lPath, target = targetType.readFile) {
try {
let localPath = path.resolve(lPath);
let type = await localExists(localPath);
switch (target) {
case targetType.readFile:
return checkLocalReadFile(localPath, type);
case targetType.readDir:
return checkLocalReadDir(localPath, type);
case targetType.writeFile:
return checkLocalWriteFile(localPath, type);
case targetType.writeDir:
return checkLocalWriteDir(localPath, type);
default:
return {
path: localPath,
type: type,
valid: true
};
}
} catch (err) {
throw formatError(err, 'checkLocalPath');
}
}
async function normalizeRemotePath(client, aPath) {

@@ -421,203 +227,2 @@ try {

function checkReadObject(aPath, type) {
return {
path: aPath,
type: type,
valid: type ? true : false,
msg: type ? undefined : `No such file ${aPath}`,
code: type ? undefined : errorCode.notexist
};
}
function checkReadFile(aPath, type) {
if (!type) {
return {
path: aPath,
type: type,
valid: false,
msg: `No such file: ${aPath}`,
code: errorCode.notexist
};
} else if (type === 'd') {
return {
path: aPath,
type: type,
valid: false,
msg: `Bad path: ${aPath} must be a file`,
code: errorCode.badPath
};
}
return {
path: aPath,
type: type,
valid: true
};
}
function checkReadDir(aPath, type) {
if (!type) {
return {
path: aPath,
type: type,
valid: false,
msg: `No such directory: ${aPath}`,
code: errorCode.notdir
};
} else if (type !== 'd') {
return {
path: aPath,
type: type,
valid: false,
msg: `Bad path: ${aPath} must be a directory`,
code: errorCode.badPath
};
}
return {
path: aPath,
type: type,
valid: true
};
}
async function checkWriteFile(client, aPath, type) {
if (type && type === 'd') {
return {
path: aPath,
type: type,
valid: false,
msg: `Bad path: ${aPath} must be a regular file`,
code: errorCode.badPath
};
} else if (!type) {
let {root, dir} = path.parse(aPath);
//let parentDir = path.parse(aPath).dir;
if (!dir) {
return {
path: aPath,
type: false,
valid: false,
msg: `Bad path: ${aPath} cannot determine parent directory`,
code: errorCode.badPath
};
}
if (root === dir) {
return {
path: aPath,
type: type,
valid: true
};
}
let parentType = await client.exists(dir);
if (!parentType) {
return {
path: aPath,
type: type,
valid: false,
msg: `Bad path: ${dir} parent not exist`,
code: errorCode.badPath
};
} else if (parentType !== 'd') {
return {
path: aPath,
type: type,
valid: false,
msg: `Bad path: ${dir} must be a directory`,
code: errorCode.badPath
};
}
return {
path: aPath,
type: type,
valid: true
};
}
return {
path: aPath,
type: type,
valid: true
};
}
async function checkWriteDir(client, aPath, type) {
if (type && type !== 'd') {
return {
path: aPath,
type: type,
valid: false,
msg: `Bad path: ${aPath} must be a directory`,
code: errorCode.badPath
};
} else if (!type) {
let {root, dir} = path.parse(aPath);
if (root === dir) {
return {
path: aPath,
type: type,
valid: true
};
}
if (!dir) {
return {
path: aPath,
type: false,
valid: false,
msg: `Bad path: ${aPath} cannot determine directory parent`,
code: errorCode.badPath
};
}
let parentType = await client.exists(dir);
if (parentType && parentType !== 'd') {
return {
path: aPath,
type: type,
valid: false,
msg: `Bad path: ${parentDir} must be a directory`,
code: errorCode.badPath
};
}
}
// don't care if parent does not exist as it might be created
// via recursive call to mkdir.
return {
path: aPath,
type: type,
valid: true
};
}
function checkWriteObject(aPath, type) {
// for writeObj, not much error checking we can do
// Just return path, type and valid indicator
return {
path: aPath,
type: type,
valid: true
};
}
async function checkRemotePath(client, rPath, target = targetType.readFile) {
let aPath = await normalizeRemotePath(client, rPath);
let type = await client.exists(aPath);
switch (target) {
case targetType.readObj:
return checkReadObject(aPath, type);
case targetType.readFile:
return checkReadFile(aPath, type);
case targetType.readDir:
return checkReadDir(aPath, type);
case targetType.writeFile:
return checkWriteFile(client, aPath, type);
case targetType.writeDir:
return checkWriteDir(client, aPath, type);
case targetType.writeObj:
return checkWriteObject(aPath, type);
default:
throw formatError(
`Unknown target type: ${target}`,
'checkRemotePath',
errorCode.generic
);
}
}
/**

@@ -675,9 +280,9 @@ * Check to see if there is an active sftp connection

removeListeners,
makeErrorListener,
makeEndListener,
makeCloseListener,
errorListener,
endListener,
closeListener,
addTempListeners,
removeTempListeners,
localExists,
checkLocalPath,
normalizeRemotePath,
checkRemotePath,
haveConnection,

@@ -684,0 +289,0 @@ dumpListeners,

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc