diff --git a/.npmignore b/.npmignore index 159d218..a075e6d 100644 --- a/.npmignore +++ b/.npmignore @@ -1,14 +1,15 @@ *.tgz .DS_Store +README.org node_modules tmp -/test/ -example/ +test +example .env -.prettier +.prettierrc env-turing -.eslintrc +.eslintrc.js .mocharc.js .indium.json -README.org + diff --git a/README.org b/README.org new file mode 100644 index 0000000..229af29 --- /dev/null +++ b/README.org @@ -0,0 +1,992 @@ +* SSH2 SFTP Client + +an SFTP client for node.js, a wrapper around [[https://github.com/mscdex/ssh2][SSH2]] which provides a high level +convenience abstraction as well as a Promise based API. + +Documentation on the methods and available options in the underlying modules can +be found on the [[https://github.com/mscdex/ssh2][SSH2]] and [[https://github.com/mscdex/ssh2-streams/blob/master/SFTPStream.md][SSH2-STREAMS]] project pages. + +Current stable release is *v4.0.2*. Previous release was *v2.5.2*. + +Code has been tested against Node versions 8.16.1, 10.16.3 and 12.9.1 + +Node versions < 8.x are not supported. + +* Installation + +#+begin_src shell +npm install ssh2-sftp-client +#+end_src + +* Basic Usage + +#+begin_src javascript + let Client = require('ssh2-sftp-client'); + let sftp = new Client(); + + sftp.connect({ + host: '127.0.0.1', + port: '8080', + username: 'username', + password: '******' + }).then(() => { + return sftp.list('/pathname'); + }).then(data => { + console.log(data, 'the data info'); + }).catch(err => { + console.log(err, 'catch error'); + }); + +#+end_src + +* Breaking Changes in Version 4.0.0 + +There has been minor changes to the API signatures + +- The ~connect()~ method no longer accepts a 'connectMethod' argument. It was + not clear what this argument was for or what it did. + +- Additional options are now available in the configure object passed to the + ~connect()~ method to control the connection retry functionality. + +- Node versions before 8.x are no longer supported. + +- Error message formats have changed. While they are now more consistent, if you + have code which parses the messages, it will need to be updated. + +- The ~auxList()~ method is deprecated. An additional optional ~pattern~ + argument has been added to the ~list()~ method to facilitate filtering of + results returned by ~list()~. Both 'glob' and regexp pattern styles are + supported. + +- The properties returned by the ~stat()~ method have changed. The ~permissions~ + property has been removed as it contained the same information as the ~mode~ + property. New properties ~isDirectory~, ~isFile~, ~isBlockDevice~, + ~isCharacterDevice~, ~isSymbolicLink~, ~isFIFO~ and ~isSocket~ have been added. + +* Documentation + +The connection options are the same as those offered by the underlying SSH2 +module. For full details, please see [[https://github.com/mscdex/ssh2#user-content-client-methods][SSH2 client methods]] + +All the methods will return a Promise, except for ~on()~ and +~removeListener()~, which are typically only used in special use cases. + +** Methods +*** connect(config) ===> SFTPstream + +Connect to an sftp server. Full documentation for connection options is +available [[https://github.com/mscdex/ssh2#user-content-client-methods][here]] + +**** Connection Options + +This module is based on the excellent [[https://github.com/mscdex/ssh2#client][SSH2]] module. That module is a general SSH2 +client and server library and provides much more functionality than just SFTP +connectivity. Many of the connect options provided by that module are less +relevant for SFTP connections. It is recommended you keep the config options to +the minimum needed and stick to the options listed in the ~commonOpts~ below. + +The ~retries~, ~retry_factor~ and ~retry_minTimeout~ options are not part of the +SSH2 module. These are part of the configuration for the [[https://www.npmjs.com/package/retry][retry]] package and what +is used to enable retrying of sftp connection attempts. See the documentation +for that package for an explanation of these values. + +#+begin_src js2 + // common options + + let commonOpts { + host: 'localhost', // string Hostname or IP of server. + port: 22, // Port number of the server. + forceIPv4: false, // boolean (optional) Only connect via IPv4 address + forceIPv6: false, // boolean (optional) Only connect via IPv6 address + username: 'donald', // string Username for authentication. + password: 'borsch', // string Password for password-based user authentication + agent: process.env.SSH_AGENT, // string - Path to ssh-agent's UNIX socket + privateKey: fs.readFileSync('/path/to/key'), // Buffer or string that contains + passphrase; 'a pass phrase', // string - For an encrypted private key + readyTimeout: 20000, // integer How long (in ms) to wait for the SSH handshake + strictVendor: true // boolean - Performs a strict server vendor check + debug: myDebug // function - Set this to a function that receives a single + // string argument to get detailed (local) debug information. + retries: 2 // integer. Number of times to retry connecting + retry_factor: 2 // integer. Time factor used to calculate time between retries + retry_minTimeout: 2000 // integer. Minimum timeout between attempts + }; + + // rarely used options + + let advancedOpts { + localAddress, + localPort, + hostHash, + hostVerifier, + agentForward, + localHostname, + localUsername, + tryKeyboard, + authHandler, + keepaliveInterval, + keepaliveCountMax, + sock, + algorithms, + compress + }; + +#+end_src + +**** Example Use + +#+begin_src javascript + sftp.connect({ + host: example.com, + port: 22, + username: 'donald', + password: 'youarefired' + }); + +#+end_src + +*** list(path, pattern) ==> Array[object] + +Retrieves a directory listing. This method returns a Promise, which once +realised, returns an array of objects representing items in the remote +directory. + +- path :: {String} Remote directory path +- pattern :: (optional) {string|RegExp} A pattern used to filter the items included in the returned + array. Pattern can be a simple /glob/ style string or a regular + experession. Defaults to ~/.*/~. + +**** Example Use + +#+begin_src js2 + const Client = require('ssh2-sftp-client'); + + const config = { + host: 'exmaple.com', + port: 22, + username: 'red-don', + password: 'my-secret' + }; + + let sftp = new Client; + + sftp.connect(config) + .then(() => { + return sftp.list('/path/to/remote/dir'); + }) + .then(data => { + console.log(data); + }) + .then(() => { + sftp.end(); + }) + .catch(err => { + console.error(err.message); + }); + +#+end_src + +**** Return Objects + +The objects in the array returned by ~list()~ have the following properties; + +#+begin_src javascript + { + type: // file type(-, d, l) + name: // file name + size: // file size + modifyTime: // file timestamp of modified time + accessTime: // file timestamp of access time + rights: { + user: + group: + other: + }, + owner: // user ID + group: // group ID + } + +#+end_src + +**** Pattern Filter + +The filter options can be a regular expression (most powerful option) or a +simple /glob/ like string where * will match any number of characters e.g + +#+begin_example + foo* => foo, foobar, foobaz + ,*bar => bar, foobar, tabbar + ,*oo* => foo, foobar, look, book +#+end_example + +The /glob/ style matching is very simple. In most cases, you are best off using +a real regular expression which will allow you to do more powerful matching and +anchor matches to the beginning/end of the string etc. + +*** exists(path) ==> boolean + +Tests to see if remote file or directory exists. Returns type of remote object +if it exists or false if it does not. + +**** Example Use + +#+begin_src js2 + const Client = require('ssh2-sftp-client'); + + const config = { + host: 'exmaple.com', + port: 22, + username: 'red-don', + password: 'my-secret' + }; + + let sftp = new Client; + + sftp.connect(config) + .then(() => { + return sftp.exists('/path/to/remote/dir'); + }) + .then(data => { + console.log(data); // will be false or d, -, l (dir, file or link) + }) + .then(() => { + sftp.end(); + }) + .catch(err => { + console.error(err.message); + }); + +#+end_src + +*** stat(path) ==> object + +Returns the attributes associated with the object pointed to by ~path~. + +- path :: String. Remote path to directory or file on remote server + +**** Attributes + +The ~stat()~ method returns an object with the following properties; + +#+begin_src js2 + let stats = { + mode: 33279, // integer representing type and permissions + uid: 1000, // user ID + gid: 985, // group ID + size: 5, // file size + accessTime: 1566868566000, // Last access time. milliseconds + modifyTime: 1566868566000, // last modify time. milliseconds + isDirectory: false, // true if object is a directory + isFile: true, // true if object is a file + isBlockDevice: false, // true if object is a block device + isCharcterDevice: false, // true if object is a character device + isSymbolicLink: false, // true if object is a symbolic link + isFIFO: false, // true if object is a FIFO + isSocket: false // true if object is a socket + }; + +#+end_src +**** Example Use + +#+begin_src js2 + let client = new Client(); + + client.connect(config) + .then(() => { + return client.stat('/path/to/remote/file'); + }) + .then(data => { + // do something with data + }) + .then(() => { + client.end(); + }) + .catch(err => { + console.error(err.message); + }); + +#+end_src + +*** get(path, dst, options) ==> String + +Retrieve a file from a remote SFTP server. The ~dst~ argument defines the +destination and can be either a string, a buffer or a writeable stream. In +general, if your going to pass in a string as the destination, you are probably +better off using the ~fastGet()~ method. + +- path :: String. Path to the remote file to download +- dst :: String|Buffer|Writeable. Destination for the data. If a string, it + should be a local file path. +- options :: Options for the ~get()~ command (see below). + +**** Options + +The options object can be used to pass options to the underlying readStream used +to read the data from the remote server. + +#+begin_src js2 + { flags: 'r', + encoding: null, + handle: null, + mode: 0o666, + autoClose: true + } + +#+end_src + +Most of the time, you won't want to use any options. Sometimes, it may be useful +to set the encoding. For example, to 'utf-8'. However, it is important not to do +this for binary files to avoid data corruption. + +**** Example Use + +#+begin_src javascript + let client = new Client(); + + let remotePath = '/remote/server/path/file.txt'; + let dst = fs.createWriteStream('/local/file/path/copy.txt'); + + client.connect(config) + .then(() => { + return client.get(remotePath, dst); + }) + .then(() => { + client.end(); + }) + .catch(err => { + console.error(err.message); + }); + +#+end_src + +- Tip :: See examples file in the Git repository for more examples. You can pass + any writeable stream in as the destination. For example, if you pass in + ~zlib.createGunzip()~ writeable stream, you can both download and + decompress a gzip file 'on the fly'. + +*** fastGet(remotePath, localPath, options) ===> string + +Downloads a file at remotePath to localPath using parallel reads for faster +throughput. This is the simplest method if you just want to download a file. + +- remotePath :: String. Path to the remote file to download +- localPath :: String. Path on local file system for the downloaded file. The + local path should include the filename to use for saving the + file. +- options :: Options for ~fastGet()~ (see below) + +**** OPtions + +#+begin_src js2 + { + concurrency: 64, // integer. Number of concurrent reads to use + chunkSize: 32768, // integer. Size of each read in bytes + step: function(total_transferred, chunk, total) // callback called each time a + // chunk is transferred + } +#+end_src + +- Warning :: Some servers do not respond correctly to requests to alter chunk + size. This can result in lost or corrupted data. + +**** Sample Use + +#+begin_src javascript + let client = new Client(); + let remotePath = '/server/path/file.txt'; + let localPath = '/local/path/file.txt'; + + client.connect(config) + .then(() => { + client.fastGet(remotePath, localPath); + }) + .then(() => { + client.end(); + }) + .catch(err => { + console.error(err.message); + }); + +#+end_src + +*** put(src, remotePath, options) ==> string + +Upload data from local system to remote server. If the ~src~ argument is a +string, it is interpreted as a local file path to be used for the data to +transfer. If the ~src~ argument is a buffer, the contents of the buffer are +copied to the remote file and if it is a readable stream, the contents of that +stream are piped to the ~remotePath~ on the server. + +- src :: string | buffer | readable stream. Data source for data to copy to the + remote server. +- remotePath :: string. Path to the remote file to be created on the server. +- options :: object. Options which can be passed to adjust the write stream used + in sending the data to the remote server (see below). + +**** Options + +The following options are supported; + +#+begin_src js2 + { + flags: 'w', // w - write and a - append + encoding: null, // use null for binary files + mode: 0o666, // mode to use for created file (rwx) + autoClose: true // automatically close the write stream when finished + } + +#+end_src + +The most common options to use are mode and encoding. The values shown above are +the defaults. You do not have to set encoding to utf-8 for text files, null is +fine for all file types. However, using utf-8 encoding for binary files will +often result in data corruption. + +**** Example Use + +#+begin_src javascript + let client = new Client(); + + let data = fs.createReadStream('/path/to/local/file.txt'); + let remote = '/path/to/remote/file.txt'; + + client.connect(config) + .then(() => { + return client.put(data, remote); + }) + .then(() => { + return client.end(); + }) + .catch(err => { + console.error(err.message); + }); + +#+end_src + +- Tip :: If the src argument is a path string, consider just using ~fastPut()~. + +*** fastPut(localPath, remotePath, options) ==> string + +Uploads the data in file at ~localPath~ to a new file on remote server at +~remotePath~ using concurrency. The options object allows tweaking of the fast put process. + +- localPath :: string. Path to local file to upload +- remotePath :: string. Path to remote file to create +- options :: object. Options passed to createWriteStream (see below) + +**** Options + +#+begin_src js2 + { + concurrency: 64, // integer. Number of concurrent reads + chunkSize: 32768, // integer. Size of each read in bytes + mode: 0o755, // mixed. Integer or string representing the file mode to set + step: function(total_transferred, chunk, total) // function. Called every time + // a part of a file was transferred + } + +#+end_src + +- Warning :: There have been reports that some SFTP servers will not honour + requests for non-default chunk sizes. This can result in data loss + or corruption. + +**** Example Use + +#+begin_src js2 + let localFile = '/path/to/file.txt'; + let remoteFile = '/path/to/remote/file.txt'; + let client = new Client(); + + client.connect(config) + .then(() => { + client.fastPut(localFile, remoteFile); + }) + .then(() => { + client.end(); + }) + .catch(err => { + console.error(err.message); + }); + +#+end_src + +*** append(input, remotePath, options) ==> string + +Append the ~input~ data to an existing remote file. There is no integrity +checking performed apart from normal writeStream checks. This function simply +opens a writeStream on the remote file in append mode and writes the data passed +in to the file. + +- input :: string | readStream. Data to append to remote file +- remotePath :: string. Path to remote file +- options :: object. Options to pass to writeStream (see below) + +**** Options + +The following options are supported; + +#+begin_src js2 + { + flags: 'a', // w - write and a - append + encoding: null, // use null for binary files + mode: 0o666, // mode to use for created file (rwx) + autoClose: true // automatically close the write stream when finished + } + +#+end_src + +The most common options to use are mode and encoding. The values shown above are +the defaults. You do not have to set encoding to utf-8 for text files, null is +fine for all file types. Generally, I would not attempt to append binary files. + +**** Example Use + +#+begin_src js2 + let remotePath = '/path/to/remote/file.txt'; + let client = new Client(); + + client.connect(config) + .then(() => { + return client.append(Buffer.from('Hello world'), remotePath); + }) + .then(() => { + return client.end(); + }) + .catch(err => { + console.error(err.message); + }); + +#+end_src + +*** mkdir(path, recursive) ==> string + +Create a new directory. If the recursive flag is set to true, the method will +create any directories in the path which do not already exist. Recursive flag +defaults to false. + +- path :: string. Path to remote directory to create +- recursive :: boolean. If true, create any missing directories in the path as + well + +**** Example Use + +#+begin_src javascript + let remoteDir = '/path/to/new/dir'; + let client = new Client(); + + client.connect(config) + .then(() => { + return client.mkdir(remoteDir, true); + }) + .then(() => { + return client.end(); + }) + .catch(err => { + console.error(err.message); + }); + +#+end_src + +*** rmdir(path, recursive) ==> string + +Remove a directory. If removing a directory and recursive flag is set to +~true~, the specified directory and all sub-directories and files will be +deleted. If set to false and the directory has sub-directories or files, the +action will fail. + +- path :: string. Path to remote directory +- recursive :: boolean. If true, remove all files and directories in target + directory. Defaults to false + +**** Example Use + +#+begin_src js2 + let remoteDir = '/path/to/remote/dir'; + let client = new Client(); + + client.connect(config) + .then(() => { + return client.rmdir(remoteDir, true); + }) + .then(() => { + return client.end(); + }) + .catch(err => { + console.error(err.message); + }); + +#+end_src + +*** delete(path) ==> string + +Delete a file on the remote server. + +- path :: string. Path to remote file to be deleted. + +**** Example Use + +#+begin_src js2 + let remoteFile = '/path/to/remote/file.txt'; + let client = new Client(); + + client.connect(config) + .then(() => { + return client.delete(remoteFile); + }) + .then(() => { + return client.end(); + }) + .catch(err => { + console.error(err.message); + }); + +#+end_src + +*** rename(fromPath, toPath) ==> string + +Rename a file or directory from ~fromPath~ to ~toPath~. You must have the +necessary permissions to modify the remote file. + +**** Example Use + +#+begin_src js2 + let from = '/remote/path/to/old.txt'; + let to = '/remote/path/to/new.txt'; + let client = new Client(); + + client.connect(config) + .then(() => { + return client.rename(from, to); + }) + .then(() => { + return client.end(); + }) + .catch(err => { + console.error(err.message); + }); + +#+end_src + +*** chmod(path, mode) ==> string + +Change the mode (read, write or execute permissions) of a remote file or +directory. + +- path :: string. Path to the remote file or directory +- mode :: octal. New mode to set for the remote file or directory + +**** Example Use + +#+begin_src js2 + let path = '/path/to/remote/file.txt'; + let ndwMode = 0o644; // rw-r-r + let client = new Client(); + + client.connect(config) + .then(() => { + return client.chmod(path, newMode); + }) + .then(() => { + return client.end(); + }) + .catch(err => { + console.error(err.message); + }); + +#+end_src + +*** end() ==> boolean + +Ends the current client session, releasing the client socket and associated +resources. This function also removes all listeners associated with the client. + +**** Example Use + +#+begin_src js2 + let client = new Client(); + + client.connect(config) + .then(() => { + // do some sftp stuff + }) + .then(() => { + return client.end(); + }) + .catch(err => { + console.error(err.message); + }); + +#+end_src + +*** Add and Remove Listeners + +Although normally not required, you can add and remove custom listeners on the +ssh2 client object. This object supports a number of events, but only a few of +them have any meaning in the context of SFTP. These are + +- error :: An error occured. Calls listener with an error argument. +- end :: The socket has been disconnected. No argument. +- close :: The socket was closed. Boolean argument which is true when the socket + was closed due to errors. + +**** on(eventType, listener) + +Adds the specified listener to the specified event type. It the event type is +~error~, the listener should accept 1 argument, which will be an Error object. If +the event type is ~close~, the listener should accept one argument of a boolean +type, which will be true when the client connection was closed due to errors. + +**** removeListener(eventType, listener) + +Removes the specified listener from the event specified in eventType. Note that +the ~end()~ method automatically removes all listeners from the client object. + +* FAQ +** How can you pass writable stream as dst for get method? + +If the dst argument passed to the get method is a writeable stream, the remote +file will be piped into that writeable. If the writeable you pass in is a +writeable stream created with ~fs.createWriteStream()~, the data will be written +to the file specified in the constructor call to ~createWriteStream()~. + +The wrteable stream can be any type of write stream. For example, the below code +will convert all the characters in the remote file to upper case before it is +saved to the local file system. This could just as easily be something like a +gunzip stream from ~zlib~, enabling you to decompress remote zipped files as you +bring thenm across before saving to local file system. + +#+begin_src javascript + 'use strict'; + + // Example of using a writeable with get to retrieve a file. + // This code will read the remote file, convert all characters to upper case + // and then save it to a local file + + const Client = require('../src/index.js'); + const path = require('path'); + const fs = require('fs'); + const through = require('through2'); + + const config = { + host: 'arch-vbox', + port: 22, + username: 'tim', + password: 'xxxx' + }; + + const sftp = new Client(); + const remoteDir = '/home/tim/testServer'; + + function toupper() { + return through(function(buf, enc, next) { + next(null, buf.toString().toUpperCase()); + }); + } + + sftp + .connect(config) + .then(() => { + return sftp.list(remoteDir); + }) + .then(data => { + // list of files in testServer + console.dir(data); + let remoteFile = path.join(remoteDir, 'test.txt'); + let upperWtr = toupper(); + let fileWtr = fs.createWriteStream(path.join(__dirname, 'loud-text.txt')); + upperWtr.pipe(fileWtr); + return sftp.get(remoteFile, upperWtr); + }) + .then(() => { + return sftp.end(); + }) + .catch(err => { + console.error(err.message); + }); + +#+end_src + +** How can I upload files without having to specify a password? + +There are a couple of ways to do this. Essentially, you want to setup SSH keys +and use these for authentication to the remote server. + +One solution, provided by @KalleVuorjoki is to use the SSH agent +process. *Note*: SSH_AUTH_SOCK is normally created by your OS when you load the +ssh-agent as part of the login session. + +#+begin_src javascript + let sftp = new Client(); + sftp.connect({ + host: 'YOUR-HOST', + port: 'YOUR-PORT', + username: 'YOUR-USERNAME', + agent: process.env.SSH_AUTH_SOCK + }).then(() => { + sftp.fastPut(....) + } + +#+end_src + +Another alternative is to just pass in the SSH key directly as part of the +configuration. + +#+begin_src javascript + let sftp = new Client(); + sftp.connect({ + host: 'YOUR-HOST', + port: 'YOUR-PORT', + username: 'YOUR-USERNAME', + privateKey: fs.readFileSync('/path/to/ssh/ke') + }).then(() => { + sftp.fastPut(.....) + } + +#+end_src + +** How can I connect through a Socks Proxy + +This solution was provided by @jmorino. + +#+begin_src js2 + import { SocksClient } from 'socks'; + import SFTPClient from 'ssh2-sftp-client'; + + const host = 'my-sftp-server.net'; + const port = 22; // default SSH/SFTP port on remote server + + // connect to SOCKS 5 proxy + const { socket } = await SocksClient.createConnection({ + proxy: { + host: 'my.proxy', // proxy hostname + port: 1080, // proxy port + type: 5, // for SOCKS v5 + }, + command: 'connect', + destination: { host, port } // the remote SFTP server + }); + + const client = new SFTPClient(); + client.connect({ + host, + sock: socket, // pass the socket to proxy here (see ssh2 doc) + username: '.....', + privateKey: '.....' + }) + + + // client is connected +#+end_src + +* Change Log +** v4.0.2 (Current stable version) + - Fix some minor packaging issues + +** v4.0.0 + +- Remove support for node < 8.x +- Fix connection retry feature +- sftp connection object set to null when 'end' signal is raised +- Removed 'connectMethod' argument from connect method. +- Refined adding/removing of listeners in connect() and end() methods to enable + errors to be adequately caught and reported. +- Depricate auxList() and add pattern/regexp filter option to list() +- Refactored handling of event signals to provide better feedback to clients +- Removed pointless 'permissions' property from objects returned by ~stat()~ + (same as mode property). Added additional properties describing the type of + object. +- Added the ~removeListener()~ method to compliment the existing ~on()~ method. + +** v2.5.2 + - Repository transferred to theophilusx + - Fix error in package.json pointing to wrong repository + +** v2.5.1 + - Apply 4 pull requests to address minor issues prior to transfer + +** v2.5.0 + - ??? + +** v2.4.3 + - merge #108, #110 + - fix connect promise if connection ends + +** v2.4.2 + - merge #105 + - fix windows path + +** v2.4.1 + - merge pr #99, #100 + - bug fix + +** v2.4.0 +- Requires node.js v7.5.0 or above. +- merge pr #97, thanks for @theophilusx + - Remove emmitter.maxListener warnings + - Upgraded ssh2 dependency from 0.5.5 to 0.6.1 + - Enhanced error messages to provide more context and to be more consistent + - re-factored test + - Added new 'exists' method and re-factored mkdir/rmdir + +** v2.3.0 +- add: `stat` method +- add `fastGet` and `fastPut` method. +- fix: `mkdir` file exists decision logic + +** v3.0.0 -- deprecate this version +- change: `sftp.get` will return chunk not stream anymore +- fix: get readable not emitting data events in node 10.0.0 + +** v2.1.1 +- add: event listener. [doc](https://github.com/jyu213/ssh2-sftp-client#Event) +- add: `get` or `put` method add extra options [pr#52](https://github.com/jyu213/ssh2-sftp-client/pull/52) + +** v2.0.1 +- add: `chmod` method [pr#33](https://github.com/jyu213/ssh2-sftp-client/pull/33) +- update: upgrade ssh2 to V0.5.0 [pr#30](https://github.com/jyu213/ssh2-sftp-client/pull/30) +- fix: get method stream error reject unwork [#22](https://github.com/jyu213/ssh2-sftp-client/issues/22) +- fix: return Error object on promise rejection [pr#20](https://github.com/jyu213/ssh2-sftp-client/pull/20) + +** v1.1.0 +- fix: add encoding control support for binary stream + +** v1.0.5: +- fix: multi image upload +- change: remove `this.client.sftp` to `connect` function + +* Logging Issues + +Please log an issue for all bugs, questions, feature and enhancement +requests. Please ensure you include the module version, node version and +platform. + +* Pull Requests + +Pull requests are always welcomed. However, please ensure your changes pass all +tests and if your adding a new feature, that tests for that feature are +included. Likewise, for new features or enhancements, please include any +relevant documentation updates. + +This module will adopt a standard semantic versioning policy. Please indicate in +your pull request what level of change it represents i.e. + +- Major :: Change to API or major change in functionality which will require an + increase in major version number. +- Ninor :: Minor change, enhancement or new feature which does not change + existing API and will not break existing client code. +- Bug Fix :: No change to functionality or features. Simple fix of an existing + bug. + +* Contributors + +This module was initially written by jyu213. On August 23rd, 2019, theophilusx +took over responsibility for maintaining this module. A number of other people +have contributed to this module, but until now, this was not tracked. My +intention is to credit anyone who contributes going forward. + +- jyu213 :: Original author +- theophilusx :: Current maintainer + + diff --git a/package.json b/package.json index 241c072..794cbd6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ssh2-sftp-client", - "version": "4.0.1", + "version": "4.0.2", "description": "ssh2 sftp client for node", "main": "src/index.js", "repository": {